# Backend Horizontal Instructions
## Purpose
The Backend horizontal defines the server-side architecture, business logic implementation, data persistence, and system integration patterns for each vertical component. It ensures scalable, secure, and maintainable server-side systems across the entire application stack.
## Content Requirements
### Required Sections
1. **Architecture Patterns**
- Application architecture (layered, hexagonal, clean)
- Service organization and boundaries
- Dependency injection and inversion of control
- Design patterns implementation
2. **API Implementation**
- RESTful service implementation
- GraphQL resolvers and schema
- WebSocket and real-time communication
- API versioning and backward compatibility
3. **Data Layer**
- Database design and optimization
- ORM/ODM configuration and usage
- Repository and data access patterns
- Transaction management and consistency
4. **Business Logic**
- Domain models and entities
- Service layer implementation
- Validation and business rules
- Workflow and process orchestration
5. **Infrastructure Integration**
- External service integrations
- Message queue and event processing
- Caching strategies and implementation
- Monitoring and observability
### Documentation Format
```yaml
services:
UserService:
type: "business_service"
framework: "node.js"
dependencies: ["bcrypt", "jsonwebtoken", "nodemailer"]
responsibilities:
- "User registration and authentication"
- "Profile management"
- "Password reset functionality"
- "Email verification"
interfaces:
rest_api:
base_path: "/api/v1/users"
endpoints:
- method: "POST"
path: "/"
operation: "createUser"
authentication: "none"
- method: "GET"
path: "/{id}"
operation: "getUserById"
authentication: "bearer_token"
data_access:
repositories: ["UserRepository", "ProfileRepository"]
transactions: "required_for_user_creation"
caching: "user_profile_cache"
external_integrations:
- service: "EmailService"
purpose: "Send verification emails"
type: "asynchronous"
- service: "AuditService"
purpose: "Log user actions"
type: "fire_and_forget"
performance:
response_time: "< 200ms (95th percentile)"
throughput: "100 requests/second"
memory_usage: "< 128MB per instance"
security:
authentication: "JWT tokens"
authorization: "Role-based access control"
input_validation: "Joi schema validation"
output_sanitization: "XSS protection"
```
## Validation Rules
### Quality Control
1. **Code Quality Standards**
- SOLID principles adherence
- Clean code practices
- Consistent error handling patterns
- Comprehensive logging and monitoring
2. **Security Requirements**
- Input validation and sanitization
- Authentication and authorization
- Secure data storage and transmission
- Vulnerability scanning and remediation
3. **Performance Standards**
- Response time requirements met
- Memory usage within limits
- Database query optimization
- Caching strategy implementation
4. **Reliability and Resilience**
- Circuit breaker patterns
- Graceful degradation
- Health check endpoints
- Automatic retry mechanisms
### Automated Checks
- Unit and integration test coverage > 85%
- Security vulnerability scanning
- Performance benchmarking
- Code quality analysis (SonarQube, ESLint)
- API contract validation
## Evolution Rules
### Across Verticals (Left to Right)
1. **Foundation → Core**
- Basic CRUD operations evolve into complex business workflows
- Simple data validation becomes sophisticated business rules
- Single-tenant services become multi-tenant aware
2. **Core → Integration**
- Internal services expose external APIs
- Synchronous processing includes asynchronous patterns
- Monolithic services become microservice-ready
3. **Integration → Extension**
- Fixed services become configurable and extensible
- Standard implementations support plugin architectures
- Centralized services become distributed and federated
### Service Maturity Levels
- **Level 1**: Basic service with minimal business logic
- **Level 2**: Rich business service with validation and security
- **Level 3**: Advanced service with monitoring and optimization
- **Level 4**: AI-enhanced service with predictive capabilities
## Dependencies
### Upstream Dependencies
1. **Specs Horizontal**
- Business requirements and rules
- Performance and scalability requirements
- Security and compliance requirements
- Integration specifications
2. **Schema Horizontal**
- Data model definitions
- Validation rules and constraints
- Relationship specifications
- Migration strategies
3. **API Horizontal**
- Interface contracts and specifications
- Authentication and authorization patterns
- Request/response schemas
- Error handling conventions
### Downstream Dependencies
1. **Tests Horizontal**
- Service testing strategies
- Integration test scenarios
- Performance benchmarks
- Security test cases
2. **Frontend Horizontal**
- API implementation for client requirements
- Real-time data streaming
- Authentication service integration
## Best Practices
### Architecture Principles
1. **Clean Architecture**
- Separation of concerns
- Dependency inversion
- Domain-driven design
- Testability and maintainability
2. **Microservice Patterns**
- Single responsibility principle
- Database per service
- API gateway pattern
- Service mesh architecture
3. **Event-Driven Architecture**
- Event sourcing patterns
- CQRS implementation
- Saga pattern for distributed transactions
- Event streaming and processing
4. **Security-First Design**
- Zero-trust architecture
- Defense in depth
- Principle of least privilege
- Secure by default
### Common Implementation Patterns
```typescript
// Clean Architecture Service Pattern
interface CreateUserUseCase {
execute(userData: CreateUserRequest): Promise<User>;
}
class CreateUserService implements CreateUserUseCase {
constructor(
private userRepository: UserRepository,
private emailService: EmailService,
private eventBus: EventBus,
private logger: Logger
) {}
async execute(userData: CreateUserRequest): Promise<User> {
this.logger.info('Creating user', { email: userData.email });
// Validation
const validationResult = await this.validateUserData(userData);
if (!validationResult.isValid) {
throw new ValidationError(validationResult.errors);
}
// Check if user exists
const existingUser = await this.userRepository.findByEmail(userData.email);
if (existingUser) {
throw new ConflictError('User with this email already exists');
}
// Create user
const user = await this.createUserWithTransaction(userData);
// Publish event
await this.eventBus.publish(new UserCreatedEvent(user));
this.logger.info('User created successfully', { userId: user.id });
return user;
}
private async createUserWithTransaction(userData: CreateUserRequest): Promise<User> {
return this.userRepository.transaction(async (trx) => {
// Hash password
const hashedPassword = await bcrypt.hash(userData.password, 10);
// Create user
const user = await this.userRepository.create({
...userData,
password: hashedPassword,
emailVerified: false,
createdAt: new Date()
}, trx);
// Create profile
await this.userRepository.createProfile({
userId: user.id,
firstName: userData.firstName,
lastName: userData.lastName
}, trx);
return user;
});
}
private async validateUserData(userData: CreateUserRequest): Promise<ValidationResult> {
const schema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/).required(),
firstName: Joi.string().min(1).max(50).required(),
lastName: Joi.string().min(1).max(50).required()
});
const { error } = schema.validate(userData);
return {
isValid: !error,
errors: error ? error.details.map(d => d.message) : []
};
}
}
// Repository Pattern Implementation
class UserRepository {
constructor(
private db: Database,
private logger: Logger
) {}
async findById(id: string): Promise<User | null> {
const startTime = Date.now();
try {
const result = await this.db
.select('*')
.from('users')
.where('id', id)
.first();
this.logger.debug('User lookup completed', {
userId: id,
duration: Date.now() - startTime,
found: !!result
});
return result ? this.mapToUser(result) : null;
} catch (error) {
this.logger.error('Failed to find user by ID', {
userId: id,
error: error.message
});
throw new DatabaseError('Failed to retrieve user');
}
}
async findByEmail(email: string): Promise<User | null> {
const cacheKey = `user:email:${email}`;
// Check cache first
const cached = await this.cache.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const result = await this.db
.select('*')
.from('users')
.where('email', email)
.first();
const user = result ? this.mapToUser(result) : null;
// Cache result
if (user) {
await this.cache.setex(cacheKey, 300, JSON.stringify(user));
}
return user;
}
async create(userData: CreateUserData, trx?: Transaction): Promise<User> {
const db = trx || this.db;
const [insertedUser] = await db
.insert({
id: uuid.v4(),
email: userData.email,
password_hash: userData.password,
first_name: userData.firstName,
last_name: userData.lastName,
email_verified: userData.emailVerified || false,
created_at: userData.createdAt || new Date(),
updated_at: new Date()
})
.into('users')
.returning('*');
// Invalidate cache
await this.cache.del(`user:email:${userData.email}`);
return this.mapToUser(insertedUser);
}
async transaction<T>(callback: (trx: Transaction) => Promise<T>): Promise<T> {
const trx = await this.db.transaction();
try {
const result = await callback(trx);
await trx.commit();
return result;
} catch (error) {
await trx.rollback();
throw error;
}
}
private mapToUser(row: any): User {
return {
id: row.id,
email: row.email,
firstName: row.first_name,
lastName: row.last_name,
emailVerified: row.email_verified,
createdAt: row.created_at,
updatedAt: row.updated_at
};
}
}
// API Controller Pattern
class UserController {
constructor(
private createUserUseCase: CreateUserUseCase,
private getUserUseCase: GetUserUseCase,
private logger: Logger
) {}
@Post('/users')
@ApiOperation({ summary: 'Create a new user' })
@ApiResponse({ status: 201, description: 'User created successfully' })
@ApiResponse({ status: 400, description: 'Validation error' })
@ApiResponse({ status: 409, description: 'User already exists' })
async createUser(@Body() userData: CreateUserDto): Promise<UserResponseDto> {
const requestId = uuid.v4();
this.logger.info('User creation request received', {
requestId,
email: userData.email
});
try {
const user = await this.createUserUseCase.execute(userData);
this.logger.info('User creation successful', {
requestId,
userId: user.id
});
return {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
createdAt: user.createdAt.toISOString()
};
} catch (error) {
this.logger.error('User creation failed', {
requestId,
error: error.message,
stack: error.stack
});
if (error instanceof ValidationError) {
throw new BadRequestException(error.message);
}
if (error instanceof ConflictError) {
throw new ConflictException(error.message);
}
throw new InternalServerErrorException('Internal server error');
}
}
@Get('/users/:id')
@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Get user by ID' })
@ApiResponse({ status: 200, description: 'User retrieved successfully' })
@ApiResponse({ status: 404, description: 'User not found' })
async getUserById(
@Param('id') id: string,
@CurrentUser() currentUser: User
): Promise<UserResponseDto> {
// Authorization check
if (currentUser.id !== id && !currentUser.isAdmin) {
throw new ForbiddenException('Access denied');
}
const user = await this.getUserUseCase.execute(id);
if (!user) {
throw new NotFoundException('User not found');
}
return {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
createdAt: user.createdAt.toISOString()
};
}
}
```
## Error Handling Patterns
### Centralized Error Handling
```typescript
// Custom Error Classes
class AppError extends Error {
public readonly statusCode: number;
public readonly isOperational: boolean;
constructor(message: string, statusCode: number, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
public readonly validationErrors: string[];
constructor(errors: string[]) {
super('Validation failed', 400);
this.validationErrors = errors;
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} not found`, 404);
}
}
class ConflictError extends AppError {
constructor(message: string) {
super(message, 409);
}
}
// Global Error Handler Middleware
const errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => {
const logger = req.logger || console;
// Log error
if (error instanceof AppError && error.isOperational) {
logger.warn('Operational error', {
error: error.message,
statusCode: error.statusCode,
stack: error.stack,
url: req.url,
method: req.method
});
} else {
logger.error('Programming error', {
error: error.message,
stack: error.stack,
url: req.url,
method: req.method
});
}
// Send error response
if (error instanceof ValidationError) {
return res.status(400).json({
status: 'error',
message: error.message,
errors: error.validationErrors
});
}
if (error instanceof AppError) {
return res.status(error.statusCode).json({
status: 'error',
message: error.message
});
}
// Unknown error
res.status(500).json({
status: 'error',
message: 'Internal server error'
});
};
// Process Error Handlers
process.on('uncaughtException', (error: Error) => {
console.error('Uncaught exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason: any) => {
console.error('Unhandled rejection:', reason);
process.exit(1);
});
```
## Performance Optimization
### Caching Strategies
```typescript
// Multi-Level Caching
class CacheService {
constructor(
private redis: Redis,
private memoryCache: NodeCache,
private logger: Logger
) {}
async get<T>(key: string): Promise<T | null> {
// Level 1: Memory cache
const memoryResult = this.memoryCache.get<T>(key);
if (memoryResult) {
this.logger.debug('Cache hit (memory)', { key });
return memoryResult;
}
// Level 2: Redis cache
const redisResult = await this.redis.get(key);
if (redisResult) {
const parsed = JSON.parse(redisResult);
// Store in memory cache for next time
this.memoryCache.set(key, parsed, 60); // 60 seconds TTL
this.logger.debug('Cache hit (redis)', { key });
return parsed;
}
this.logger.debug('Cache miss', { key });
return null;
}
async set(key: string, value: any, ttl: number = 300): Promise<void> {
// Store in both levels
this.memoryCache.set(key, value, Math.min(ttl, 300));
await this.redis.setex(key, ttl, JSON.stringify(value));
this.logger.debug('Cache set', { key, ttl });
}
async delete(key: string): Promise<void> {
this.memoryCache.del(key);
await this.redis.del(key);
this.logger.debug('Cache deleted', { key });
}
async invalidatePattern(pattern: string): Promise<void> {
const keys = await this.redis.keys(pattern);
if (keys.length > 0) {
await this.redis.del(...keys);
}
// For memory cache, we need to iterate
const memoryKeys = this.memoryCache.keys();
memoryKeys.forEach(key => {
if (key.match(pattern.replace('*', '.*'))) {
this.memoryCache.del(key);
}
});
this.logger.debug('Cache pattern invalidated', { pattern, keysCount: keys.length });
}
}
// Database Query Optimization
class OptimizedUserRepository extends UserRepository {
async findUsersWithPagination(
filters: UserFilters,
pagination: PaginationOptions
): Promise<PaginatedResult<User>> {
const query = this.db
.select(
'u.id',
'u.email',
'u.first_name',
'u.last_name',
'u.created_at',
'p.avatar_url'
)
.from('users as u')
.leftJoin('user_profiles as p', 'u.id', 'p.user_id');
// Apply filters
if (filters.email) {
query.where('u.email', 'ilike', `%${filters.email}%`);
}
if (filters.createdAfter) {
query.where('u.created_at', '>=', filters.createdAfter);
}
if (filters.verified !== undefined) {
query.where('u.email_verified', filters.verified);
}
// Count query for pagination
const countQuery = query.clone().clearSelect().count('* as total');
const [{ total }] = await countQuery;
// Apply pagination
const offset = (pagination.page - 1) * pagination.limit;
const users = await query
.orderBy('u.created_at', 'desc')
.limit(pagination.limit)
.offset(offset);
return {
data: users.map(this.mapToUser),
total: parseInt(total as string),
page: pagination.page,
limit: pagination.limit,
totalPages: Math.ceil(parseInt(total as string) / pagination.limit)
};
}
}
```
## Security Implementation
### Authentication and Authorization
```typescript
// JWT Authentication Service
class AuthService {
constructor(
private userRepository: UserRepository,
private tokenService: TokenService,
private logger: Logger
) {}
async authenticate(email: string, password: string): Promise<AuthResult> {
this.logger.info('Authentication attempt', { email });
// Rate limiting check
const attempts = await this.getFailedAttempts(email);
if (attempts >= 5) {
throw new TooManyAttemptsError('Account temporarily locked');
}
// Find user
const user = await this.userRepository.findByEmail(email);
if (!user) {
await this.recordFailedAttempt(email);
throw new UnauthorizedError('Invalid credentials');
}
// Verify password
const isValidPassword = await bcrypt.compare(password, user.passwordHash);
if (!isValidPassword) {
await this.recordFailedAttempt(email);
throw new UnauthorizedError('Invalid credentials');
}
// Clear failed attempts
await this.clearFailedAttempts(email);
// Generate tokens
const accessToken = this.tokenService.generateAccessToken(user);
const refreshToken = this.tokenService.generateRefreshToken(user);
// Store refresh token
await this.tokenService.storeRefreshToken(refreshToken, user.id);
this.logger.info('Authentication successful', { userId: user.id });
return {
user: this.sanitizeUser(user),
accessToken,
refreshToken
};
}
async refreshToken(refreshToken: string): Promise<TokenRefreshResult> {
const tokenData = await this.tokenService.validateRefreshToken(refreshToken);
if (!tokenData) {
throw new UnauthorizedError('Invalid refresh token');
}
const user = await this.userRepository.findById(tokenData.userId);
if (!user) {
throw new UnauthorizedError('User not found');
}
const newAccessToken = this.tokenService.generateAccessToken(user);
return {
accessToken: newAccessToken,
user: this.sanitizeUser(user)
};
}
private sanitizeUser(user: User): PublicUser {
return {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
roles: user.roles
};
}
}
// Authorization Middleware
const authorize = (requiredPermissions: Permission[]) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
throw new UnauthorizedError('No token provided');
}
const tokenData = await tokenService.validateAccessToken(token);
const user = await userRepository.findById(tokenData.userId);
if (!user) {
throw new UnauthorizedError('User not found');
}
// Check permissions
const hasPermission = requiredPermissions.every(permission =>
user.permissions.includes(permission)
);
if (!hasPermission) {
throw new ForbiddenError('Insufficient permissions');
}
req.user = user;
next();
} catch (error) {
next(error);
}
};
};
```
## Integration Guidelines
### Microservices Communication
```typescript
// Service Communication Pattern
class ServiceCommunicator {
constructor(
private httpClient: HttpClient,
private messageQueue: MessageQueue,
private circuitBreaker: CircuitBreaker,
private logger: Logger
) {}
// Synchronous communication with circuit breaker
async callService<T>(
serviceName: string,
endpoint: string,
data?: any
): Promise<T> {
return this.circuitBreaker.execute(async () => {
const url = `${this.getServiceUrl(serviceName)}${endpoint}`;
const response = await this.httpClient.post(url, data, {
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'X-Request-ID': uuid.v4()
}
});
return response.data;
});
}
// Asynchronous communication via message queue
async publishEvent(event: DomainEvent): Promise<void> {
await this.messageQueue.publish(event.type, {
id: uuid.v4(),
type: event.type,
data: event.data,
timestamp: new Date().toISOString(),
source: process.env.SERVICE_NAME
});
this.logger.info('Event published', {
eventType: event.type,
eventId: event.id
});
}
// Subscribe to events
subscribeToEvents(eventTypes: string[], handler: EventHandler): void {
eventTypes.forEach(eventType => {
this.messageQueue.subscribe(eventType, async (message) => {
try {
await handler(message);
} catch (error) {
this.logger.error('Event processing failed', {
eventType,
error: error.message
});
// Retry logic or dead letter queue
await this.handleEventProcessingError(message, error);
}
});
});
}
}
```
### Tools and Technologies
- **Frameworks**: Node.js (Express, Fastify), Python (FastAPI, Django), Java (Spring Boot)
- **Databases**: PostgreSQL, MongoDB, Redis, Elasticsearch
- **Message Queues**: RabbitMQ, Apache Kafka, AWS SQS
- **Caching**: Redis, Memcached, Application-level caching
- **Monitoring**: Prometheus, Grafana, ELK Stack, Datadog
- **Security**: OAuth 2.0, JWT, Passport.js, Helmet.js
- **Testing**: Jest, Mocha, Pytest, JUnit
- **Documentation**: Swagger/OpenAPI, JSDoc, TypeDoc