mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-01-30 05:44:19 -05:00
1745 lines
39 KiB
Plaintext
1745 lines
39 KiB
Plaintext
# Backend Architecture Guidelines
|
|
|
|
This document contains comprehensive guidelines for creating robust, scalable, and maintainable backend systems.
|
|
|
|
## Architecture Philosophy
|
|
|
|
Build backend systems that are:
|
|
|
|
- **Scalable and Resilient** - Able to handle growing loads and recover from failures
|
|
- **Secure by Design** - Security integrated at every level, not added as an afterthought
|
|
- **Maintainable** - Clean architecture that's easy to understand and extend
|
|
- **Performance Optimized** - Efficient resource utilization and response times
|
|
- **Observable** - Comprehensive logging, monitoring, and debugging capabilities
|
|
|
|
## Core Architecture Principles
|
|
|
|
### Clean Architecture
|
|
|
|
- **Layer Separation**
|
|
- Controllers/Routes (API endpoints)
|
|
- Services (business logic)
|
|
- Data Access (repository pattern)
|
|
- Domain Models (entities)
|
|
- Infrastructure (external services, databases)
|
|
|
|
- **Dependency Inversion**
|
|
- High-level modules don't depend on low-level modules
|
|
- Both depend on abstractions
|
|
- Abstractions don't depend on details
|
|
- Details depend upon abstractions
|
|
|
|
- **Single Responsibility**
|
|
- Each module has one reason to change
|
|
- Functions do one thing well
|
|
- Classes encapsulate cohesive functionality
|
|
- Services manage specific domains
|
|
|
|
- **Domain-Driven Design**
|
|
- Business logic in the domain layer
|
|
- Ubiquitous language shared with stakeholders
|
|
- Bounded contexts to isolate domains
|
|
- Aggregates to enforce invariants
|
|
|
|
### API Design
|
|
|
|
- **RESTful Principles**
|
|
- Resource-oriented endpoints
|
|
- Appropriate HTTP methods
|
|
- Proper status codes
|
|
- Hypermedia links where appropriate
|
|
- Version management
|
|
|
|
- **GraphQL Considerations**
|
|
- Schema-first development
|
|
- Resolver optimization
|
|
- Query complexity analysis
|
|
- Batching and caching
|
|
- Authorization directives
|
|
|
|
- **API Versioning**
|
|
- URL path versioning (`/v1/resources`)
|
|
- Accept header versioning
|
|
- Content negotiation
|
|
- Deprecation strategy
|
|
- Documentation of changes
|
|
|
|
- **Contract First**
|
|
- OpenAPI/Swagger documentation
|
|
- Schema validation
|
|
- Consumer-driven contracts
|
|
- Integration tests against spec
|
|
- Automated documentation
|
|
|
|
### Statelessness and Scaling
|
|
|
|
- **Horizontal Scaling**
|
|
- Stateless services
|
|
- Load balancing
|
|
- Session management via tokens
|
|
- No server affinity
|
|
- Container orchestration
|
|
|
|
- **Vertical Scaling**
|
|
- Resource allocation
|
|
- Performance profiling
|
|
- Memory optimization
|
|
- CPU utilization
|
|
- I/O efficiency
|
|
|
|
- **Microservices Considerations**
|
|
- Service boundaries
|
|
- Inter-service communication
|
|
- API gateways
|
|
- Service discovery
|
|
- Circuit breaking
|
|
|
|
- **Monolith Optimization**
|
|
- Modular architecture
|
|
- Clear boundaries
|
|
- Resource isolation
|
|
- Deployment strategies
|
|
- Scaling bottleneck identification
|
|
|
|
## Database and Data Management
|
|
|
|
### Schema Design
|
|
|
|
- **Relational Database**
|
|
- Normalization (3NF baseline)
|
|
- Foreign key constraints
|
|
- Indexing strategy
|
|
- Query optimization
|
|
- Transaction boundaries
|
|
|
|
- **NoSQL Database**
|
|
- Data access patterns
|
|
- Denormalization strategy
|
|
- Eventual consistency
|
|
- Partition keys
|
|
- Compound indexes
|
|
|
|
- **Migration Strategy**
|
|
- Version control for schema
|
|
- Forward-only migrations
|
|
- Rollback planning
|
|
- Zero-downtime updates
|
|
- Data validation
|
|
|
|
- **Entity Relationships**
|
|
- One-to-one
|
|
- One-to-many
|
|
- Many-to-many
|
|
- Polymorphic relationships
|
|
- Self-referential relationships
|
|
|
|
### Data Access Patterns
|
|
|
|
- **Repository Pattern**
|
|
- Entity-specific repositories
|
|
- Query abstraction
|
|
- Transaction support
|
|
- Caching integration
|
|
- Error handling
|
|
|
|
- **Object-Relational Mapping**
|
|
- Entity mapping
|
|
- Lazy/eager loading
|
|
- Change tracking
|
|
- Query generation
|
|
- Performance considerations
|
|
|
|
- **Query Optimization**
|
|
- Execution plans
|
|
- Index utilization
|
|
- N+1 query prevention
|
|
- Batch operations
|
|
- Connection pooling
|
|
|
|
- **Data Caching**
|
|
- Cache invalidation
|
|
- TTL strategies
|
|
- Distributed caching
|
|
- Cache warming
|
|
- Stale-while-revalidate
|
|
|
|
### Data Integrity and Validation
|
|
|
|
- **Input Validation**
|
|
- Request schema validation
|
|
- Type checking
|
|
- Business rule enforcement
|
|
- Cross-field validation
|
|
- Sanitization
|
|
|
|
- **Output Validation**
|
|
- Response schema conformance
|
|
- Content security
|
|
- Sensitive data redaction
|
|
- Consistent formatting
|
|
- Pagination metadata
|
|
|
|
- **Data Constraints**
|
|
- Database constraints
|
|
- Application-level validation
|
|
- Cross-service consistency
|
|
- Idempotency guarantees
|
|
- State transition rules
|
|
|
|
- **Error Cases**
|
|
- Input validation errors
|
|
- Business rule violations
|
|
- Resource not found
|
|
- Conflict resolution
|
|
- Server capability limits
|
|
|
|
## Security Implementation
|
|
|
|
### Authentication
|
|
|
|
- **User Identity**
|
|
- Credential storage
|
|
- Password policies
|
|
- Multi-factor authentication
|
|
- Account lockout
|
|
- Password reset
|
|
|
|
- **JWT Implementation**
|
|
- Token structure
|
|
- Signing algorithms
|
|
- Expiration strategy
|
|
- Refresh mechanism
|
|
- Revocation strategy
|
|
|
|
- **OAuth/OIDC**
|
|
- Authorization flows
|
|
- Provider integration
|
|
- Scope management
|
|
- Token validation
|
|
- User info endpoints
|
|
|
|
- **API Authentication**
|
|
- API keys
|
|
- Client credentials
|
|
- Certificate-based
|
|
- IP whitelisting
|
|
- Rate limiting
|
|
|
|
### Authorization
|
|
|
|
- **Role-Based Access**
|
|
- Role definition
|
|
- Permission mapping
|
|
- Role hierarchy
|
|
- Default deny
|
|
- Least privilege
|
|
|
|
- **Attribute-Based Access**
|
|
- Policy evaluation
|
|
- Context awareness
|
|
- Dynamic permissions
|
|
- Environmental conditions
|
|
- Temporal constraints
|
|
|
|
- **Resource Ownership**
|
|
- Multi-tenancy
|
|
- User-based isolation
|
|
- Team/organization access
|
|
- Delegation model
|
|
- Ownership transfer
|
|
|
|
- **Permission Enforcement**
|
|
- Controller/middleware level
|
|
- Service layer enforcement
|
|
- Data access filtering
|
|
- Object-level security
|
|
- Field-level security
|
|
|
|
### Data Protection
|
|
|
|
- **Encryption**
|
|
- Data at rest
|
|
- Data in transit
|
|
- Key management
|
|
- Rotation policies
|
|
- Algorithm selection
|
|
|
|
- **PII Handling**
|
|
- Classification
|
|
- Minimization
|
|
- Anonymization
|
|
- Pseudonymization
|
|
- Retention policies
|
|
|
|
- **Secrets Management**
|
|
- Environment variables
|
|
- Vault services
|
|
- Access control
|
|
- Audit logging
|
|
- Rotation strategy
|
|
|
|
- **Database Security**
|
|
- Network isolation
|
|
- Access controls
|
|
- Query parameterization
|
|
- Connection security
|
|
- Auditing
|
|
|
|
### Security Posture
|
|
|
|
- **Vulnerability Management**
|
|
- Dependency scanning
|
|
- Static analysis
|
|
- Dynamic testing
|
|
- Penetration testing
|
|
- Responsible disclosure
|
|
|
|
- **Security Headers**
|
|
- Content Security Policy
|
|
- CORS configuration
|
|
- XSS protection
|
|
- CSRF prevention
|
|
- Clickjacking protection
|
|
|
|
- **Rate Limiting**
|
|
- Request throttling
|
|
- Account limits
|
|
- IP-based limiting
|
|
- Graduated response
|
|
- Breach detection
|
|
|
|
- **Audit Logging**
|
|
- Security events
|
|
- Access attempts
|
|
- Administrative actions
|
|
- Data modifications
|
|
- System changes
|
|
|
|
## Error Handling and Resilience
|
|
|
|
### Error Management
|
|
|
|
- **Standardized Errors**
|
|
- Error classification
|
|
- Status code mapping
|
|
- Error codes
|
|
- User messages
|
|
- Developer details
|
|
|
|
- **Exception Handling**
|
|
- Try-catch patterns
|
|
- Async error handling
|
|
- Middleware interception
|
|
- Global error handlers
|
|
- Service-specific handling
|
|
|
|
- **Graceful Degradation**
|
|
- Fallback strategies
|
|
- Default behaviors
|
|
- Partial content responses
|
|
- Circuit breakers
|
|
- Bulkhead pattern
|
|
|
|
- **Retry Logic**
|
|
- Exponential backoff
|
|
- Jitter implementation
|
|
- Maximum attempts
|
|
- Idempotency guarantees
|
|
- Failure reporting
|
|
|
|
### Resilience Patterns
|
|
|
|
- **Circuit Breaker**
|
|
- Failure threshold
|
|
- Recovery timeout
|
|
- Half-open state
|
|
- Health monitoring
|
|
- Circuit isolation
|
|
|
|
- **Bulkhead Pattern**
|
|
- Resource isolation
|
|
- Thread pools
|
|
- Connection limitations
|
|
- Request prioritization
|
|
- Failure containment
|
|
|
|
- **Timeouts**
|
|
- Connection timeouts
|
|
- Read timeouts
|
|
- Write timeouts
|
|
- Service timeouts
|
|
- Cascade prevention
|
|
|
|
- **Rate Limiters**
|
|
- Request throttling
|
|
- Token bucket algorithm
|
|
- Leaky bucket algorithm
|
|
- Fixed/sliding window
|
|
- Adaptive strategies
|
|
|
|
### Recovery Strategies
|
|
|
|
- **Disaster Recovery**
|
|
- Backup strategy
|
|
- Restore procedures
|
|
- Recovery time objectives
|
|
- Recovery point objectives
|
|
- Failover systems
|
|
|
|
- **Data Consistency**
|
|
- Outbox pattern
|
|
- Saga pattern
|
|
- Eventual consistency
|
|
- Two-phase commit
|
|
- Compensating transactions
|
|
|
|
- **Self-Healing**
|
|
- Health checks
|
|
- Automatic restarts
|
|
- Replication
|
|
- State reconciliation
|
|
- Data repair
|
|
|
|
- **Chaos Engineering**
|
|
- Failure injection
|
|
- Load testing
|
|
- Network degradation
|
|
- Resource exhaustion
|
|
- Recovery validation
|
|
|
|
## Performance Optimization
|
|
|
|
### Response Time
|
|
|
|
- **Caching Strategy**
|
|
- Response caching
|
|
- Object caching
|
|
- Computation caching
|
|
- Cache hierarchy
|
|
- Invalidation triggers
|
|
|
|
- **Query Optimization**
|
|
- Indexing strategy
|
|
- Query planning
|
|
- Result limiting
|
|
- Join optimization
|
|
- Execution analysis
|
|
|
|
- **Async Processing**
|
|
- Background jobs
|
|
- Message queues
|
|
- Scheduled tasks
|
|
- Event-driven architecture
|
|
- Long-running processes
|
|
|
|
- **I/O Management**
|
|
- Connection pooling
|
|
- Batch operations
|
|
- Stream processing
|
|
- File I/O optimization
|
|
- Network I/O efficiency
|
|
|
|
### Resource Utilization
|
|
|
|
- **Memory Management**
|
|
- Memory profiling
|
|
- Leak detection
|
|
- Object pooling
|
|
- Garbage collection tuning
|
|
- Buffer management
|
|
|
|
- **CPU Optimization**
|
|
- Thread allocation
|
|
- Worker processes
|
|
- Computation distribution
|
|
- Algorithm efficiency
|
|
- Hot path optimization
|
|
|
|
- **Database Efficiency**
|
|
- Connection pooling
|
|
- Query optimization
|
|
- Read/write splitting
|
|
- Sharding
|
|
- Replication strategy
|
|
|
|
- **Network Efficiency**
|
|
- Payload compression
|
|
- Request batching
|
|
- Keep-alive connections
|
|
- Response streaming
|
|
- Protocol selection
|
|
|
|
### Scaling Strategies
|
|
|
|
- **Horizontal Scaling**
|
|
- Stateless design
|
|
- Load balancing
|
|
- Session management
|
|
- Data partitioning
|
|
- Service discovery
|
|
|
|
- **Vertical Scaling**
|
|
- Resource allocation
|
|
- Hardware optimization
|
|
- Application tuning
|
|
- Server configuration
|
|
- Database sizing
|
|
|
|
- **Caching Layers**
|
|
- Client-side cache
|
|
- CDN integration
|
|
- API gateway cache
|
|
- Application cache
|
|
- Database cache
|
|
|
|
- **Read/Write Splitting**
|
|
- Command Query Responsibility Segregation
|
|
- Read replicas
|
|
- Write sharding
|
|
- Cache read optimization
|
|
- Eventual consistency model
|
|
|
|
## Observability and Monitoring
|
|
|
|
### Logging
|
|
|
|
- **Log Levels**
|
|
- Error: System failures
|
|
- Warn: Potential issues
|
|
- Info: System events
|
|
- Debug: Development details
|
|
- Trace: Detailed execution flow
|
|
|
|
- **Log Structure**
|
|
- Timestamp
|
|
- Severity
|
|
- Service/component
|
|
- Correlation ID
|
|
- Context information
|
|
|
|
- **Log Storage**
|
|
- Centralized collection
|
|
- Retention policies
|
|
- Access controls
|
|
- Search capabilities
|
|
- Archival strategy
|
|
|
|
- **Log Analysis**
|
|
- Pattern detection
|
|
- Anomaly identification
|
|
- Performance insights
|
|
- Error investigation
|
|
- Audit capabilities
|
|
|
|
### Metrics
|
|
|
|
- **System Metrics**
|
|
- CPU utilization
|
|
- Memory usage
|
|
- Disk I/O
|
|
- Network throughput
|
|
- Connection count
|
|
|
|
- **Application Metrics**
|
|
- Request rate
|
|
- Response time
|
|
- Error rate
|
|
- Concurrent users
|
|
- Business transactions
|
|
|
|
- **Database Metrics**
|
|
- Query performance
|
|
- Connection utilization
|
|
- Index efficiency
|
|
- Lock contention
|
|
- Storage growth
|
|
|
|
- **Custom Business Metrics**
|
|
- Conversion rates
|
|
- User engagement
|
|
- Feature usage
|
|
- Business KPIs
|
|
- Revenue indicators
|
|
|
|
### Tracing
|
|
|
|
- **Distributed Tracing**
|
|
- Trace context propagation
|
|
- Span collection
|
|
- Service mapping
|
|
- Latency analysis
|
|
- Dependency visualization
|
|
|
|
- **Transaction Tracking**
|
|
- Request lifecycle
|
|
- Service boundaries
|
|
- Error propagation
|
|
- Resource utilization
|
|
- External calls
|
|
|
|
- **Performance Profiling**
|
|
- Hotspot identification
|
|
- Method-level timing
|
|
- Resource consumption
|
|
- Lock contention
|
|
- I/O blocking
|
|
|
|
- **User Journey Tracking**
|
|
- Session correlation
|
|
- Flow visualization
|
|
- Conversion funnels
|
|
- Abandonment points
|
|
- Experience metrics
|
|
|
|
### Alerting
|
|
|
|
- **Alert Configuration**
|
|
- Threshold definition
|
|
- Duration conditions
|
|
- Composite alerts
|
|
- Priority levels
|
|
- Notification channels
|
|
|
|
- **Alert Management**
|
|
- Escalation policies
|
|
- On-call rotations
|
|
- Alert grouping
|
|
- Suppression rules
|
|
- Maintenance windows
|
|
|
|
- **Alert Response**
|
|
- Playbooks
|
|
- Automated remediation
|
|
- Incident classification
|
|
- Resolution tracking
|
|
- Post-mortem analysis
|
|
|
|
- **Business Alerting**
|
|
- KPI thresholds
|
|
- Trend deviations
|
|
- Conversion drops
|
|
- Revenue impacts
|
|
- Customer experience degradation
|
|
|
|
## Testing Strategy
|
|
|
|
### Test Types
|
|
|
|
- **Unit Testing**
|
|
- Function/method testing
|
|
- Component isolation
|
|
- Mock dependencies
|
|
- State verification
|
|
- Edge case coverage
|
|
|
|
- **Integration Testing**
|
|
- Service interaction
|
|
- API contracts
|
|
- Database integration
|
|
- External service mocking
|
|
- Environment setup
|
|
|
|
- **System Testing**
|
|
- End-to-end workflows
|
|
- Real environment
|
|
- Data flow validation
|
|
- UI integration
|
|
- Cross-service functionality
|
|
|
|
- **Performance Testing**
|
|
- Load testing
|
|
- Stress testing
|
|
- Endurance testing
|
|
- Spike testing
|
|
- Scalability testing
|
|
|
|
### Test Implementation
|
|
|
|
- **Test-Driven Development**
|
|
- Write tests first
|
|
- Red-green-refactor cycle
|
|
- Continuous test execution
|
|
- Test coverage goals
|
|
- Regression prevention
|
|
|
|
- **Behavior-Driven Development**
|
|
- Specification by example
|
|
- Shared understanding
|
|
- Business-centric language
|
|
- Acceptance criteria
|
|
- Feature validation
|
|
|
|
- **Test Organization**
|
|
- Test hierarchy
|
|
- Descriptive naming
|
|
- Setup and teardown
|
|
- Shared fixtures
|
|
- Test isolation
|
|
|
|
- **Test Automation**
|
|
- CI/CD integration
|
|
- Parallel execution
|
|
- Selective testing
|
|
- Test reporting
|
|
- Failure analysis
|
|
|
|
### Test Data Management
|
|
|
|
- **Test Data Creation**
|
|
- Factories/builders
|
|
- Realistic data
|
|
- Randomization
|
|
- Edge cases
|
|
- Invalid data
|
|
|
|
- **Database Fixtures**
|
|
- Known state setup
|
|
- Test isolation
|
|
- Transactional tests
|
|
- Cleanup procedures
|
|
- Data versioning
|
|
|
|
- **Mocking and Stubbing**
|
|
- External dependencies
|
|
- Service virtualization
|
|
- Response simulation
|
|
- Behavior verification
|
|
- State verification
|
|
|
|
- **Property-Based Testing**
|
|
- Generative testing
|
|
- Invariant checking
|
|
- Random inputs
|
|
- Edge case discovery
|
|
- Shrinking to minimal examples
|
|
|
|
## Deployment and Operations
|
|
|
|
### CI/CD Pipeline
|
|
|
|
- **Continuous Integration**
|
|
- Automated builds
|
|
- Test execution
|
|
- Static analysis
|
|
- Security scanning
|
|
- Artifact generation
|
|
|
|
- **Continuous Delivery**
|
|
- Environment promotion
|
|
- Configuration management
|
|
- Deployment approval
|
|
- Release notes
|
|
- Rollback capability
|
|
|
|
- **Automated Testing**
|
|
- Unit test suite
|
|
- Integration tests
|
|
- E2E testing
|
|
- Performance verification
|
|
- Security validation
|
|
|
|
- **Code Quality Gates**
|
|
- Test coverage
|
|
- Static analysis
|
|
- Duplicate detection
|
|
- Complexity metrics
|
|
- Vulnerability scanning
|
|
|
|
### Infrastructure as Code
|
|
|
|
- **Environment Definition**
|
|
- Infrastructure templates
|
|
- Configuration scripts
|
|
- Network setup
|
|
- Resource allocation
|
|
- Security groups
|
|
|
|
- **Configuration Management**
|
|
- Environment variables
|
|
- Feature flags
|
|
- Secrets handling
|
|
- Parameter stores
|
|
- Configuration versioning
|
|
|
|
- **Deployment Strategies**
|
|
- Blue/green deployment
|
|
- Canary releases
|
|
- Feature flagging
|
|
- A/B testing
|
|
- Rolling updates
|
|
|
|
- **Containerization**
|
|
- Image building
|
|
- Registry management
|
|
- Orchestration
|
|
- Scaling policies
|
|
- Service discovery
|
|
|
|
### Operation Management
|
|
|
|
- **Incident Response**
|
|
- Alert triage
|
|
- Escalation procedures
|
|
- Communication channels
|
|
- Resolution tracking
|
|
- Post-incident review
|
|
|
|
- **Runbooks**
|
|
- Common operations
|
|
- Troubleshooting guides
|
|
- Recovery procedures
|
|
- Maintenance tasks
|
|
- Emergency protocols
|
|
|
|
- **Capacity Planning**
|
|
- Resource monitoring
|
|
- Growth forecasting
|
|
- Scaling thresholds
|
|
- Cost optimization
|
|
- Performance benchmarks
|
|
|
|
- **Change Management**
|
|
- Change requests
|
|
- Risk assessment
|
|
- Approval workflow
|
|
- Implementation plans
|
|
- Verification procedures
|
|
|
|
## Code Organization
|
|
|
|
### Project Structure
|
|
|
|
- **Directory Organization**
|
|
- Feature-based grouping
|
|
- Layer-based separation
|
|
- Config isolation
|
|
- Test proximity
|
|
- Documentation location
|
|
|
|
- **Module System**
|
|
- Clear dependencies
|
|
- Interface definitions
|
|
- Circular dependency prevention
|
|
- Encapsulation
|
|
- Public APIs
|
|
|
|
- **Naming Conventions**
|
|
- Consistent patterns
|
|
- Descriptive names
|
|
- Purpose indication
|
|
- Abbreviation avoidance
|
|
- Version indicators
|
|
|
|
- **Configuration Management**
|
|
- Environment separation
|
|
- Defaults and overrides
|
|
- Validation
|
|
- Documentation
|
|
- Secret handling
|
|
|
|
### Coding Standards
|
|
|
|
- **Style Guidelines**
|
|
- Formatting rules
|
|
- Naming conventions
|
|
- Comment practices
|
|
- Module organization
|
|
- Code documentation
|
|
|
|
- **Code Quality**
|
|
- Complexity limits
|
|
- Function size
|
|
- Class responsibility
|
|
- Coupling metrics
|
|
- Duplication prevention
|
|
|
|
- **Documentation**
|
|
- API documentation
|
|
- Implementation notes
|
|
- Architecture documentation
|
|
- Decision records
|
|
- Operation guides
|
|
|
|
- **Version Control**
|
|
- Commit messages
|
|
- Branch strategy
|
|
- Pull request process
|
|
- Code review standards
|
|
- Merge requirements
|
|
|
|
## Implementation Examples
|
|
|
|
### Express.js API Implementation
|
|
|
|
```typescript
|
|
// src/controllers/userController.ts
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { UserService } from '../services/userService';
|
|
import { CreateUserDto, UpdateUserDto } from '../dtos/userDtos';
|
|
|
|
export class UserController {
|
|
constructor(private userService: UserService) {}
|
|
|
|
async getUsers(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const users = await this.userService.findAll();
|
|
res.status(200).json(users);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
async getUserById(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const user = await this.userService.findById(id);
|
|
|
|
if (!user) {
|
|
res.status(404).json({ message: 'User not found' });
|
|
return;
|
|
}
|
|
|
|
res.status(200).json(user);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
async createUser(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userData: CreateUserDto = req.body;
|
|
const newUser = await this.userService.create(userData);
|
|
res.status(201).json(newUser);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
async updateUser(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const userData: UpdateUserDto = req.body;
|
|
const updatedUser = await this.userService.update(id, userData);
|
|
|
|
if (!updatedUser) {
|
|
res.status(404).json({ message: 'User not found' });
|
|
return;
|
|
}
|
|
|
|
res.status(200).json(updatedUser);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
async deleteUser(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const id = parseInt(req.params.id);
|
|
const deleted = await this.userService.delete(id);
|
|
|
|
if (!deleted) {
|
|
res.status(404).json({ message: 'User not found' });
|
|
return;
|
|
}
|
|
|
|
res.status(204).send();
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Service Layer Implementation
|
|
|
|
```typescript
|
|
// src/services/userService.ts
|
|
import { User } from '../models/user';
|
|
import { UserRepository } from '../repositories/userRepository';
|
|
import { CreateUserDto, UpdateUserDto } from '../dtos/userDtos';
|
|
import { NotFoundError, ValidationError } from '../utils/errors';
|
|
|
|
export class UserService {
|
|
constructor(private userRepository: UserRepository) {}
|
|
|
|
async findAll(): Promise<User[]> {
|
|
return this.userRepository.findAll();
|
|
}
|
|
|
|
async findById(id: number): Promise<User | null> {
|
|
return this.userRepository.findById(id);
|
|
}
|
|
|
|
async create(userData: CreateUserDto): Promise<User> {
|
|
// Validate data
|
|
this.validateUserData(userData);
|
|
|
|
// Check if email already exists
|
|
const existingUser = await this.userRepository.findByEmail(userData.email);
|
|
if (existingUser) {
|
|
throw new ValidationError('Email already registered');
|
|
}
|
|
|
|
// Create new user
|
|
return this.userRepository.create(userData);
|
|
}
|
|
|
|
async update(id: number, userData: UpdateUserDto): Promise<User | null> {
|
|
// Validate data
|
|
this.validateUserData(userData, false);
|
|
|
|
// Check if user exists
|
|
const user = await this.userRepository.findById(id);
|
|
if (!user) {
|
|
throw new NotFoundError('User not found');
|
|
}
|
|
|
|
// Check email uniqueness if changing email
|
|
if (userData.email && userData.email !== user.email) {
|
|
const existingUser = await this.userRepository.findByEmail(userData.email);
|
|
if (existingUser) {
|
|
throw new ValidationError('Email already registered');
|
|
}
|
|
}
|
|
|
|
// Update user
|
|
return this.userRepository.update(id, userData);
|
|
}
|
|
|
|
async delete(id: number): Promise<boolean> {
|
|
return this.userRepository.delete(id);
|
|
}
|
|
|
|
private validateUserData(data: CreateUserDto | UpdateUserDto, isCreating = true): void {
|
|
const errors: string[] = [];
|
|
|
|
if (isCreating && !data.email) {
|
|
errors.push('Email is required');
|
|
}
|
|
|
|
if (data.email && !this.isValidEmail(data.email)) {
|
|
errors.push('Invalid email format');
|
|
}
|
|
|
|
if (isCreating && !data.password) {
|
|
errors.push('Password is required');
|
|
}
|
|
|
|
if (data.password && data.password.length < 8) {
|
|
errors.push('Password must be at least 8 characters long');
|
|
}
|
|
|
|
if (errors.length > 0) {
|
|
throw new ValidationError(errors.join(', '));
|
|
}
|
|
}
|
|
|
|
private isValidEmail(email: string): boolean {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Repository Pattern Implementation
|
|
|
|
```typescript
|
|
// src/repositories/userRepository.ts
|
|
import { db } from '../config/database';
|
|
import { User } from '../models/user';
|
|
import { CreateUserDto, UpdateUserDto } from '../dtos/userDtos';
|
|
import { hashPassword } from '../utils/auth';
|
|
|
|
export class UserRepository {
|
|
async findAll(): Promise<User[]> {
|
|
return db.users.findMany({
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
async findById(id: number): Promise<User | null> {
|
|
return db.users.findUnique({
|
|
where: { id },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
async findByEmail(email: string): Promise<User | null> {
|
|
return db.users.findUnique({
|
|
where: { email },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
async create(userData: CreateUserDto): Promise<User> {
|
|
const hashedPassword = await hashPassword(userData.password);
|
|
|
|
return db.users.create({
|
|
data: {
|
|
name: userData.name,
|
|
email: userData.email,
|
|
password: hashedPassword,
|
|
role: userData.role || 'USER',
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
async update(id: number, userData: UpdateUserDto): Promise<User | null> {
|
|
const data: any = {
|
|
name: userData.name,
|
|
email: userData.email,
|
|
role: userData.role,
|
|
};
|
|
|
|
// Only hash password if it's included in the update
|
|
if (userData.password) {
|
|
data.password = await hashPassword(userData.password);
|
|
}
|
|
|
|
// Remove undefined values
|
|
Object.keys(data).forEach(key => data[key] === undefined && delete data[key]);
|
|
|
|
return db.users.update({
|
|
where: { id },
|
|
data,
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
role: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
async delete(id: number): Promise<boolean> {
|
|
try {
|
|
await db.users.delete({
|
|
where: { id },
|
|
});
|
|
return true;
|
|
} catch (error) {
|
|
// If no rows were affected, user didn't exist
|
|
if (error.code === 'P2025') {
|
|
return false;
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error Handling Middleware
|
|
|
|
```typescript
|
|
// src/middleware/errorHandler.ts
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import { ValidationError, NotFoundError, AuthorizationError } from '../utils/errors';
|
|
import logger from '../utils/logger';
|
|
|
|
export function errorHandler(
|
|
err: Error,
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
) {
|
|
// Log all errors
|
|
logger.error({
|
|
message: err.message,
|
|
stack: err.stack,
|
|
method: req.method,
|
|
path: req.path,
|
|
ip: req.ip,
|
|
userId: req.user?.id,
|
|
});
|
|
|
|
// Handle specific error types
|
|
if (err instanceof ValidationError) {
|
|
return res.status(400).json({
|
|
status: 'error',
|
|
message: err.message,
|
|
code: 'VALIDATION_ERROR',
|
|
});
|
|
}
|
|
|
|
if (err instanceof NotFoundError) {
|
|
return res.status(404).json({
|
|
status: 'error',
|
|
message: err.message,
|
|
code: 'NOT_FOUND',
|
|
});
|
|
}
|
|
|
|
if (err instanceof AuthorizationError) {
|
|
return res.status(403).json({
|
|
status: 'error',
|
|
message: err.message,
|
|
code: 'FORBIDDEN',
|
|
});
|
|
}
|
|
|
|
// Handle unexpected errors
|
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
|
|
return res.status(500).json({
|
|
status: 'error',
|
|
message: 'Internal server error',
|
|
code: 'SERVER_ERROR',
|
|
...(isDevelopment && {
|
|
detail: err.message,
|
|
stack: err.stack,
|
|
}),
|
|
});
|
|
}
|
|
```
|
|
|
|
### Authentication Implementation
|
|
|
|
```typescript
|
|
// src/middleware/authenticate.ts
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import jwt from 'jsonwebtoken';
|
|
import { UserService } from '../services/userService';
|
|
import { AuthorizationError } from '../utils/errors';
|
|
|
|
interface TokenPayload {
|
|
userId: number;
|
|
role: string;
|
|
iat: number;
|
|
exp: number;
|
|
}
|
|
|
|
export function authenticate(
|
|
requiredRoles: string[] = []
|
|
) {
|
|
return async (req: Request, res: Response, next: NextFunction) => {
|
|
try {
|
|
// Get token from authorization header
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
throw new AuthorizationError('Authentication required');
|
|
}
|
|
|
|
const token = authHeader.split(' ')[1];
|
|
|
|
// Verify token
|
|
let payload: TokenPayload;
|
|
try {
|
|
payload = jwt.verify(
|
|
token,
|
|
process.env.JWT_SECRET!
|
|
) as TokenPayload;
|
|
} catch (err) {
|
|
throw new AuthorizationError('Invalid token');
|
|
}
|
|
|
|
// Check token expiration
|
|
const now = Math.floor(Date.now() / 1000);
|
|
if (payload.exp < now) {
|
|
throw new AuthorizationError('Token expired');
|
|
}
|
|
|
|
// Check role if required
|
|
if (requiredRoles.length > 0 && !requiredRoles.includes(payload.role)) {
|
|
throw new AuthorizationError('Insufficient permissions');
|
|
}
|
|
|
|
// Get user and attach to request
|
|
const userService = new UserService();
|
|
const user = await userService.findById(payload.userId);
|
|
|
|
if (!user) {
|
|
throw new AuthorizationError('User not found');
|
|
}
|
|
|
|
// Attach user to request object
|
|
req.user = user;
|
|
|
|
next();
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
};
|
|
}
|
|
```
|
|
|
|
### Custom Error Classes
|
|
|
|
```typescript
|
|
// src/utils/errors.ts
|
|
export class ValidationError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'ValidationError';
|
|
Object.setPrototypeOf(this, ValidationError.prototype);
|
|
}
|
|
}
|
|
|
|
export class NotFoundError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'NotFoundError';
|
|
Object.setPrototypeOf(this, NotFoundError.prototype);
|
|
}
|
|
}
|
|
|
|
export class AuthorizationError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'AuthorizationError';
|
|
Object.setPrototypeOf(this, AuthorizationError.prototype);
|
|
}
|
|
}
|
|
|
|
export class DatabaseError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'DatabaseError';
|
|
Object.setPrototypeOf(this, DatabaseError.prototype);
|
|
}
|
|
}
|
|
|
|
export class ServiceError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'ServiceError';
|
|
Object.setPrototypeOf(this, ServiceError.prototype);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Libraries and Tools
|
|
|
|
### Core Dependencies
|
|
|
|
- **Web Framework**
|
|
- Express.js
|
|
- Fastify
|
|
- NestJS
|
|
- Koa
|
|
|
|
- **Database Access**
|
|
- Prisma
|
|
- TypeORM
|
|
- Sequelize
|
|
- Knex
|
|
|
|
- **Validation**
|
|
- Joi
|
|
- Zod
|
|
- class-validator
|
|
- Yup
|
|
|
|
- **Authentication**
|
|
- Passport.js
|
|
- jsonwebtoken
|
|
- bcrypt
|
|
- OAuth libraries
|
|
|
|
### Development Tools
|
|
|
|
- **Testing**
|
|
- Jest
|
|
- Mocha/Chai
|
|
- SuperTest
|
|
- Cypress
|
|
- k6
|
|
|
|
- **Linting & Formatting**
|
|
- ESLint
|
|
- Prettier
|
|
- TypeScript
|
|
- Husky (pre-commit hooks)
|
|
- lint-staged
|
|
|
|
- **Documentation**
|
|
- Swagger/OpenAPI
|
|
- JSDoc
|
|
- Postman collections
|
|
- Markdown documentation
|
|
- Architecture Decision Records (ADRs)
|
|
|
|
- **Monitoring**
|
|
- Prometheus
|
|
- Grafana
|
|
- Datadog
|
|
- New Relic
|
|
- Sentry
|
|
|
|
## Best Practices Checklist
|
|
|
|
### Development Checklist
|
|
|
|
- [ ] Use TypeScript with strict mode
|
|
- [ ] Document all APIs with OpenAPI/Swagger
|
|
- [ ] Write comprehensive tests (unit, integration, E2E)
|
|
- [ ] Automate CI/CD pipeline
|
|
- [ ] Implement request validation
|
|
- [ ] Set up logging and monitoring
|
|
- [ ] Use consistent error handling
|
|
- [ ] Configure linting and code formatting
|
|
- [ ] Implement authentication and authorization
|
|
- [ ] Set up database migrations
|
|
|
|
### Security Checklist
|
|
|
|
- [ ] Use HTTPS for all communications
|
|
- [ ] Implement proper authentication
|
|
- [ ] Apply authorization on all endpoints
|
|
- [ ] Validate all input data
|
|
- [ ] Use parameterized queries
|
|
- [ ] Sanitize output to prevent XSS
|
|
- [ ] Apply rate limiting
|
|
- [ ] Set security headers
|
|
- [ ] Handle secrets securely
|
|
- [ ] Scan dependencies for vulnerabilities
|
|
|
|
### Production Readiness
|
|
|
|
- [ ] Configure proper logging
|
|
- [ ] Set up monitoring and alerts
|
|
- [ ] Implement health checks
|
|
- [ ] Configure CI/CD pipeline
|
|
- [ ] Document deployment procedures
|
|
- [ ] Create runbooks for common operations
|
|
- [ ] Set up backup and restore procedures
|
|
- [ ] Plan for scaling
|
|
- [ ] Document API contracts
|
|
- [ ] Create incident response plan
|
|
|
|
## Code Examples
|
|
|
|
### Express.js Application Setup
|
|
|
|
```typescript
|
|
// src/app.ts
|
|
import express from 'express';
|
|
import cors from 'cors';
|
|
import helmet from 'helmet';
|
|
import compression from 'compression';
|
|
import rateLimit from 'express-rate-limit';
|
|
import { errorHandler } from './middleware/errorHandler';
|
|
import { notFoundHandler } from './middleware/notFoundHandler';
|
|
import { requestLogger } from './middleware/requestLogger';
|
|
import routes from './routes';
|
|
|
|
const app = express();
|
|
|
|
// Middleware
|
|
app.use(helmet()); // Security headers
|
|
app.use(cors()); // CORS handling
|
|
app.use(compression()); // Response compression
|
|
app.use(express.json()); // Parse JSON bodies
|
|
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
|
|
app.use(requestLogger); // Log all requests
|
|
|
|
// Rate limiting
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // 100 requests per IP
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
message: 'Too many requests from this IP, please try again later',
|
|
});
|
|
app.use(limiter);
|
|
|
|
// Routes
|
|
app.use('/api', routes);
|
|
|
|
// Error handling
|
|
app.use(notFoundHandler);
|
|
app.use(errorHandler);
|
|
|
|
export default app;
|
|
```
|
|
|
|
### API Route Definition
|
|
|
|
```typescript
|
|
// src/routes/userRoutes.ts
|
|
import { Router } from 'express';
|
|
import { UserController } from '../controllers/userController';
|
|
import { UserService } from '../services/userService';
|
|
import { UserRepository } from '../repositories/userRepository';
|
|
import { authenticate } from '../middleware/authenticate';
|
|
import { validateRequest } from '../middleware/validateRequest';
|
|
import { createUserSchema, updateUserSchema } from '../validation/userSchemas';
|
|
|
|
const router = Router();
|
|
const userRepository = new UserRepository();
|
|
const userService = new UserService(userRepository);
|
|
const userController = new UserController(userService);
|
|
|
|
router.get('/',
|
|
authenticate(['ADMIN']),
|
|
(req, res, next) => userController.getUsers(req, res, next)
|
|
);
|
|
|
|
router.get('/:id',
|
|
authenticate(),
|
|
(req, res, next) => userController.getUserById(req, res, next)
|
|
);
|
|
|
|
router.post('/',
|
|
validateRequest(createUserSchema),
|
|
(req, res, next) => userController.createUser(req, res, next)
|
|
);
|
|
|
|
router.put('/:id',
|
|
authenticate(),
|
|
validateRequest(updateUserSchema),
|
|
(req, res, next) => userController.updateUser(req, res, next)
|
|
);
|
|
|
|
router.delete('/:id',
|
|
authenticate(['ADMIN']),
|
|
(req, res, next) => userController.deleteUser(req, res, next)
|
|
);
|
|
|
|
export default router;
|
|
```
|
|
|
|
### Dependency Injection Setup
|
|
|
|
```typescript
|
|
// src/config/container.ts
|
|
import { Container } from 'inversify';
|
|
import { TYPES } from './types';
|
|
import { UserController } from '../controllers/userController';
|
|
import { UserService } from '../services/userService';
|
|
import { UserRepository } from '../repositories/userRepository';
|
|
import { DatabaseConnection } from '../config/database';
|
|
import { Logger } from '../utils/logger';
|
|
|
|
const container = new Container();
|
|
|
|
// Infrastructure
|
|
container.bind<DatabaseConnection>(TYPES.DatabaseConnection).to(DatabaseConnection).inSingletonScope();
|
|
container.bind<Logger>(TYPES.Logger).to(Logger).inSingletonScope();
|
|
|
|
// Repositories
|
|
container.bind<UserRepository>(TYPES.UserRepository).to(UserRepository).inSingletonScope();
|
|
|
|
// Services
|
|
container.bind<UserService>(TYPES.UserService).to(UserService).inSingletonScope();
|
|
|
|
// Controllers
|
|
container.bind<UserController>(TYPES.UserController).to(UserController).inSingletonScope();
|
|
|
|
export { container };
|
|
```
|
|
|
|
### Testing Example
|
|
|
|
```typescript
|
|
// src/services/__tests__/userService.test.ts
|
|
import { UserService } from '../userService';
|
|
import { UserRepository } from '../../repositories/userRepository';
|
|
import { ValidationError, NotFoundError } from '../../utils/errors';
|
|
|
|
// Mock the repository
|
|
jest.mock('../../repositories/userRepository');
|
|
|
|
describe('UserService', () => {
|
|
let userService: UserService;
|
|
let userRepository: jest.Mocked<UserRepository>;
|
|
|
|
beforeEach(() => {
|
|
userRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
userService = new UserService(userRepository);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('create', () => {
|
|
it('should create a user with valid data', async () => {
|
|
// Arrange
|
|
const userData = {
|
|
name: 'John Doe',
|
|
email: 'john@example.com',
|
|
password: 'password123',
|
|
};
|
|
|
|
userRepository.findByEmail.mockResolvedValue(null);
|
|
userRepository.create.mockResolvedValue({
|
|
id: 1,
|
|
...userData,
|
|
role: 'USER',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
|
|
// Act
|
|
const result = await userService.create(userData);
|
|
|
|
// Assert
|
|
expect(userRepository.findByEmail).toHaveBeenCalledWith(userData.email);
|
|
expect(userRepository.create).toHaveBeenCalledWith(userData);
|
|
expect(result).toHaveProperty('id', 1);
|
|
expect(result).toHaveProperty('name', userData.name);
|
|
expect(result).toHaveProperty('email', userData.email);
|
|
});
|
|
|
|
it('should throw ValidationError if email already exists', async () => {
|
|
// Arrange
|
|
const userData = {
|
|
name: 'John Doe',
|
|
email: 'john@example.com',
|
|
password: 'password123',
|
|
};
|
|
|
|
userRepository.findByEmail.mockResolvedValue({
|
|
id: 1,
|
|
name: 'Existing User',
|
|
email: userData.email,
|
|
role: 'USER',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
|
|
// Act & Assert
|
|
await expect(userService.create(userData)).rejects.toThrow(ValidationError);
|
|
expect(userRepository.create).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should throw ValidationError if email is invalid', async () => {
|
|
// Arrange
|
|
const userData = {
|
|
name: 'John Doe',
|
|
email: 'invalid-email',
|
|
password: 'password123',
|
|
};
|
|
|
|
// Act & Assert
|
|
await expect(userService.create(userData)).rejects.toThrow(ValidationError);
|
|
expect(userRepository.findByEmail).not.toHaveBeenCalled();
|
|
expect(userRepository.create).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('update', () => {
|
|
it('should update a user with valid data', async () => {
|
|
// Arrange
|
|
const userId = 1;
|
|
const userData = {
|
|
name: 'Updated Name',
|
|
};
|
|
|
|
userRepository.findById.mockResolvedValue({
|
|
id: userId,
|
|
name: 'Original Name',
|
|
email: 'user@example.com',
|
|
role: 'USER',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
|
|
userRepository.update.mockResolvedValue({
|
|
id: userId,
|
|
name: 'Updated Name',
|
|
email: 'user@example.com',
|
|
role: 'USER',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
});
|
|
|
|
// Act
|
|
const result = await userService.update(userId, userData);
|
|
|
|
// Assert
|
|
expect(userRepository.findById).toHaveBeenCalledWith(userId);
|
|
expect(userRepository.update).toHaveBeenCalledWith(userId, userData);
|
|
expect(result).toHaveProperty('id', userId);
|
|
expect(result).toHaveProperty('name', 'Updated Name');
|
|
});
|
|
|
|
it('should throw NotFoundError if user does not exist', async () => {
|
|
// Arrange
|
|
const userId = 999;
|
|
const userData = {
|
|
name: 'Updated Name',
|
|
};
|
|
|
|
userRepository.findById.mockResolvedValue(null);
|
|
|
|
// Act & Assert
|
|
await expect(userService.update(userId, userData)).rejects.toThrow(NotFoundError);
|
|
expect(userRepository.update).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Code Structure Best Practices
|
|
|
|
- Keep files small and focused on a single responsibility
|
|
- Use consistent naming conventions across the codebase
|
|
- Group related functionality together
|
|
- Separate business logic from infrastructure concerns
|
|
- Maintain clear dependency boundaries
|
|
|
|
### API Design Best Practices
|
|
|
|
- Use nouns for resource endpoints
|
|
- Keep URLs clean and simple
|
|
- Use HTTP methods appropriately
|
|
- Return appropriate status codes
|
|
- Document all endpoints
|
|
|
|
### Security Best Practices
|
|
|
|
- Never trust client input
|
|
- Implement proper authentication and authorization
|
|
- Follow the principle of least privilege
|
|
- Keep dependencies up to date
|
|
- Run regular security scans
|
|
|
|
### Performance Best Practices
|
|
|
|
- Implement appropriate caching
|
|
- Optimize database queries
|
|
- Use async/await for I/O operations
|
|
- Monitor and optimize bottlenecks
|
|
- Consider pagination for large data sets
|
|
|
|
### Testing Best Practices
|
|
|
|
- Write tests at multiple levels (unit, integration, system)
|
|
- Use test doubles (mocks, stubs, spies) appropriately
|
|
- Aim for high code coverage
|
|
- Test happy paths and edge cases
|
|
- Incorporate testing into the development workflow |