From 83081e8f297b8d1071c5ef2e8e2fbfc55f1cb81a Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Wed, 14 Jan 2026 11:49:58 +0700 Subject: [PATCH] chore: Remove obsolete skill documentation files - Deleted multiple outdated skill documentation files, including those related to API design, API gateway patterns, versioning strategies, caching patterns, CI/CD advanced patterns, bilingual code comments, configuration management, data consistency patterns, Prisma database patterns, Kubernetes deployment patterns, and more. - This cleanup streamlines the documentation repository, ensuring that only relevant and up-to-date information is maintained for current development practices. --- docs/en/skills/api-design.md | 602 ------------ docs/en/skills/api-gateway-advanced.md | 204 ---- docs/en/skills/api-versioning-strategy.md | 404 -------- docs/en/skills/caching-patterns.md | 369 ------- docs/en/skills/cicd-advanced-patterns.md | 544 ---------- docs/en/skills/comment-code.md | 489 --------- docs/en/skills/configuration-management.md | 131 --- docs/en/skills/data-consistency-patterns.md | 363 ------- docs/en/skills/database-prisma.md | 571 ----------- docs/en/skills/deployment-kubernetes.md | 618 ------------ docs/en/skills/documentation.md | 507 ---------- docs/en/skills/error-handling-patterns.md | 460 --------- docs/en/skills/event-driven-architecture.md | 452 --------- docs/en/skills/infrastructure-as-code.md | 224 ----- docs/en/skills/inter-service-communication.md | 280 ------ .../microservices-development-process.md | 660 ------------- docs/en/skills/middleware-patterns.md | 413 -------- docs/en/skills/observability-monitoring.md | 658 ------------- docs/en/skills/performance-optimization.md | 158 --- docs/en/skills/project-rules.md | 400 -------- docs/en/skills/repository-pattern.md | 334 ------- docs/en/skills/resilience-patterns.md | 239 ----- docs/en/skills/security.md | 925 ------------------ docs/en/skills/service-discovery-registry.md | 323 ------ docs/en/skills/service-layer-patterns.md | 338 ------- docs/en/skills/testing-patterns.md | 589 ----------- docs/vi/skills/api-design.md | 578 ----------- docs/vi/skills/api-gateway-advanced.md | 197 ---- docs/vi/skills/api-versioning-strategy.md | 456 --------- docs/vi/skills/caching-patterns.md | 420 -------- docs/vi/skills/cicd-advanced-patterns.md | 578 ----------- docs/vi/skills/comment-code.md | 682 ------------- docs/vi/skills/configuration-management.md | 119 --- docs/vi/skills/data-consistency-patterns.md | 378 ------- docs/vi/skills/database-prisma.md | 683 ------------- docs/vi/skills/deployment-kubernetes.md | 585 ----------- docs/vi/skills/documentation.md | 602 ------------ docs/vi/skills/error-handling-patterns.md | 488 --------- docs/vi/skills/event-driven-architecture.md | 505 ---------- docs/vi/skills/infrastructure-as-code.md | 244 ----- docs/vi/skills/inter-service-communication.md | 303 ------ .../microservices-development-process.md | 344 ------- docs/vi/skills/middleware-patterns.md | 462 --------- docs/vi/skills/observability-monitoring.md | 723 -------------- docs/vi/skills/performance-optimization.md | 154 --- docs/vi/skills/project-rules.md | 564 ----------- docs/vi/skills/repository-pattern.md | 421 -------- docs/vi/skills/resilience-patterns.md | 217 ---- docs/vi/skills/security.md | 853 ---------------- docs/vi/skills/service-discovery-registry.md | 344 ------- docs/vi/skills/service-layer-patterns.md | 416 -------- docs/vi/skills/testing-patterns.md | 841 ---------------- 52 files changed, 23412 deletions(-) delete mode 100644 docs/en/skills/api-design.md delete mode 100644 docs/en/skills/api-gateway-advanced.md delete mode 100644 docs/en/skills/api-versioning-strategy.md delete mode 100644 docs/en/skills/caching-patterns.md delete mode 100644 docs/en/skills/cicd-advanced-patterns.md delete mode 100644 docs/en/skills/comment-code.md delete mode 100644 docs/en/skills/configuration-management.md delete mode 100644 docs/en/skills/data-consistency-patterns.md delete mode 100644 docs/en/skills/database-prisma.md delete mode 100644 docs/en/skills/deployment-kubernetes.md delete mode 100644 docs/en/skills/documentation.md delete mode 100644 docs/en/skills/error-handling-patterns.md delete mode 100644 docs/en/skills/event-driven-architecture.md delete mode 100644 docs/en/skills/infrastructure-as-code.md delete mode 100644 docs/en/skills/inter-service-communication.md delete mode 100644 docs/en/skills/microservices-development-process.md delete mode 100644 docs/en/skills/middleware-patterns.md delete mode 100644 docs/en/skills/observability-monitoring.md delete mode 100644 docs/en/skills/performance-optimization.md delete mode 100644 docs/en/skills/project-rules.md delete mode 100644 docs/en/skills/repository-pattern.md delete mode 100644 docs/en/skills/resilience-patterns.md delete mode 100644 docs/en/skills/security.md delete mode 100644 docs/en/skills/service-discovery-registry.md delete mode 100644 docs/en/skills/service-layer-patterns.md delete mode 100644 docs/en/skills/testing-patterns.md delete mode 100644 docs/vi/skills/api-design.md delete mode 100644 docs/vi/skills/api-gateway-advanced.md delete mode 100644 docs/vi/skills/api-versioning-strategy.md delete mode 100644 docs/vi/skills/caching-patterns.md delete mode 100644 docs/vi/skills/cicd-advanced-patterns.md delete mode 100644 docs/vi/skills/comment-code.md delete mode 100644 docs/vi/skills/configuration-management.md delete mode 100644 docs/vi/skills/data-consistency-patterns.md delete mode 100644 docs/vi/skills/database-prisma.md delete mode 100644 docs/vi/skills/deployment-kubernetes.md delete mode 100644 docs/vi/skills/documentation.md delete mode 100644 docs/vi/skills/error-handling-patterns.md delete mode 100644 docs/vi/skills/event-driven-architecture.md delete mode 100644 docs/vi/skills/infrastructure-as-code.md delete mode 100644 docs/vi/skills/inter-service-communication.md delete mode 100644 docs/vi/skills/microservices-development-process.md delete mode 100644 docs/vi/skills/middleware-patterns.md delete mode 100644 docs/vi/skills/observability-monitoring.md delete mode 100644 docs/vi/skills/performance-optimization.md delete mode 100644 docs/vi/skills/project-rules.md delete mode 100644 docs/vi/skills/repository-pattern.md delete mode 100644 docs/vi/skills/resilience-patterns.md delete mode 100644 docs/vi/skills/security.md delete mode 100644 docs/vi/skills/service-discovery-registry.md delete mode 100644 docs/vi/skills/service-layer-patterns.md delete mode 100644 docs/vi/skills/testing-patterns.md diff --git a/docs/en/skills/api-design.md b/docs/en/skills/api-design.md deleted file mode 100644 index 1b0f3a89..00000000 --- a/docs/en/skills/api-design.md +++ /dev/null @@ -1,602 +0,0 @@ ---- -name: api-design -description: RESTful API design standards for GoodGo microservices. Use when creating new API endpoints, designing DTOs, implementing controllers, writing OpenAPI documentation, or standardizing API responses. ---- - -# RESTful API Design Standards - -## When to Use This Skill - -Use this skill when: -- Creating new API endpoints -- Designing request/response DTOs -- Implementing controllers and routes -- Writing OpenAPI/Swagger documentation -- Standardizing error responses -- Implementing pagination, filtering, and sorting -- Setting up API versioning -- Designing resource relationships - -## Core Principles - -1. **Consistency**: All APIs follow the same patterns -2. **Predictability**: Developers can guess endpoint behavior -3. **Simplicity**: Easy to understand and use -4. **Documentation**: Self-documenting through OpenAPI -5. **Error Handling**: Clear, actionable error messages - -## Request/Response Flow - -The following diagram illustrates how a request flows through the API layers: - -```mermaid -sequenceDiagram - participant Client - participant Middleware - participant Controller - participant Service - participant Repository - participant Database - - Client->>Middleware: HTTP Request - Middleware->>Middleware: Authentication
Rate Limiting
Validation - Middleware->>Controller: Validated Request - Controller->>Controller: Parse DTO
Extract Params - Controller->>Service: Business Logic Call - Service->>Repository: Data Access Call - Repository->>Database: Query Execution - Database-->>Repository: Data Result - Repository-->>Service: Entity/Entities - Service-->>Controller: Business Result - Controller->>Controller: Transform to DTO
Format Response - Controller-->>Middleware: Response Object - Middleware-->>Client: HTTP Response -``` - -## API Structure Hierarchy - -The following diagram shows the hierarchical structure of RESTful API endpoints: - -```mermaid -graph TD - A[API Base URL
https://api.goodgo.com] --> B[Version
/v1] - B --> C[Resource Collection
/users] - B --> D[Resource Collection
/orders] - B --> E[Resource Collection
/products] - - C --> F[Resource Instance
/users/:id] - C --> G[Sub-Resource
/users/:id/orders] - - F --> H[GET /users/:id
Retrieve user] - F --> I[PUT /users/:id
Update user] - F --> J[DELETE /users/:id
Delete user] - - C --> K[GET /users
List users] - C --> L[POST /users
Create user] - - G --> M[GET /users/:id/orders
List user orders] - G --> N[POST /users/:id/orders
Create order] - - style A fill:#e1f5ff - style B fill:#b3e5fc - style C fill:#81d4fa - style D fill:#81d4fa - style E fill:#81d4fa - style F fill:#4fc3f7 - style G fill:#4fc3f7 -``` - -## URL Structure - -``` -https://api.goodgo.com/v1/{resource}/{id}/{sub-resource} - -Examples: -GET /v1/users # List users -POST /v1/users # Create user -GET /v1/users/123 # Get user by ID -PUT /v1/users/123 # Update user -DELETE /v1/users/123 # Delete user -GET /v1/users/123/orders # Get user's orders -POST /v1/users/123/orders # Create order for user -``` - -## HTTP Methods - -- **GET**: Retrieve resource(s) - Safe, Idempotent -- **POST**: Create new resource - Not idempotent -- **PUT**: Full update - Idempotent -- **PATCH**: Partial update - Idempotent -- **DELETE**: Remove resource - Idempotent - -## Standard Response Format - -### Success Response - -```typescript -interface SuccessResponse { - success: true; - data: T; - metadata?: { - timestamp: string; - version: string; - requestId: string; - }; - pagination?: { - page: number; - limit: number; - total: number; - totalPages: number; - }; -} - -// Example -{ - "success": true, - "data": { - "id": "123", - "email": "user@example.com", - "name": "John Doe" - }, - "metadata": { - "timestamp": "2024-01-01T00:00:00Z", - "version": "1.0.0", - "requestId": "req_abc123" - } -} -``` - -### Error Response - -```typescript -interface ErrorResponse { - success: false; - error: { - code: string; - message: string; - details?: any; - field?: string; - stack?: string; // Only in development - }; - metadata?: { - timestamp: string; - requestId: string; - }; -} - -// Example -{ - "success": false, - "error": { - "code": "VALIDATION_ERROR", - "message": "Invalid email format", - "field": "email", - "details": { - "provided": "invalid-email", - "expected": "valid email address" - } - } -} -``` - -## Status Codes - -```typescript -// Success codes -200 OK // GET, PUT, PATCH success -201 Created // POST success with resource creation -204 No Content // DELETE success - -// Client errors -400 Bad Request // Invalid request data -401 Unauthorized // Missing/invalid authentication -403 Forbidden // Valid auth but no permission -404 Not Found // Resource doesn't exist -409 Conflict // Resource conflict (duplicate) -422 Unprocessable // Validation errors - -// Server errors -500 Internal Error // Unexpected server error -502 Bad Gateway // External service error -503 Service Unavailable // Service temporarily down -504 Gateway Timeout // External service timeout -``` - -## DTOs (Data Transfer Objects) - -### Request DTOs - -```typescript -// create.dto.ts -import { IsEmail, IsNotEmpty, IsOptional, MinLength } from 'class-validator'; - -export class CreateUserDto { - @IsEmail() - @IsNotEmpty() - email: string; - - @MinLength(6) - @IsNotEmpty() - password: string; - - @IsOptional() - name?: string; -} - -// update.dto.ts -export class UpdateUserDto { - @IsEmail() - @IsOptional() - email?: string; - - @IsOptional() - name?: string; - - @IsOptional() - avatar?: string; -} - -// query.dto.ts -export class QueryUsersDto { - @IsOptional() - @Type(() => Number) - @Min(1) - page?: number = 1; - - @IsOptional() - @Type(() => Number) - @Min(1) - @Max(100) - limit?: number = 10; - - @IsOptional() - search?: string; - - @IsOptional() - @IsIn(['createdAt', 'name', 'email']) - sortBy?: string = 'createdAt'; - - @IsOptional() - @IsIn(['asc', 'desc']) - order?: 'asc' | 'desc' = 'desc'; -} -``` - -### Response DTOs - -```typescript -// user.response.dto.ts -export class UserResponseDto { - id: string; - email: string; - name: string; - avatar?: string; - role: string; - createdAt: Date; - updatedAt: Date; - - // Hide sensitive data - static fromEntity(user: User): UserResponseDto { - const { password, ...data } = user; - return data; - } -} - -// paginated.response.dto.ts -export class PaginatedResponseDto { - data: T[]; - pagination: { - page: number; - limit: number; - total: number; - totalPages: number; - }; -} -``` - -## Controller Implementation - -```typescript -// user.controller.ts -@Controller('users') -@ApiTags('Users') -export class UserController { - constructor(private readonly userService: UserService) {} - - @Get() - @ApiOperation({ summary: 'List users' }) - @ApiQuery({ type: QueryUsersDto }) - @ApiResponse({ status: 200, type: PaginatedResponseDto }) - async list(@Query() query: QueryUsersDto): Promise { - const { data, total } = await this.userService.findAll(query); - - return { - success: true, - data: data.map(UserResponseDto.fromEntity), - pagination: { - page: query.page, - limit: query.limit, - total, - totalPages: Math.ceil(total / query.limit) - } - }; - } - - @Get(':id') - @ApiOperation({ summary: 'Get user by ID' }) - @ApiParam({ name: 'id', type: 'string' }) - @ApiResponse({ status: 200, type: UserResponseDto }) - @ApiResponse({ status: 404, description: 'User not found' }) - async getById(@Param('id') id: string): Promise { - const user = await this.userService.findById(id); - - if (!user) { - throw new HttpException( - { - success: false, - error: { - code: 'USER_NOT_FOUND', - message: `User with ID ${id} not found` - } - }, - HttpStatus.NOT_FOUND - ); - } - - return { - success: true, - data: UserResponseDto.fromEntity(user) - }; - } - - @Post() - @ApiOperation({ summary: 'Create user' }) - @ApiBody({ type: CreateUserDto }) - @ApiResponse({ status: 201, type: UserResponseDto }) - async create(@Body() dto: CreateUserDto): Promise { - const user = await this.userService.create(dto); - - return { - success: true, - data: UserResponseDto.fromEntity(user) - }; - } - - @Put(':id') - @ApiOperation({ summary: 'Update user' }) - @UseGuards(AuthGuard) - async update( - @Param('id') id: string, - @Body() dto: UpdateUserDto - ): Promise { - const user = await this.userService.update(id, dto); - - return { - success: true, - data: UserResponseDto.fromEntity(user) - }; - } - - @Delete(':id') - @ApiOperation({ summary: 'Delete user' }) - @UseGuards(AuthGuard, RolesGuard) - @Roles('admin') - async delete(@Param('id') id: string): Promise { - await this.userService.delete(id); - - return { - success: true, - data: { deleted: true } - }; - } -} -``` - -## OpenAPI/Swagger Documentation - -```yaml -# openapi/user-service.yaml -openapi: 3.0.0 -info: - title: User Service API - version: 1.0.0 - description: User management endpoints -servers: - - url: https://api.goodgo.com/v1 -paths: - /users: - get: - summary: List users - parameters: - - name: page - in: query - schema: - type: integer - default: 1 - - name: limit - in: query - schema: - type: integer - default: 10 - responses: - '200': - description: List of users - content: - application/json: - schema: - $ref: '#/components/schemas/UserListResponse' - post: - summary: Create user - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CreateUserRequest' - responses: - '201': - description: User created - '400': - description: Validation error -``` - -## Pagination Pattern - -```typescript -// pagination.service.ts -export class PaginationService { - paginate( - query: any, - options: { - page: number; - limit: number; - sortBy?: string; - order?: 'asc' | 'desc'; - } - ) { - const skip = (options.page - 1) * options.limit; - - return { - skip, - take: options.limit, - orderBy: options.sortBy ? { - [options.sortBy]: options.order || 'desc' - } : undefined - }; - } -} -``` - -## Error Handling - -The following diagram illustrates the error handling flow in the API: - -```mermaid -flowchart TD - A[Request Received] --> B{Validation
Middleware} - B -->|Invalid| C[ValidationError] - B -->|Valid| D[Controller] - - D --> E{Business Logic} - E -->|Not Found| F[NotFoundError] - E -->|Unauthorized| G[UnauthorizedError] - E -->|Forbidden| H[ForbiddenError] - E -->|Conflict| I[ConflictError] - E -->|Success| J[Return Success Response] - - E -->|Unexpected Error| K[Generic Error] - - C --> L[Error Handler
Middleware] - F --> L - G --> L - H --> L - I --> L - K --> L - - L --> M{Error Type?} - M -->|ValidationError| N[400 Bad Request
VALIDATION_ERROR] - M -->|UnauthorizedError| O[401 Unauthorized
UNAUTHORIZED] - M -->|ForbiddenError| P[403 Forbidden
FORBIDDEN] - M -->|NotFoundError| Q[404 Not Found
NOT_FOUND] - M -->|ConflictError| R[409 Conflict
CONFLICT] - M -->|Unknown Error| S{Development
Mode?} - - S -->|Yes| T[500 Internal Error
INTERNAL_ERROR
+ Stack Trace] - S -->|No| U[500 Internal Error
INTERNAL_ERROR
Generic Message] - - N --> V[Format Error Response
success: false
error: code, message, details] - O --> V - P --> V - Q --> V - R --> V - T --> V - U --> V - - V --> W[Log Error
Server-Side] - W --> X[Send HTTP Response] - J --> X - - style A fill:#e1f5ff - style J fill:#c8e6c9 - style L fill:#fff9c4 - style V fill:#ffccbc - style X fill:#e1f5ff -``` - -### Error Handler Implementation - -```typescript -// error.middleware.ts -export function errorHandler( - err: Error, - req: Request, - res: Response, - next: NextFunction -) { - const isDev = process.env.NODE_ENV === 'development'; - - // Known errors - if (err instanceof ValidationError) { - return res.status(400).json({ - success: false, - error: { - code: 'VALIDATION_ERROR', - message: err.message, - details: err.errors - } - }); - } - - if (err instanceof UnauthorizedError) { - return res.status(401).json({ - success: false, - error: { - code: 'UNAUTHORIZED', - message: 'Authentication required' - } - }); - } - - // Unknown errors - logger.error('Unhandled error:', err); - - res.status(500).json({ - success: false, - error: { - code: 'INTERNAL_ERROR', - message: isDev ? err.message : 'Internal server error', - stack: isDev ? err.stack : undefined - } - }); -} -``` - -## Best Practices - -1. **Resource Naming** - - Use plural nouns (`/users` not `/user`) - - Use kebab-case for multi-word resources - - Keep URLs as short as possible - -2. **Versioning** - - Include version in URL (`/v1/users`) - - Maintain backward compatibility - - Deprecate old versions gracefully - -3. **Security** - - Always use HTTPS - - Implement rate limiting - - Validate all inputs - - Use proper authentication/authorization - -4. **Performance** - - Implement pagination for lists - - Use field filtering when possible - - Cache responses appropriately - - Compress responses (gzip) - -5. **Documentation** - - Keep OpenAPI spec up to date - - Include examples in documentation - - Document error responses - - Version your documentation \ No newline at end of file diff --git a/docs/en/skills/api-gateway-advanced.md b/docs/en/skills/api-gateway-advanced.md deleted file mode 100644 index c7055a31..00000000 --- a/docs/en/skills/api-gateway-advanced.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -name: api-gateway-advanced -description: Advanced API Gateway patterns for GoodGo microservices including API composition, request/response transformation, service mesh integration, advanced routing, and gateway-level resilience. ---- - -# API Gateway Advanced Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing API composition and aggregation -- Transforming requests/responses at gateway level -- Integrating service mesh with Traefik -- Implementing advanced routing strategies -- Adding gateway-level circuit breakers -- Implementing API versioning at gateway - -## API Gateway Architecture - -The API Gateway serves as the single entry point for all client requests, handling routing, composition, transformation, and resilience patterns. - -```mermaid -graph TB - Client[Client Application] --> Gateway[API Gateway] - - subgraph Gateway["API Gateway Components"] - Router[Request Router] - Auth[Authentication/Authorization] - RateLimit[Rate Limiting] - CircuitBreaker[Circuit Breaker] - Cache[Gateway Cache] - Transformer[Request/Response Transformer] - Composition[API Composition Engine] - end - - Gateway --> Router - Router --> Auth - Auth --> RateLimit - RateLimit --> CircuitBreaker - CircuitBreaker --> Cache - Cache --> Transformer - Transformer --> Composition - - Composition --> Service1[User Service] - Composition --> Service2[Order Service] - Composition --> Service3[Payment Service] - Composition --> Service4[Other Services] - - CircuitBreaker -.-> Service1 - CircuitBreaker -.-> Service2 - CircuitBreaker -.-> Service3 - CircuitBreaker -.-> Service4 - - Cache --> Redis[(Redis Cache)] - - style Gateway fill:#e1f5ff - style Composition fill:#fff4e1 - style CircuitBreaker fill:#ffe1e1 -``` - -## Request Routing Flow - -Requests flow through the gateway middleware chain in a specific order, ensuring proper handling at each stage. - -```mermaid -sequenceDiagram - participant Client - participant Gateway - participant RateLimit - participant Auth - participant CircuitBreaker - participant Cache - participant Transformer - participant Service - - Client->>Gateway: HTTP Request - Gateway->>RateLimit: Check Rate Limit - RateLimit-->>Gateway: Allowed - - Gateway->>Auth: Validate Token - Auth-->>Gateway: Authenticated - - Gateway->>CircuitBreaker: Check Circuit State - alt Circuit Open - CircuitBreaker-->>Gateway: Service Unavailable - Gateway-->>Client: 503 Error - else Circuit Closed/Half-Open - Gateway->>Cache: Check Cache - alt Cache Hit - Cache-->>Gateway: Cached Response - Gateway->>Transformer: Transform Response - Transformer-->>Gateway: Transformed - Gateway-->>Client: Response - else Cache Miss - Gateway->>Transformer: Transform Request - Transformer-->>Gateway: Transformed - Gateway->>Service: Forward Request - Service-->>Gateway: Response - Gateway->>Cache: Store in Cache - Gateway->>Transformer: Transform Response - Transformer-->>Gateway: Transformed - Gateway-->>Client: Response - end - end -``` - -## API Composition Patterns - -API composition enables the gateway to aggregate data from multiple services, reducing client round trips and improving performance. - -### Fan-Out / Fan-In Pattern (Parallel Aggregation) - -```mermaid -graph LR - Client[Client Request] --> Gateway[API Gateway] - - subgraph Gateway["API Composition"] - Comp[Composition Handler] - end - - Gateway --> Comp - - Comp -->|Parallel Calls| S1[User Service] - Comp -->|Parallel Calls| S2[Order Service] - Comp -->|Parallel Calls| S3[Payment Service] - - S1 -->|Response| Comp - S2 -->|Response| Comp - S3 -->|Response| Comp - - Comp -->|Aggregated Response| Client - - style Comp fill:#fff4e1 - style S1 fill:#e1ffe1 - style S2 fill:#e1ffe1 - style S3 fill:#e1ffe1 -``` - -### Chaining Pattern (Sequential Calls with Compensation) - -```mermaid -sequenceDiagram - participant Client - participant Gateway - participant OrderService - participant PaymentService - - Client->>Gateway: Create Order Request - Gateway->>OrderService: POST /orders - OrderService-->>Gateway: Order Created - - Gateway->>PaymentService: POST /payments - alt Payment Success - PaymentService-->>Gateway: Payment Processed - Gateway-->>Client: Success Response - else Payment Failed - PaymentService-->>Gateway: Payment Error - Gateway->>OrderService: DELETE /orders/:id (Compensate) - OrderService-->>Gateway: Order Deleted - Gateway-->>Client: Error Response - end -``` - -## Key Patterns - -### API Composition - -```typescript -// Aggregate multiple service responses -const [user, orders, payments] = await Promise.all([ - userClient.get(`/users/${userId}`), - orderClient.get(`/orders?userId=${userId}`), - paymentClient.get(`/payments?userId=${userId}`), -]); -``` - -### Request/Response Transformation - -```typescript -// Transform at gateway level -transformer.addRule({ - path: '/api/v1/users', - requestTransform: (req) => { - if (!req.query.page) req.query.page = '1'; - return req; - }, - responseTransform: (res, data) => { - return { success: true, data }; - }, -}); -``` - -## Best Practices - -1. Use API composition for aggregating related data -2. Cache at gateway for frequently accessed data -3. Implement circuit breaker at gateway level -4. Keep transformations simple and testable - -## Resources - -- [Traefik Documentation](https://doc.traefik.io/traefik/) -- [Middleware Patterns](./middleware-patterns.md) -- Skill Source: `.cursor/skills/api-gateway-advanced/SKILL.md` diff --git a/docs/en/skills/api-versioning-strategy.md b/docs/en/skills/api-versioning-strategy.md deleted file mode 100644 index efed41cd..00000000 --- a/docs/en/skills/api-versioning-strategy.md +++ /dev/null @@ -1,404 +0,0 @@ ---- -name: api-versioning-strategy -description: API versioning strategies for GoodGo microservices including semantic versioning, backward compatibility patterns, API deprecation, version negotiation, and breaking changes handling. ---- - -# API Versioning Strategy - -## When to Use This Skill - -Use this skill when: -- Versioning APIs -- Handling breaking changes -- Implementing API deprecation -- Maintaining backward compatibility -- Implementing version negotiation -- Managing multiple API versions -- Planning API evolution -- Communicating API changes to consumers - -## Core Concepts - -### Versioning Strategies - -1. **URL Path Versioning**: `/api/v1/users`, `/api/v2/users` -2. **Header Versioning**: `Accept: application/vnd.goodgo.v1+json` -3. **Query Parameter**: `/api/users?version=1` -4. **Semantic Versioning**: Major.Minor.Patch (e.g., 1.2.3) - -### Compatibility Types - -- **Backward Compatible**: New version works with old clients -- **Forward Compatible**: Old version works with new clients -- **Breaking Changes**: Incompatible changes requiring new version - -## Version Negotiation - -Version negotiation allows clients to request a specific API version through headers while maintaining clean URLs. The middleware extracts the version from the `Accept` header and routes to the appropriate handler. - -```mermaid -sequenceDiagram - participant Client - participant Middleware as Version Negotiation
Middleware - participant Controller as Version-Aware
Controller - participant Service - - Client->>Middleware: Request with Accept header
Accept: application/vnd.goodgo.v1+json - Middleware->>Middleware: Extract version from header - alt Version specified - Middleware->>Middleware: Parse version number - alt Version supported - Middleware->>Controller: Set req.apiVersion = 1 - Controller->>Controller: Check version - Controller->>Service: Call service method - Service-->>Controller: Return data - Controller->>Controller: Format response for v1 - Controller-->>Client: v1 response format - else Version not supported - Middleware-->>Client: 400 Unsupported Version - end - else No version specified - Middleware->>Controller: Set req.apiVersion = latest (2) - Controller->>Service: Call service method - Service-->>Controller: Return data - Controller->>Controller: Format response for v2 - Controller-->>Client: v2 response format (default) - end -``` - -### Implementation - -```typescript -// src/middlewares/version-negotiation.middleware.ts -import { Request, Response, NextFunction } from 'express'; -import { logger } from '@goodgo/logger'; - -export function versionNegotiation( - req: Request, - res: Response, - next: NextFunction -): void { - const acceptHeader = req.headers.accept || ''; - const versionMatch = acceptHeader.match(/application\/vnd\.goodgo\.v(\d+)\+json/); - - if (versionMatch) { - const requestedVersion = parseInt(versionMatch[1], 10); - req.apiVersion = requestedVersion; - - const supportedVersions = [1, 2]; - if (!supportedVersions.includes(requestedVersion)) { - return res.status(400).json({ - success: false, - error: { - code: 'UNSUPPORTED_VERSION', - message: `API version ${requestedVersion} is not supported. Supported versions: ${supportedVersions.join(', ')}`, - }, - }); - } - } else { - req.apiVersion = 2; // Default to latest - } - - next(); -} -``` - -## API Deprecation Timeline - -API deprecation follows a structured timeline to give consumers adequate time to migrate. The lifecycle progresses through active, deprecated, sunset, and removed phases. - -```mermaid -gantt - title API Version Lifecycle Timeline - dateFormat YYYY-MM-DD - section Version 1 - Active (v1 only) :active, v1-active, 2024-01-01, 2024-06-01 - Deprecated (v1 + v2) :crit, v1-deprecated, 2024-06-01, 2024-12-31 - Sunset Period :v1-sunset, 2024-12-31, 2025-01-31 - Removed :v1-removed, 2025-01-31, 1d - section Version 2 - Development :v2-dev, 2024-03-01, 2024-06-01 - Active (v1 + v2) :active, v2-active, 2024-06-01, 2025-12-31 -``` - -### Deprecation Phases - -```mermaid -stateDiagram-v2 - [*] --> Active: Version Released - Active --> Deprecated: New Version Released
Add Deprecation Headers - Deprecated --> Sunset: Sunset Date Reached
Stop Accepting New Requests - Sunset --> Removed: Grace Period Ended
Remove Routes - Removed --> [*] - - note right of Active - - Version fully supported - - No warnings - - All features available - end note - - note right of Deprecated - - Deprecation header set - - Warning headers added - - Migration guide provided - - Still functional - end note - - note right of Sunset - - Read-only mode - - No new requests accepted - - Existing requests honored - end note -``` - -### Deprecation Headers - -```typescript -// src/middlewares/deprecation.middleware.ts -export function deprecationMiddleware(version: string, sunsetDate: string) { - return (req: Request, res: Response, next: NextFunction): void => { - if (req.apiVersion && parseInt(req.apiVersion.toString()) < parseInt(version)) { - res.setHeader('Deprecation', 'true'); - res.setHeader('Sunset', sunsetDate); - res.setHeader('Link', `<${req.url.replace(/\/v\d+/, `/v${version}`)}>; rel="successor-version"`); - res.setHeader('Warning', `299 - "API version ${req.apiVersion} is deprecated. Please migrate to version ${version} by ${sunsetDate}"`); - } - - next(); - }; -} -``` - -## Migration Flow - -Breaking changes require a careful 3-phase migration strategy to ensure zero downtime and smooth client transitions. - -```mermaid -flowchart TD - Start([Breaking Change Identified]) --> Phase1[Phase 1: Support Both Versions] - - Phase1 --> DeployV2[Deploy v2 alongside v1] - DeployV2 --> Monitor1[Monitor v1 and v2 usage] - Monitor1 --> Wait1[Wait for client adoption] - Wait1 --> Phase2{Sufficient
v2 adoption?} - - Phase2 -->|No| Wait1 - Phase2 -->|Yes| Phase2Start[Phase 2: Deprecate v1] - - Phase2Start --> AddHeaders[Add deprecation headers to v1] - AddHeaders --> NotifyClients[Notify clients via
deprecation warnings] - NotifyClients --> ProvideGuide[Provide migration guide] - ProvideGuide --> Monitor2[Monitor migration progress] - Monitor2 --> Wait2[Wait until sunset date] - Wait2 --> Phase3{Sunset date
reached?} - - Phase3 -->|No| Monitor2 - Phase3 -->|Yes| Phase3Start[Phase 3: Remove v1] - - Phase3Start --> StopAccepting[Stop accepting new v1 requests] - StopAccepting --> GracePeriod[Grace period for
existing requests] - GracePeriod --> RemoveRoutes[Remove v1 routes] - RemoveRoutes --> End([Migration Complete]) - - style Phase1 fill:#e1f5ff - style Phase2Start fill:#fff4e1 - style Phase3Start fill:#ffe1e1 - style End fill:#e1ffe1 -``` - -### Implementation Strategy - -```typescript -// src/core/api/migration.strategy.ts -export class MigrationStrategy { - /** - * Phase 1: Support both versions - */ - phase1SupportBoth(): void { - router.use('/v1', v1Router); - router.use('/v2', v2Router); - } - - /** - * Phase 2: Deprecate v1 - */ - phase2DeprecateV1(): void { - router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router); - router.use('/v2', v2Router); - } - - /** - * Phase 3: Remove v1 - */ - phase3RemoveV1(): void { - router.use('/v2', v2Router); - // v1 routes removed after sunset date - } -} -``` - -## URL Path Versioning - -### Implementation - -```typescript -// src/routes/index.ts -import { Router } from 'express'; -import v1Router from './v1'; -import v2Router from './v2'; - -const router = Router(); - -router.use('/v1', v1Router); -router.use('/v2', v2Router); - -export default router; -``` - -## Semantic Versioning - -### Version Structure - -``` -MAJOR.MINOR.PATCH - -MAJOR: Breaking changes -MINOR: Backward-compatible additions -PATCH: Backward-compatible bug fixes -``` - -### Version Response - -```typescript -// src/core/api/version.middleware.ts -export function versionMiddleware(req: Request, res: Response, next: NextFunction): void { - const originalJson = res.json.bind(res); - - res.json = (data: any) => { - const response = { - ...data, - metadata: { - ...data.metadata, - apiVersion: req.apiVersion || '2.0.0', - serviceVersion: process.env.SERVICE_VERSION || '1.0.0', - }, - }; - - return originalJson(response); - }; - - next(); -} -``` - -## Backward Compatibility - -### Compatibility Layer - -```typescript -// src/core/api/compatibility.adapter.ts -export class CompatibilityAdapter { - adaptV1ToV2(v1Data: any): any { - return { - success: true, - data: { - user: { - ...v1Data, - profile: null, // Add default for new field - }, - }, - metadata: { - version: '2.0.0', - adapted: true, - }, - }; - } - - adaptV2RequestToV1(v2Request: any): any { - return { - email: v2Request.email, - name: v2Request.name, - // Ignore new fields - }; - } -} -``` - -## Best Practices - -1. **Versioning Strategy**: Choose URL path or header, be consistent -2. **Semantic Versioning**: Use MAJOR.MINOR.PATCH -3. **Deprecation**: Always deprecate before removing -4. **Migration Guide**: Provide clear migration documentation -5. **Backward Compatibility**: Maintain compatibility when possible -6. **Communication**: Clearly communicate version changes - -## Common Mistakes - -1. **No Deprecation Period**: Breaking clients suddenly - ```typescript - // ❌ BAD: Remove v1 immediately - router.use('/v2', v2Router); - - // ✅ GOOD: Deprecate with sunset date - router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router); - router.use('/v2', v2Router); - ``` - -2. **Breaking Changes Without Major Version**: Client confusion - ``` - # ❌ BAD: Breaking change in minor version - v1.1.0 → Changed response format - - # ✅ GOOD: Breaking change = new major version - v1.x.x → v2.0.0 (new response format) - ``` - -3. **Inconsistent Versioning Strategy**: Mixed approaches - ```typescript - // ❌ BAD: Mix URL and header versioning - /api/v1/users + Accept: application/vnd.v2+json - - // ✅ GOOD: Choose one approach - /api/v1/users OR Accept: application/vnd.goodgo.v1+json - ``` - -## Quick Reference - -| Strategy | Pros | Cons | Use When | -|----------|------|------|----------| -| **URL Path** | Clear, cacheable | URL changes | Public APIs | -| **Header** | Clean URLs | Less visible | Internal APIs | -| **Query Param** | Simple | Not RESTful | Quick prototypes | - -**Semantic Versioning:** -``` -MAJOR.MINOR.PATCH - │ │ └── Bug fixes (backward compatible) - │ └──────── New features (backward compatible) - └────────────── Breaking changes -``` - -**Version Lifecycle:** -``` -v1 Active → v2 Released → v1 Deprecated → v1 Sunset → v1 Removed - │ │ │ │ │ - │ │ Add headers Remove from Delete - │ Support + warnings docs routes - Solo both -``` - -**Deprecation Headers:** -```http -Deprecation: true -Sunset: Sat, 31 Dec 2024 23:59:59 GMT -Warning: 299 - "API v1 is deprecated. Migrate to v2 by 2024-12-31" -Link: ; rel="successor-version" -``` - -## Resources - -- [API Design](./api-design.md) - API design patterns -- [Middleware Patterns](./middleware-patterns.md) - Middleware patterns -- [Project Rules](./project-rules.md) - GoodGo standards -- Skill Source: `.cursor/skills/api-versioning-strategy/SKILL.md` diff --git a/docs/en/skills/caching-patterns.md b/docs/en/skills/caching-patterns.md deleted file mode 100644 index 8266cb40..00000000 --- a/docs/en/skills/caching-patterns.md +++ /dev/null @@ -1,369 +0,0 @@ ---- -name: caching-patterns -description: Caching strategies and patterns for GoodGo microservices including multi-layer cache, Redis caching, cache key naming, TTL strategies, cache invalidation, and cache-aside patterns. ---- - -# Caching Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing caching for frequently accessed data -- Optimizing database queries with caching -- Designing cache key naming conventions -- Setting TTL (Time To Live) strategies -- Implementing cache invalidation patterns -- Using multi-layer cache (L1: Memory, L2: Redis) -- Handling cache failures gracefully - -## Core Concepts - -### Multi-Layer Cache Strategy - -The platform uses a two-layer cache architecture to balance speed and capacity: - -```mermaid -graph TB - subgraph Application["Application Layer"] - App[Application Code] - end - - subgraph L1Layer["L1 Cache - Memory (NodeCache)"] - L1[In-Memory Cache] - L1Props["• Speed: < 1ms
• Capacity: 10k keys
• TTL: 60s-5min
• Scope: Per-instance"] - end - - subgraph L2Layer["L2 Cache - Redis (Distributed)"] - L2[Redis Cache] - L2Props["• Speed: < 5ms
• Capacity: Large
• TTL: Configurable
• Scope: Shared"] - end - - subgraph DataLayer["Data Source"] - DB[(Database)] - API[External API] - end - - App -->|Check First| L1 - L1 -->|Miss| L2 - L2 -->|Miss| DB - L2 -->|Miss| API - DB -->|Store| L2 - API -->|Store| L2 - L2 -->|Warm| L1 - - L1 -.-> L1Props - L2 -.-> L2Props - - style L1 fill:#e1f5ff - style L2 fill:#fff4e1 - style DB fill:#ffe1e1 - style API fill:#ffe1e1 -``` - -**Layer Characteristics:** - -1. **L1 Cache (Memory)**: NodeCache in-memory cache - - Very fast (< 1ms access time) - - Limited capacity (10k keys default) - - Short TTL (60 seconds default, max 5 minutes) - - Per-instance (not shared across instances) - -2. **L2 Cache (Redis)**: Distributed Redis cache - - Fast (< 5ms access time) - - Large capacity - - Longer TTL (configurable) - - Shared across all service instances - -### Cache Flow - -The cache lookup follows a multi-layer approach, checking L1 first, then L2, and finally the data source. - -```mermaid -flowchart TD - Start([Request Data]) --> CheckL1{Check L1 Cache
Memory} - CheckL1 -->|Hit| ReturnL1[Return Data
from L1] - CheckL1 -->|Miss| CheckL2{Check L2 Cache
Redis} - CheckL2 -->|Hit| StoreL1[Store in L1
Warm Cache] - StoreL1 --> ReturnL2[Return Data
from L2] - CheckL2 -->|Miss| FetchSource[Fetch from
Data Source] - FetchSource --> StoreBoth[Store in L1 & L2] - StoreBoth --> ReturnSource[Return Data
from Source] - - ReturnL1 --> End([End]) - ReturnL2 --> End - ReturnSource --> End - - style CheckL1 fill:#e1f5ff - style CheckL2 fill:#fff4e1 - style FetchSource fill:#ffe1e1 - style ReturnL1 fill:#e1ffe1 - style ReturnL2 fill:#e1ffe1 - style ReturnSource fill:#e1ffe1 -``` - -## Patterns - -### Cache Service Usage - -```typescript -import { cacheService } from '../core/cache'; - -// Simple get/set -const cached = await cacheService.get('user:123'); -await cacheService.set('user:123', userData, 300); // 5 minutes TTL - -// Get or set pattern (cache-aside) -const user = await cacheService.getOrSet( - 'user:123', - async () => { - return await userRepository.findById('123'); - }, - 300 // TTL in seconds -); -``` - -### Cache Key Naming Conventions - -Use consistent naming patterns: - -```typescript -// Pattern: {entity}:{identifier} -'user:123' -'user:email:user@example.com' -'user:123:permissions' -'user:123:roles' - -// Pattern: {entity}:{identifier}:{sub-resource} -'session:abc123' -'permission:perm_123' -'role:role_123' -``` - -Cache service provides key generators: - -```typescript -cacheService.keys = { - user: (userId: string) => `user:${userId}`, - userPermissions: (userId: string) => `user:${userId}:permissions`, - userRoles: (userId: string) => `user:${userId}:roles`, - token: (token: string) => `token:${token}`, - session: (sessionId: string) => `session:${sessionId}`, -}; -``` - -### Cache-Aside Pattern - -Most common pattern - check cache first, fetch if miss: - -```typescript -async getUserPermissions(userId: string): Promise { - const cacheKey = cacheService.keys.userPermissions(userId); - - // Try cache first - const cached = await cacheService.get(cacheKey); - if (cached) { - return cached; - } - - // Cache miss - fetch from source - const permissions = await calculatePermissions(userId); - - // Store in cache - await cacheService.set(cacheKey, permissions, 300); // 5 min TTL - - return permissions; -} -``` - -### Get or Set Pattern - -Simplified cache-aside pattern: - -```typescript -const permissions = await cacheService.getOrSet( - cacheService.keys.userPermissions(userId), - async () => { - // This function only runs on cache miss - return await calculatePermissions(userId); - }, - 300 // TTL -); -``` - -### TTL Strategies - -Choose TTL based on data characteristics: - -**Short TTL (60-300s)**: Frequently changing data -- User permissions (300s) -- Session data (varies) -- Real-time statistics - -**Medium TTL (300-1800s)**: Moderately changing data -- User profiles (600s) -- Organization data (900s) -- Configuration (1800s) - -**Long TTL (1800-3600s)**: Rarely changing data -- Static configurations (3600s) -- Reference data (7200s) - -**No TTL**: Very stable data (use with caution) -- Rarely use - prefer long TTL instead - -### Cache Invalidation - -Invalidate cache when data changes to prevent serving stale data. The platform supports multiple invalidation strategies: - -```mermaid -flowchart TD - Start([Data Changed]) --> ChooseStrategy{Choose
Invalidation
Strategy} - - ChooseStrategy -->|Single Key| SingleKey[Single Key
Invalidation] - SingleKey --> DelL1[Delete from L1] - DelL1 --> DelL2[Delete from L2] - DelL2 --> Done1([Complete]) - - ChooseStrategy -->|Pattern Match| PatternMatch[Pattern-Based
Invalidation] - PatternMatch --> FindKeys[Find Matching Keys
user:123:*] - FindKeys --> DelManyL1[Delete from L1
All Matching] - DelManyL1 --> DelManyL2[Delete from L2
All Matching] - DelManyL2 --> Done2([Complete]) - - ChooseStrategy -->|Multiple Keys| MultipleKeys[Multiple Keys
Invalidation] - MultipleKeys --> ListKeys[List Keys to Delete
user:123
user:123:permissions
user:123:roles] - ListKeys --> BatchDelL1[Batch Delete from L1] - BatchDelL1 --> BatchDelL2[Batch Delete from L2] - BatchDelL2 --> Done3([Complete]) - - style SingleKey fill:#e1f5ff - style PatternMatch fill:#fff4e1 - style MultipleKeys fill:#ffe1e1 - style Done1 fill:#e1ffe1 - style Done2 fill:#e1ffe1 - style Done3 fill:#e1ffe1 -``` - -**Implementation Examples:** - -```typescript -// Single key invalidation -await cacheService.del(cacheService.keys.user(userId)); -await cacheService.del(cacheService.keys.userPermissions(userId)); - -// Pattern-based invalidation -await cacheService.invalidatePattern('user:123:*'); - -// Multiple keys -await cacheService.delMany([ - cacheService.keys.user(userId), - cacheService.keys.userPermissions(userId), - cacheService.keys.userRoles(userId), -]); -``` - -### Cache Warming - -Pre-populate cache with frequently accessed data: - -```typescript -async warmCache() { - const activeUsers = await userRepository.findActiveUsers(); - - for (const user of activeUsers) { - // Pre-cache user data - await cacheService.set( - cacheService.keys.user(user.id), - user, - 600 - ); - - // Pre-cache permissions - const permissions = await calculatePermissions(user.id); - await cacheService.set( - cacheService.keys.userPermissions(user.id), - permissions, - 300 - ); - } -} -``` - -### Error Handling - -Cache failures should not break the application: - -```typescript -async getWithCache(key: string): Promise { - try { - // Try cache first - const cached = await cacheService.get(key); - if (cached) return cached; - } catch (error) { - // Log but continue - fallback to source - logger.warn('Cache get failed, falling back to source', { key, error }); - } - - // Fallback to data source - return await fetchFromSource(); -} -``` - -## Best Practices - -1. **Cache Keys**: Use consistent naming conventions -2. **TTL Selection**: Choose TTL based on data change frequency -3. **Invalidation**: Invalidate cache when data changes -4. **Error Handling**: Don't let cache failures break the app -5. **Cache-Aside**: Use cache-aside pattern for most cases -6. **Avoid Over-Caching**: Don't cache data that changes too frequently -7. **Monitor Hit Rates**: Track cache hit rates to optimize TTL -8. **Warm Cache**: Pre-populate cache for critical data -9. **Use Multi-Layer**: Leverage both L1 and L2 cache -10. **Serialize Properly**: Ensure data is JSON serializable - -## Common Mistakes - -1. **Cache Key Collisions**: Using generic keys that collide -2. **Stale Data**: Not invalidating cache when data changes -3. **Too Short TTL**: Setting TTL too short, negating cache benefits -4. **Too Long TTL**: Setting TTL too long, serving stale data -5. **No Error Handling**: Letting cache errors break the application -6. **Caching Everything**: Caching data that doesn't benefit from caching -7. **Not Warming Cache**: Not pre-populating critical cache data -8. **Ignoring Hit Rates**: Not monitoring cache performance - -## Troubleshooting - -### Low Cache Hit Rate - -**Problem**: Cache hit rate is low, cache not effective -**Solution**: -- Review TTL values - may be too short -- Check cache key patterns - ensure consistent usage -- Verify cache invalidation isn't too aggressive -- Monitor what data is being cached - -### Stale Data Issues - -**Problem**: Serving stale data from cache -**Solution**: -- Review TTL values - may be too long -- Ensure cache invalidation on data updates -- Use shorter TTL for frequently changing data -- Implement cache versioning if needed - -### Cache Performance Issues - -**Problem**: Cache operations are slow -**Solution**: -- Check Redis connection and network latency -- Monitor Redis memory usage -- Review cache key patterns for efficiency -- Consider L1 cache hit rate (should be high) - -## Resources - -- [Multi-Layer Cache](../../services/iam-service/src/core/cache/multi-layer-cache.ts) - Multi-layer cache implementation -- [Cache Service](../../services/iam-service/src/core/cache/cache.service.ts) - Cache service wrapper -- [Cache Usage Example](../../services/iam-service/src/modules/rbac/rbac.service.ts) - Real-world cache usage diff --git a/docs/en/skills/cicd-advanced-patterns.md b/docs/en/skills/cicd-advanced-patterns.md deleted file mode 100644 index d7b95e2b..00000000 --- a/docs/en/skills/cicd-advanced-patterns.md +++ /dev/null @@ -1,544 +0,0 @@ ---- -name: cicd-advanced-patterns -description: Advanced CI/CD patterns for GoodGo microservices including blue-green deployments, canary releases, automated rollback, deployment verification, and progressive delivery. ---- - -# CI/CD Advanced Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing blue-green deployments -- Setting up canary releases -- Implementing automated rollback mechanisms -- Creating deployment verification pipelines -- Implementing progressive delivery -- Setting up deployment gates -- Implementing smoke tests -- Managing deployment strategies in Kubernetes - -## Core Concepts - -### Deployment Strategies - -1. **Rolling Update**: Gradual replacement (default K8s) -2. **Blue-Green**: Two identical environments, switch traffic -3. **Canary**: Gradual rollout to subset of users -4. **Recreate**: Stop old, start new (downtime) - -### Deployment Verification - -- Smoke tests -- Health checks -- Performance tests -- Rollback triggers - -## Blue-Green Deployment - -Blue-green deployment maintains two identical production environments (blue and green). At any time, only one environment serves live traffic. The new version is deployed to the idle environment, verified, and then traffic is switched. - -```mermaid -flowchart TD - Start([Deployment Triggered]) --> DeployGreen[Deploy to Green Environment] - DeployGreen --> WaitRollout[Wait for Rollout Complete] - WaitRollout --> RunSmokeTests[Run Smoke Tests] - RunSmokeTests --> TestsPassed{Tests Passed?} - - TestsPassed -->|Yes| SwitchTraffic[Switch Service Selector to Green] - TestsPassed -->|No| RollbackToBlue[Rollback: Keep Blue Active] - - SwitchTraffic --> MonitorHealth[Monitor Health Metrics] - MonitorHealth --> HealthOK{Health OK?} - - HealthOK -->|Yes| Complete([Deployment Complete]) - HealthOK -->|No| AutoRollback[Auto Rollback to Blue] - - AutoRollback --> Complete - RollbackToBlue --> Fail([Deployment Failed]) - - style Start fill:#e1f5ff - style Complete fill:#d4edda - style Fail fill:#f8d7da - style TestsPassed fill:#fff3cd - style HealthOK fill:#fff3cd -``` - -### Kubernetes Implementation - -```yaml -# deployments/production/kubernetes/user-service-blue.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-service-blue - labels: - app: user-service - version: blue -spec: - replicas: 3 - selector: - matchLabels: - app: user-service - version: blue - template: - metadata: - labels: - app: user-service - version: blue - spec: - containers: - - name: user-service - image: goodgo/user-service:v1.0.0 - ports: - - containerPort: 5000 - ---- -# deployments/production/kubernetes/user-service-green.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-service-green - labels: - app: user-service - version: green -spec: - replicas: 3 - selector: - matchLabels: - app: user-service - version: green - template: - metadata: - labels: - app: user-service - version: green - spec: - containers: - - name: user-service - image: goodgo/user-service:v1.1.0 - ports: - - containerPort: 5000 - ---- -# Service selector switches between blue/green -apiVersion: v1 -kind: Service -metadata: - name: user-service -spec: - selector: - app: user-service - version: blue # Switch to green after verification - ports: - - port: 80 - targetPort: 5000 -``` - -## Canary Deployment - -Canary deployment gradually rolls out changes to a small subset of users before making them available to everyone. This allows for real-world testing with minimal risk. - -```mermaid -flowchart TD - Start([Canary Deployment Started]) --> DeployCanary[Deploy Canary Version
1 Replica] - DeployCanary --> Route10[Route 10% Traffic to Canary] - Route10 --> Wait10[Wait 5-10 minutes] - Wait10 --> Check10{Health & Metrics OK?} - - Check10 -->|No| RollbackCanary[Rollback: Route 0% to Canary] - Check10 -->|Yes| Route25[Route 25% Traffic to Canary] - - Route25 --> Wait25[Wait 5-10 minutes] - Wait25 --> Check25{Health & Metrics OK?} - - Check25 -->|No| RollbackCanary - Check25 -->|Yes| Route50[Route 50% Traffic to Canary] - - Route50 --> Wait50[Wait 5-10 minutes] - Wait50 --> Check50{Health & Metrics OK?} - - Check50 -->|No| RollbackCanary - Check50 -->|Yes| Route75[Route 75% Traffic to Canary] - - Route75 --> Wait75[Wait 5-10 minutes] - Wait75 --> Check75{Health & Metrics OK?} - - Check75 -->|No| RollbackCanary - Check75 -->|Yes| Route100[Route 100% Traffic to Canary] - - Route100 --> PromoteCanary[Promote Canary to Stable] - PromoteCanary --> Complete([Canary Complete]) - - RollbackCanary --> Fail([Canary Failed]) - - style Start fill:#e1f5ff - style Complete fill:#d4edda - style Fail fill:#f8d7da - style Check10 fill:#fff3cd - style Check25 fill:#fff3cd - style Check50 fill:#fff3cd - style Check75 fill:#fff3cd -``` - -### Kubernetes Canary with Service Mesh - -```yaml -# deployments/production/kubernetes/user-service-canary.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-service-canary - labels: - app: user-service - version: canary -spec: - replicas: 1 # Start with 1 replica (10% traffic) - selector: - matchLabels: - app: user-service - version: canary - template: - metadata: - labels: - app: user-service - version: canary - spec: - containers: - - name: user-service - image: goodgo/user-service:v1.1.0 - ---- -# VirtualService splits traffic -apiVersion: networking.istio.io/v1alpha3 -kind: VirtualService -metadata: - name: user-service -spec: - hosts: - - user-service - http: - - match: - - headers: - canary: - exact: "true" - route: - - destination: - host: user-service - subset: canary - weight: 100 - - route: - - destination: - host: user-service - subset: stable - weight: 90 - - destination: - host: user-service - subset: canary - weight: 10 # 10% traffic to canary -``` - -## Automated Rollback - -Automated rollback mechanisms detect deployment failures and automatically revert to the previous stable version, minimizing downtime and impact. - -```mermaid -flowchart TD - Start([Deployment Completed]) --> RunSmokeTests[Run Smoke Tests] - RunSmokeTests --> SmokePassed{Smoke Tests Pass?} - - SmokePassed -->|No| GetPreviousRev[Get Previous Revision] - GetPreviousRev --> RollbackDeploy[Rollback Deployment] - RollbackDeploy --> VerifyRollback[Verify Rollback Success] - VerifyRollback --> RollbackComplete([Rollback Complete]) - - SmokePassed -->|Yes| MonitorHealth[Monitor Health Metrics] - MonitorHealth --> HealthOK{Health OK?} - - HealthOK -->|Yes| MonitorErrors[Monitor Error Rates] - HealthOK -->|No| GetPreviousRev - - MonitorErrors --> ErrorRateOK{Error Rate < Threshold?} - - ErrorRateOK -->|Yes| MonitorPerformance[Monitor Performance] - ErrorRateOK -->|No| GetPreviousRev - - MonitorPerformance --> PerfOK{Performance OK?} - - PerfOK -->|Yes| DeploymentSuccess([Deployment Successful]) - PerfOK -->|No| GetPreviousRev - - style Start fill:#e1f5ff - style DeploymentSuccess fill:#d4edda - style RollbackComplete fill:#f8d7da - style SmokePassed fill:#fff3cd - style HealthOK fill:#fff3cd - style ErrorRateOK fill:#fff3cd - style PerfOK fill:#fff3cd -``` - -### Rollback Script - -```bash -#!/bin/bash -# scripts/deployment/rollback.sh -# Automated rollback to previous version - -SERVICE_NAME=$1 -NAMESPACE=${2:-production} - -# Get previous deployment revision -PREVIOUS_REVISION=$(kubectl rollout history deployment/$SERVICE_NAME -n $NAMESPACE --no-headers | tail -1 | awk '{print $1}') - -if [ -z "$PREVIOUS_REVISION" ]; then - echo "No previous revision found" - exit 1 -fi - -echo "Rolling back to revision $PREVIOUS_REVISION" - -# Rollback deployment -kubectl rollout undo deployment/$SERVICE_NAME -n $NAMESPACE --to-revision=$PREVIOUS_REVISION - -# Wait for rollout -kubectl rollout status deployment/$SERVICE_NAME -n $NAMESPACE - -echo "Rollback complete" -``` - -### Automated Rollback on Failure - -```yaml -# .github/workflows/deploy-production.yml -name: Deploy Production - -on: - push: - branches: [main] - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Deploy to Kubernetes - run: | - kubectl apply -f deployments/production/kubernetes/ - kubectl rollout status deployment/user-service - - - name: Run Smoke Tests - run: ./scripts/deployment/smoke-tests.sh user-service - - - name: Rollback on Failure - if: failure() - run: ./scripts/deployment/rollback.sh user-service production -``` - -## Deployment Verification - -### Smoke Tests - -```typescript -// scripts/deployment/smoke-tests.ts -// Smoke tests for deployment verification -import axios from 'axios'; - -const SERVICE_URL = process.env.SERVICE_URL || 'http://localhost'; - -async function runSmokeTests(): Promise { - try { - // Health check - const healthResponse = await axios.get(`${SERVICE_URL}/health`); - if (healthResponse.status !== 200) { - console.error('Health check failed'); - return false; - } - - // Basic functionality test - const testResponse = await axios.get(`${SERVICE_URL}/api/v1/users`, { - timeout: 5000, - }); - - if (testResponse.status !== 200) { - console.error('Functionality test failed'); - return false; - } - - console.log('Smoke tests passed'); - return true; - } catch (error) { - console.error('Smoke tests failed', error); - return false; - } -} - -runSmokeTests().then((success) => { - process.exit(success ? 0 : 1); -}); -``` - -### Health Check Script - -```bash -#!/bin/bash -# scripts/deployment/health-checks.sh -# Comprehensive health checks - -SERVICE_NAME=$1 -NAMESPACE=${2:-production} - -echo "Running health checks for $SERVICE_NAME" - -# Check pods are ready -READY_PODS=$(kubectl get pods -n $NAMESPACE -l app=$SERVICE_NAME --field-selector=status.phase=Running --no-headers | wc -l) - -if [ $READY_PODS -eq 0 ]; then - echo "No ready pods found" - exit 1 -fi - -# Check service endpoints -ENDPOINTS=$(kubectl get endpoints $SERVICE_NAME -n $NAMESPACE -o jsonpath='{.subsets[0].addresses[*].ip}' | wc -w) - -if [ $ENDPOINTS -eq 0 ]; then - echo "No service endpoints found" - exit 1 -fi - -# Check health endpoint -SERVICE_URL=$(kubectl get service $SERVICE_NAME -n $NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') - -if [ -z "$SERVICE_URL" ]; then - SERVICE_URL="http://$SERVICE_NAME.$NAMESPACE.svc.cluster.local" -fi - -HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $SERVICE_URL/health) - -if [ $HTTP_CODE -ne 200 ]; then - echo "Health endpoint returned $HTTP_CODE" - exit 1 -fi - -echo "Health checks passed" -``` - -## Deployment Gates - -Deployment gates add checkpoints in the CI/CD pipeline that must pass before proceeding to the next stage. - -```yaml -# .github/workflows/deploy-with-gates.yml -name: Deploy with Gates - -jobs: - deploy: - steps: - - name: Deploy - run: kubectl apply -f deployments/ - - - name: Wait for Rollout - run: kubectl rollout status deployment/service - - - name: Smoke Tests Gate - id: smoke-tests - run: ./scripts/deployment/smoke-tests.sh - - - name: Performance Tests Gate - if: steps.smoke-tests.outcome == 'success' - run: ./scripts/deployment/performance-tests.sh - - - name: Manual Approval Gate - if: steps.smoke-tests.outcome == 'success' - uses: trstringer/manual-approval@v1 - with: - secret: ${{ secrets.GITHUB_TOKEN }} - approvers: team-leads - minimum-approvals: 1 - issue-title: "Approve deployment" -``` - -## Best Practices - -1. **Blue-Green**: Use for zero-downtime deployments -2. **Canary**: Use for gradual rollouts with monitoring -3. **Automated Rollback**: Always have rollback plan -4. **Smoke Tests**: Run immediately after deployment -5. **Health Checks**: Monitor health continuously -6. **Gates**: Use deployment gates for critical deployments - -## Common Mistakes - -1. **No Rollback Plan**: Can't recover from failed deployment - ```yaml - # ✅ Always have rollback command ready - kubectl rollout undo deployment/service - ``` - -2. **Skipping Smoke Tests**: Catching issues too late - ```yaml - # ✅ Run smoke tests immediately after deploy - - name: Smoke Tests - run: ./scripts/smoke-tests.sh - ``` - -3. **100% Traffic Switch**: All-or-nothing failures - ```yaml - # ❌ BAD: Immediate full switch - # ✅ GOOD: Gradual rollout (10% → 50% → 100%) - ``` - -4. **No Health Monitoring**: Missing deployment issues - ```yaml - # ✅ Monitor health after deployment - - name: Monitor Health - run: kubectl rollout status deployment/service --timeout=5m - ``` - -## Quick Reference - -| Strategy | Risk | Downtime | Resource Cost | -|----------|------|----------|---------------| -| **Blue-Green** | Low | Zero | 2x (temporary) | -| **Canary** | Low | Zero | +10-20% | -| **Rolling** | Medium | Zero | 1x | -| **Recreate** | High | Yes | 1x | - -**Deployment Commands:** -```bash -# Apply deployment -kubectl apply -f kubernetes/ - -# Check rollout status -kubectl rollout status deployment/service - -# Rollback -kubectl rollout undo deployment/service - -# Canary traffic split (Istio) -kubectl apply -f virtualservice-canary.yaml -``` - -**GitHub Actions Triggers:** -```yaml -on: - push: - branches: [main] # Deploy to prod - tags: ['v*'] # Release - pull_request: - branches: [main] # PR checks -``` - -**Deployment Gates:** -``` -Build → Test → Security Scan → Deploy Staging -→ Smoke Tests → Manual Approval → Deploy Prod -``` - -## Resources - -- [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) -- [Istio Traffic Management](https://istio.io/latest/docs/tasks/traffic-management/) -- [Deployment Kubernetes](./deployment-kubernetes.md) - K8s deployment patterns -- [Testing Patterns](./testing-patterns.md) - Testing strategies -- [Project Rules](./project-rules.md) - GoodGo coding standards -- Skill Source: `.cursor/skills/cicd-advanced-patterns/SKILL.md` diff --git a/docs/en/skills/comment-code.md b/docs/en/skills/comment-code.md deleted file mode 100644 index b760170e..00000000 --- a/docs/en/skills/comment-code.md +++ /dev/null @@ -1,489 +0,0 @@ ---- -name: comment-code -description: Add bilingual code comments in Vietnamese and English for better documentation. Use when adding comments to code, documenting functions/classes, or when user requests Vietnamese/English documentation. ---- - -# Bilingual Code Comments - -Add comprehensive code comments in both Vietnamese and English to improve code readability for international and Vietnamese teams. - -## When to Use - -- Adding comments to new code -- Documenting existing code -- Creating JSDoc/TSDoc documentation -- Writing function/class descriptions -- Explaining complex logic -- Adding inline comments - -## Comment Structure - -The following diagram illustrates the structure and hierarchy of comment types used in the GoodGo codebase: - -```mermaid -graph TB - subgraph CommentTypes["Comment Types"] - SingleLine["Single-line Comments
// EN: ...
// VI: ..."] - MultiLine["Multi-line Comments
/* EN: ...
VI: ... */"] - JSDoc["JSDoc Comments
/** EN: ...
VI: ... */"] - Prisma["Prisma Comments
/// EN: ...
VI: ..."] - end - - subgraph Contexts["Code Contexts"] - Functions["Functions"] - Classes["Classes"] - Interfaces["Interfaces/Types"] - Components["React Components"] - Controllers["API Controllers"] - Middleware["Middleware"] - Schema["Prisma Schema"] - Config["Configuration"] - end - - subgraph SpecialTypes["Special Comment Types"] - TODO["TODO Comments"] - FIXME["FIXME Comments"] - WARNING["WARNING Comments"] - NOTE["NOTE Comments"] - end - - subgraph Format["Bilingual Format"] - EN["English (EN)
First"] - VI["Vietnamese (VI)
Second"] - end - - JSDoc --> Functions - JSDoc --> Classes - JSDoc --> Interfaces - JSDoc --> Components - JSDoc --> Controllers - JSDoc --> Middleware - - SingleLine --> Config - SingleLine --> SpecialTypes - - MultiLine --> Functions - MultiLine --> Classes - - Prisma --> Schema - - Format --> CommentTypes - Format --> Contexts - Format --> SpecialTypes - - style CommentTypes fill:#e1f5ff - style Contexts fill:#fff4e1 - style SpecialTypes fill:#ffe1f5 - style Format fill:#e1ffe1 -``` - -## Documentation Flow - -The following diagram shows the decision flow for adding comments to code: - -```mermaid -flowchart TD - Start([Start: Writing Code]) --> CheckType{What type of
code element?} - - CheckType -->|Public API| HighPriority[High Priority:
Always Document] - CheckType -->|Complex Logic| HighPriority - CheckType -->|Security Code| HighPriority - CheckType -->|Config/Setup| HighPriority - CheckType -->|Error Handling| HighPriority - - CheckType -->|Helper Function| MediumPriority[Medium Priority:
Document if Helpful] - CheckType -->|Data Transform| MediumPriority - CheckType -->|External Integration| MediumPriority - - CheckType -->|Simple Getter/Setter| LowPriority[Low Priority:
Optional] - CheckType -->|Self-explanatory| LowPriority - CheckType -->|Standard CRUD| LowPriority - - HighPriority --> ChooseFormat{Choose Comment
Format} - MediumPriority --> ChooseFormat - LowPriority --> ChooseFormat - - ChooseFormat -->|Function/Class| UseJSDoc[Use JSDoc Format
/** EN: ...
VI: ... */] - ChooseFormat -->|Brief Explanation| UseSingleLine[Use Single-line
// EN: ...
// VI: ...] - ChooseFormat -->|Multi-step Process| UseMultiLine[Use Multi-line
/* EN: ...
VI: ... */] - ChooseFormat -->|Prisma Schema| UsePrisma[Use Prisma Format
/// EN: ...
VI: ...] - - UseJSDoc --> AddParams[Add @param tags
Add @returns tag
Add @throws if needed] - UseSingleLine --> WriteBilingual[Write Bilingual:
EN first, VI second] - UseMultiLine --> WriteBilingual - UsePrisma --> WriteBilingual - - AddParams --> WriteBilingual - WriteBilingual --> CheckSpecial{Special
Comment Type?} - - CheckSpecial -->|Future Work| AddTODO[Add TODO prefix] - CheckSpecial -->|Needs Fix| AddFIXME[Add FIXME prefix] - CheckSpecial -->|Important Warning| AddWARNING[Add WARNING prefix] - CheckSpecial -->|Important Note| AddNOTE[Add NOTE prefix] - CheckSpecial -->|No| End([Done]) - - AddTODO --> End - AddFIXME --> End - AddWARNING --> End - AddNOTE --> End - - style HighPriority fill:#ffcccc - style MediumPriority fill:#ffffcc - style LowPriority fill:#ccffcc - style UseJSDoc fill:#cce5ff - style UseSingleLine fill:#cce5ff - style UseMultiLine fill:#cce5ff - style UsePrisma fill:#cce5ff -``` - -## Comment Format - -### Single-line Comments - -```typescript -// EN: Initialize database connection -// VI: Khởi tạo kết nối database -const db = await createConnection(); -``` - -### Multi-line Comments - -```typescript -/** - * EN: Validates user credentials and returns JWT token - * VI: Xác thực thông tin đăng nhập và trả về JWT token - * - * @param email - User email address / Địa chỉ email người dùng - * @param password - User password / Mật khẩu người dùng - * @returns JWT token / Mã JWT token - * @throws AuthenticationError if credentials invalid / Lỗi xác thực nếu thông tin không hợp lệ - */ -async function login(email: string, password: string): Promise { - // Implementation -} -``` - -## Core Comment Patterns - -### Function Documentation -```typescript -/** - * EN: Calculates the total price including tax and discount - * VI: Tính tổng giá bao gồm thuế và giảm giá - * - * @param basePrice - Original price / Giá gốc - * @param taxRate - Tax rate (0-1) / Tỷ lệ thuế (0-1) - * @param discount - Discount amount / Số tiền giảm giá - * @returns Final price / Giá cuối cùng - */ -function calculateTotal( - basePrice: number, - taxRate: number, - discount: number -): number { - // EN: Apply discount first - // VI: Áp dụng giảm giá trước - const discountedPrice = basePrice - discount; - - // EN: Then calculate tax - // VI: Sau đó tính thuế - const tax = discountedPrice * taxRate; - - return discountedPrice + tax; -} -``` - -### Class Documentation -```typescript -/** - * EN: Handles user authentication and authorization - * VI: Xử lý xác thực và phân quyền người dùng - */ -export class AuthService { - /** - * EN: JWT secret key from environment - * VI: Khóa bí mật JWT từ biến môi trường - */ - private readonly jwtSecret: string; - - /** - * EN: Verify JWT token and return user payload - * VI: Xác minh JWT token và trả về thông tin người dùng - * - * @param token - JWT token to verify / JWT token cần xác minh - * @returns User payload / Thông tin người dùng - * @throws TokenExpiredError if token expired / Lỗi token hết hạn - */ - async verifyToken(token: string): Promise { - // Implementation - } -} -``` - -### Interface/Type Documentation -```typescript -/** - * EN: User data transfer object - * VI: Đối tượng truyền dữ liệu người dùng - */ -interface UserDto { - /** EN: Unique user identifier / VI: Mã định danh duy nhất */ - id: string; - - /** EN: User email address / VI: Địa chỉ email người dùng */ - email: string; - - /** EN: User display name / VI: Tên hiển thị người dùng */ - name: string; - - /** EN: User role for authorization / VI: Vai trò người dùng để phân quyền */ - role: 'admin' | 'user' | 'guest'; -} -``` - -### React Components -```typescript -/** - * EN: User profile card component - * VI: Component thẻ hồ sơ người dùng - * - * @param user - User data to display / Dữ liệu người dùng để hiển thị - * @param onEdit - Callback when edit button clicked / Callback khi nhấn nút chỉnh sửa - */ -export function UserCard({ user, onEdit }: UserCardProps) { - // EN: Local state for loading status - // VI: State cục bộ cho trạng thái loading - const [isLoading, setIsLoading] = useState(false); - - return ( -
- {/* EN: Display user avatar / VI: Hiển thị avatar người dùng */} - {user.name} - - {/* EN: User information section / VI: Phần thông tin người dùng */} -
-

{user.name}

-

{user.email}

-
-
- ); -} -``` - -### Prisma Schema -```prisma -/// EN: User model for authentication and profile -/// VI: Model người dùng cho xác thực và hồ sơ -model User { - /// EN: Unique identifier / VI: Mã định danh duy nhất - id String @id @default(cuid()) - - /// EN: User email (unique) / VI: Email người dùng (duy nhất) - email String @unique - - /// EN: Hashed password / VI: Mật khẩu đã mã hóa - password String - - /// EN: Display name / VI: Tên hiển thị - name String - - /// EN: Account creation timestamp / VI: Thời gian tạo tài khoản - createdAt DateTime @default(now()) - - /// EN: Last update timestamp / VI: Thời gian cập nhật cuối - updatedAt DateTime @updatedAt - - @@map("users") -} -``` - -### API Controllers -```typescript -/** - * EN: User management controller - * VI: Controller quản lý người dùng - */ -export class UserController { - /** - * EN: Get user by ID - * VI: Lấy thông tin người dùng theo ID - * - * GET /api/users/:id - */ - async getById(req: Request, res: Response) { - try { - // EN: Extract user ID from params - // VI: Lấy ID người dùng từ params - const { id } = req.params; - - // EN: Fetch user from database - // VI: Lấy người dùng từ database - const user = await this.userService.findById(id); - - if (!user) { - return res.status(404).json({ - success: false, - error: { - code: 'USER_NOT_FOUND', - message: 'User not found / Không tìm thấy người dùng', - }, - }); - } - - return res.json({ - success: true, - data: user, - }); - } catch (error) { - logger.error('Failed to get user', { error, userId: req.params.id }); - return res.status(500).json({ - success: false, - error: { - code: 'INTERNAL_ERROR', - message: 'Internal server error / Lỗi máy chủ nội bộ', - }, - }); - } - } -} -``` - -### Middleware -```typescript -/** - * EN: Authentication middleware to verify JWT tokens - * VI: Middleware xác thực để kiểm tra JWT token - */ -export function authMiddleware( - req: Request, - res: Response, - next: NextFunction -) { - // EN: Extract token from Authorization header - // VI: Lấy token từ header Authorization - const authHeader = req.headers.authorization; - const token = authHeader?.replace('Bearer ', ''); - - if (!token) { - return res.status(401).json({ - success: false, - error: { - code: 'NO_TOKEN', - message: 'Authentication required / Yêu cầu xác thực', - }, - }); - } - - try { - // EN: Verify token and extract payload - // VI: Xác minh token và lấy payload - const payload = jwt.verify(token, JWT_SECRET); - req.user = payload; - next(); - } catch (error) { - return res.status(401).json({ - success: false, - error: { - code: 'INVALID_TOKEN', - message: 'Invalid or expired token / Token không hợp lệ hoặc hết hạn', - }, - }); - } -} -``` - -## Best Practices - -### 1. Comment Placement -- Place bilingual comments together (EN first, then VI) -- Keep comments close to the code they describe -- Use JSDoc format for functions and classes - -### 2. Comment Content -- **DO**: Explain WHY, not WHAT (code shows what) -- **DO**: Document complex logic and business rules -- **DO**: Include parameter descriptions and return types -- **DO**: Document error conditions and exceptions -- **DON'T**: State the obvious -- **DON'T**: Write redundant comments - -### 3. Language Guidelines -- **English**: Use clear, concise technical English -- **Vietnamese**: Use proper Vietnamese technical terms -- Keep translations accurate and natural -- Use consistent terminology across codebase - -### 4. Special Cases - -#### TODO Comments -```typescript -// TODO EN: Implement caching for better performance -// TODO VI: Triển khai caching để cải thiện hiệu suất -``` - -#### FIXME Comments -```typescript -// FIXME EN: This causes memory leak, needs refactoring -// FIXME VI: Đoạn này gây rò rỉ bộ nhớ, cần refactor -``` - -#### WARNING Comments -```typescript -// WARNING EN: Do not modify this without updating the database schema -// WARNING VI: Không sửa đổi phần này mà không cập nhật schema database -``` - -### 5. Documentation Priority - -**High Priority** (Always document): -- Public APIs and exported functions -- Complex algorithms and business logic -- Security-critical code -- Configuration and environment setup -- Error handling strategies - -**Medium Priority** (Document when helpful): -- Helper functions with non-obvious behavior -- Data transformations -- Integration points with external services - -**Low Priority** (Optional): -- Simple getters/setters -- Self-explanatory code -- Standard CRUD operations - - -## Integration with Project Rules - -When commenting code in this project: -- Follow the code organization from `project-rules` skill -- Use consistent terminology with project documentation -- Align with the API response format standards -- Document according to the testing standards -- Include security considerations where relevant - -## Quick Reference - -### Function Comment Template -```typescript -/** - * EN: [Brief description in English] - * VI: [Mô tả ngắn gọn bằng tiếng Việt] - * - * @param paramName - EN description / VI mô tả - * @returns EN description / VI mô tả - * @throws ErrorType EN when / VI khi nào - */ -``` - -### Inline Comment Template -```typescript -// EN: [English explanation] -// VI: [Giải thích tiếng Việt] -``` - -### Complex Block Template -```typescript -// EN: Step N: [What this block does] -// VI: Bước N: [Block này làm gì] -``` diff --git a/docs/en/skills/configuration-management.md b/docs/en/skills/configuration-management.md deleted file mode 100644 index b0db6753..00000000 --- a/docs/en/skills/configuration-management.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -name: configuration-management -description: Configuration management patterns for GoodGo microservices including feature flags, dynamic configuration reloading, environment-specific configurations, and secrets management. ---- - -# Configuration Management Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing feature flags and feature toggles -- Managing environment-specific configurations -- Implementing dynamic configuration reloading -- Managing secrets and sensitive configuration -- Implementing configuration validation - -## Key Patterns - -### Configuration Loading Flow - -The configuration loading process fetches configuration from multiple sources, validates it, and supports dynamic reloading: - -```mermaid -flowchart TD - Start([Application Startup]) --> LoadConfig[Load Configuration] - LoadConfig --> FetchSource{Fetch from Source} - - FetchSource --> |Environment Variables| EnvVars[Read Env Vars] - FetchSource --> |Config Files| ConfigFiles[Read JSON/YAML] - FetchSource --> |Database| Database[Query Config Table] - FetchSource --> |External Service| ExternalService[Call Config API] - - EnvVars --> Validate[Validate with Zod Schema] - ConfigFiles --> Validate - Database --> Validate - ExternalService --> Validate - - Validate --> |Valid| StoreConfig[Store in Memory Map] - Validate --> |Invalid| LogError[Log Validation Error] - LogError --> ThrowError[Throw Error] - ThrowError --> End([Application Fails to Start]) - - StoreConfig --> CheckChange{Value Changed?} - CheckChange --> |Yes| EmitEvent[Emit 'config-changed' Event] - CheckChange --> |No| SkipEvent[Skip Event] - - EmitEvent --> Ready[Configuration Ready] - SkipEvent --> Ready - Ready --> End - - Ready --> AutoReload{Auto-Reload Enabled?} - AutoReload --> |Yes| SetInterval[Set Interval Timer] - AutoReload --> |No| End - SetInterval --> Wait[Wait Interval] - Wait --> LoadConfig -``` - -### Feature Flag Evaluation Flow - -Feature flags support multiple evaluation strategies including global flags, user-specific flags, and percentage-based rollouts: - -```mermaid -flowchart TD - Start([Check Feature Flag]) --> GetFlag[Get Flag by Key] - GetFlag --> FlagExists{Flag Exists?} - - FlagExists --> |No| ReturnFalse[Return false] - ReturnFalse --> End([End]) - - FlagExists --> |Yes| CheckEnabled{Flag Enabled?} - CheckEnabled --> |No| ReturnFalse - - CheckEnabled --> |Yes| HasUserId{User ID Provided?} - HasUserId --> |No| ReturnTrue[Return true] - ReturnTrue --> End - - HasUserId --> |Yes| CheckUserSpecific{User-Specific Flag?} - CheckUserSpecific --> |Yes| MatchUser{User ID Matches?} - MatchUser --> |Yes| ReturnTrue - MatchUser --> |No| CheckPercentage - - CheckUserSpecific --> |No| CheckPercentage{Percentage Rollout?} - CheckPercentage --> |No| ReturnTrue - - CheckPercentage --> |Yes| HashUser[Hash User ID] - HashUser --> CalcHash[Calculate Hash % 100] - CalcHash --> CompareHash{Hash < Percentage?} - - CompareHash --> |Yes| ReturnTrue - CompareHash --> |No| ReturnFalse -``` - -### Feature Flags - -```typescript -// Check if feature is enabled -const enabled = await featureFlagService.isEnabled('new-feature', userId); - -if (enabled) { - // Use new feature -} -``` - -### Dynamic Configuration - -```typescript -// Load and auto-reload configuration -await configService.load(); -configService.startAutoReload(60000); // Reload every minute - -const value = configService.get('config-key', 'default-value'); -``` - -### Configuration Validation - -```typescript -// Validate with Zod -const config = validateConfig(process.env); -``` - -## Best Practices - -1. Always validate configuration at startup -2. Provide sensible defaults -3. Never commit secrets to code -4. Use feature flags for gradual rollouts - -## Resources - -- [Feature Flags Pattern](https://martinfowler.com/articles/feature-toggles.html) -- Skill Source: `.cursor/skills/configuration-management/SKILL.md` diff --git a/docs/en/skills/data-consistency-patterns.md b/docs/en/skills/data-consistency-patterns.md deleted file mode 100644 index d3845a80..00000000 --- a/docs/en/skills/data-consistency-patterns.md +++ /dev/null @@ -1,363 +0,0 @@ ---- -name: data-consistency-patterns -description: Data consistency patterns for distributed microservices including Saga patterns, distributed transactions, eventual consistency, compensation, and idempotency. Use when handling distributed transactions, implementing eventual consistency, or managing data synchronization across services. ---- - -# Data Consistency Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing distributed transactions across multiple services -- Handling eventual consistency in microservices -- Implementing Saga patterns for distributed workflows -- Designing compensation strategies for failed transactions -- Implementing idempotent operations -- Managing data synchronization across services -- Handling conflict resolution - -## Core Concepts - -### ACID vs BASE - -**ACID (Traditional):** Atomicity, Consistency, Isolation, Durability - -**BASE (Distributed):** Basic Availability, Soft state, Eventual consistency - -### Consistency Models - -- **Strong Consistency**: All nodes see same data at same time -- **Eventual Consistency**: System becomes consistent over time -- **Weak Consistency**: No guarantees about when consistency occurs - -## Key Patterns - -### Saga Orchestrator Pattern - -#### Saga Orchestration Flow - -The following diagram illustrates how a Saga orchestrator executes steps sequentially and handles compensation on failure: - -```mermaid -sequenceDiagram - participant Client - participant Orchestrator - participant Step1 as Step 1: Create Order - participant Step2 as Step 2: Reserve Inventory - participant Step3 as Step 3: Process Payment - - Client->>Orchestrator: Execute Saga - Orchestrator->>Step1: Execute Step 1 - Step1-->>Orchestrator: Success (Order Created) - Orchestrator->>Step2: Execute Step 2 - Step2-->>Orchestrator: Success (Inventory Reserved) - Orchestrator->>Step3: Execute Step 3 - Step3-->>Orchestrator: Failure (Payment Failed) - Orchestrator->>Step2: Compensate Step 2 - Step2-->>Orchestrator: Compensation Complete - Orchestrator->>Step1: Compensate Step 1 - Step1-->>Orchestrator: Compensation Complete - Orchestrator-->>Client: Saga Failed (Compensated) -``` - -#### Compensation Flow - -When a step fails, the orchestrator compensates all previously completed steps in reverse order: - -```mermaid -flowchart TD - Start([Saga Execution Starts]) --> Step1[Execute Step 1] - Step1 -->|Success| Step2[Execute Step 2] - Step1 -->|Failure| Fail1[Saga Failed
No Compensation Needed] - - Step2 -->|Success| Step3[Execute Step 3] - Step2 -->|Failure| Comp1[Compensate Step 1] - - Step3 -->|Success| Complete([Saga Completed]) - Step3 -->|Failure| Comp2[Compensate Step 2] - - Comp2 --> Comp1 - Comp1 --> Fail2[Saga Failed
All Steps Compensated] - - style Start fill:#e1f5ff - style Complete fill:#d4edda - style Fail1 fill:#f8d7da - style Fail2 fill:#f8d7da - style Comp1 fill:#fff3cd - style Comp2 fill:#fff3cd -``` - -#### Eventual Consistency Flow - -This diagram shows how data becomes consistent across services over time through event propagation: - -```mermaid -flowchart LR - subgraph ServiceA[Service A: Write Model] - Write[Write Operation] --> EventStore[Event Store] - EventStore --> Publish[Publish Event] - end - - subgraph EventBus[Event Bus] - Publish --> Queue[Event Queue] - end - - subgraph ServiceB[Service B: Read Model] - Queue --> Consume[Consume Event] - Consume --> Update[Update Read Model] - Update --> Consistent[Eventually Consistent] - end - - subgraph ServiceC[Service C: Read Model] - Queue --> Consume2[Consume Event] - Consume2 --> Update2[Update Read Model] - Update2 --> Consistent2[Eventually Consistent] - end - - style Write fill:#e1f5ff - style Consistent fill:#d4edda - style Consistent2 fill:#d4edda - style Queue fill:#fff3cd -``` - -```typescript -// Centralized orchestrator coordinates steps -const saga = new SagaOrchestrator(); -await saga.execute({ - sagaId: 'saga_123', - steps: [ - { name: 'create-order', execute: createOrder, compensate: cancelOrder }, - { name: 'reserve-inventory', execute: reserveInventory, compensate: releaseInventory }, - { name: 'process-payment', execute: chargePayment, compensate: refundPayment }, - ], - data: {}, - status: 'pending', -}); -``` - -### Saga Choreography Pattern - -In choreography, services react to events without a central coordinator: - -```typescript -// Services react to events -eventConsumer.on('order.created', async (event) => { - await inventoryService.reserve(event.data.items); - await eventPublisher.publish('inventory.reserved', {...}); -}); -``` - -#### Saga Choreography Flow - -The following diagram shows how services coordinate through events in a choreography pattern: - -```mermaid -sequenceDiagram - participant OrderService - participant EventBus - participant InventoryService - participant PaymentService - participant NotificationService - - OrderService->>EventBus: Publish order.created - EventBus->>InventoryService: order.created event - InventoryService->>InventoryService: Reserve Inventory - InventoryService->>EventBus: Publish inventory.reserved - EventBus->>PaymentService: inventory.reserved event - PaymentService->>PaymentService: Process Payment - PaymentService->>EventBus: Publish payment.processed - EventBus->>NotificationService: payment.processed event - NotificationService->>NotificationService: Send Confirmation - - Note over InventoryService,PaymentService: If payment fails,
compensation events are published - PaymentService->>EventBus: Publish payment.failed - EventBus->>InventoryService: payment.failed event - InventoryService->>InventoryService: Release Inventory - EventBus->>OrderService: payment.failed event - OrderService->>OrderService: Cancel Order -``` - -### Idempotency - -Idempotency ensures operations can be safely retried without side effects: - -```typescript -// Execute operation with idempotency check -await idempotencyHandler.execute( - idempotencyKey, - async () => await userService.create(data) -); -``` - -#### Idempotency Flow - -```mermaid -flowchart TD - Request[Client Request] --> Check{Idempotency Key
Exists?} - Check -->|Yes| Return[Return Cached Result] - Check -->|No| Execute[Execute Operation] - Execute --> Store[Store Result with Key] - Store --> Return2[Return Result] - - Return --> Client[Client Response] - Return2 --> Client - - style Check fill:#fff3cd - style Return fill:#d4edda - style Return2 fill:#d4edda -``` - -### Optimistic Locking - -Optimistic locking prevents lost updates using version fields: - -```typescript -// Update with version check -await optimisticLockService.updateWithLock( - repository, - id, - (current) => ({ ...current, name: newName }) -); -``` - -#### Optimistic Locking Flow - -```mermaid -sequenceDiagram - participant Client1 - participant Client2 - participant Service - participant DB[(Database)] - - Client1->>Service: Read Entity (version=1) - Service->>DB: SELECT * WHERE id=123 - DB-->>Service: Entity (version=1) - Service-->>Client1: Entity Data - - Client2->>Service: Read Entity (version=1) - Service->>DB: SELECT * WHERE id=123 - DB-->>Service: Entity (version=1) - Service-->>Client2: Entity Data - - Client1->>Service: Update (version=1) - Service->>DB: UPDATE WHERE id=123 AND version=1 - DB-->>Service: Success (version=2) - - Client2->>Service: Update (version=1) - Service->>DB: UPDATE WHERE id=123 AND version=1 - DB-->>Service: Conflict (version=2 exists) - Service-->>Client2: OptimisticLockError - Note over Client2: Retry with new version -``` - -### CQRS Pattern - -Command Query Responsibility Segregation separates read and write operations for optimized performance and scalability. - -#### CQRS Architecture Flow - -The following diagram illustrates how CQRS separates write and read paths: - -```mermaid -flowchart TB - subgraph WritePath[Write Path] - Command[Command] --> WriteModel[Write Model
Normalized] - WriteModel --> Event[Domain Event] - Event --> EventStore[(Event Store)] - end - - subgraph ReadPath[Read Path] - Query[Query] --> ReadModel[Read Model
Denormalized] - ReadModel --> Response[Query Response] - end - - subgraph Sync[Eventual Consistency] - EventStore --> EventHandler[Event Handler] - EventHandler --> Projection[Projection] - Projection --> ReadModel - end - - style WritePath fill:#e1f5ff - style ReadPath fill:#d4edda - style Sync fill:#fff3cd - style EventStore fill:#f8d7da -``` - -```typescript -// Write path: Command handler -await commandHandler.handle({ - type: 'CREATE_ORDER', - data: { userId, items } -}); - -// Read path: Optimized query -const orders = await readModel.findOrdersByUser(userId); -``` - -### Outbox Pattern - -The Outbox pattern ensures reliable event publishing by storing events in the same database transaction as business data. - -#### Outbox Pattern Flow - -This diagram shows how the Outbox pattern guarantees event delivery: - -```mermaid -sequenceDiagram - participant Client - participant Service - participant DB[(Database)] - participant OutboxTable[(Outbox Table)] - participant Processor[Outbox Processor] - participant EventBus[Event Bus] - - Client->>Service: Business Operation - Service->>DB: Begin Transaction - Service->>DB: Update Business Data - Service->>OutboxTable: Insert Event (same transaction) - Service->>DB: Commit Transaction - - Note over Service,OutboxTable: Event stored atomically
with business data - - Processor->>OutboxTable: Poll for Unpublished Events - OutboxTable-->>Processor: Return Events - Processor->>EventBus: Publish Event - EventBus-->>Processor: Publish Confirmed - Processor->>OutboxTable: Mark as Published - - Note over Processor,EventBus: Background processor
ensures delivery -``` - -```typescript -// Execute business operation and store event in same transaction -await prisma.$transaction(async (tx) => { - // Business operation - await tx.order.create({ data: orderData }); - - // Store event in outbox (same transaction) - await tx.outboxEvent.create({ - data: { - eventType: 'order.created', - payload: orderData, - status: 'pending' - } - }); -}); - -// Background processor publishes events from outbox -``` - -## Best Practices - -1. **Design Compensations**: Every step needs compensation -2. **Idempotent Steps**: Make steps idempotent for retries -3. **Conflict Resolution**: Define resolution strategies -4. **Monitoring**: Track saga execution and consistency lag -5. **Read Models**: Use separate read models for queries (CQRS) - -## Resources - -- [Saga Pattern](https://microservices.io/patterns/data/saga.html) -- [Event-Driven Architecture](./event-driven-architecture.md) -- [Error Handling Patterns](./error-handling-patterns.md) -- Skill Source: `.cursor/skills/data-consistency-patterns/SKILL.md` diff --git a/docs/en/skills/database-prisma.md b/docs/en/skills/database-prisma.md deleted file mode 100644 index 9887f581..00000000 --- a/docs/en/skills/database-prisma.md +++ /dev/null @@ -1,571 +0,0 @@ ---- -name: database-prisma -description: Prisma ORM and database patterns for GoodGo microservices. Use when working with databases, creating Prisma schemas, writing migrations, implementing repositories, or optimizing queries. ---- - -# Prisma Database Patterns - -## When to Use This Skill - -Use this skill when: -- Setting up Prisma for a new service -- Creating or modifying database schemas -- Writing database migrations -- Implementing repository patterns -- Optimizing database queries -- Setting up database connections -- Implementing transactions -- Working with Neon PostgreSQL - -## Core Concepts - -### Architecture -- Repository pattern for data access -- Prisma as ORM for type safety -- Neon PostgreSQL as primary database -- Connection pooling for performance -- Transaction support for data consistency - -## Prisma Setup - -### Installation - -```bash -npm install @prisma/client prisma -npm install --save-dev @types/node -``` - -### Configuration - -```typescript -// prisma/schema.prisma -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -// Base model with common fields -model User { - id String @id @default(cuid()) - email String @unique - name String? - password String - role Role @default(USER) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - // Relations - posts Post[] - profile Profile? - - // Indexes for performance - @@index([email]) - @@index([createdAt]) - @@map("users") -} - -model Post { - id String @id @default(cuid()) - title String - content String? - published Boolean @default(false) - authorId String - author User @relation(fields: [authorId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@index([authorId]) - @@index([published, createdAt]) - @@map("posts") -} - -model Profile { - id String @id @default(cuid()) - bio String? - avatar String? - userId String @unique - user User @relation(fields: [userId], references: [id]) - - @@map("profiles") -} - -enum Role { - USER - ADMIN - MODERATOR -} -``` - -### Schema Relationships - -The following diagram illustrates the relationships between User, Post, and Profile models: - -```mermaid -erDiagram - User ||--o{ Post : "has many" - User ||--o| Profile : "has one" - - User { - string id PK - string email UK - string name - string password - enum role - datetime createdAt - datetime updatedAt - } - - Post { - string id PK - string title - string content - boolean published - string authorId FK - datetime createdAt - datetime updatedAt - } - - Profile { - string id PK - string bio - string avatar - string userId FK,UK - } -``` - -## Database Connection - -```typescript -// src/lib/prisma.ts -import { PrismaClient } from '@prisma/client'; - -const globalForPrisma = global as unknown as { - prisma: PrismaClient | undefined; -}; - -export const prisma = globalForPrisma.prisma ?? - new PrismaClient({ - log: process.env.NODE_ENV === 'development' - ? ['query', 'error', 'warn'] - : ['error'], - }); - -if (process.env.NODE_ENV !== 'production') { - globalForPrisma.prisma = prisma; -} - -// Middleware for soft delete -prisma.$use(async (params, next) => { - if (params.model && params.action === 'delete') { - return next({ - ...params, - action: 'update', - args: { - ...params.args, - data: { deletedAt: new Date() } - } - }); - } - return next(params); -}); -``` - -## Repository Pattern - -```typescript -// src/repositories/base.repository.ts -export abstract class BaseRepository { - constructor(protected prisma: PrismaClient) {} - - abstract findById(id: string): Promise; - abstract findAll(options?: any): Promise; - abstract create(data: any): Promise; - abstract update(id: string, data: any): Promise; - abstract delete(id: string): Promise; -} - -// src/repositories/user.repository.ts -export class UserRepository extends BaseRepository { - async findById(id: string): Promise { - return this.prisma.user.findUnique({ - where: { id }, - include: { profile: true } - }); - } - - async findByEmail(email: string): Promise { - return this.prisma.user.findUnique({ - where: { email } - }); - } - - async findAll(options: { - page?: number; - limit?: number; - search?: string; - sortBy?: string; - order?: 'asc' | 'desc'; - } = {}): Promise<{ data: User[]; total: number }> { - const { - page = 1, - limit = 10, - search, - sortBy = 'createdAt', - order = 'desc' - } = options; - - const where = search ? { - OR: [ - { email: { contains: search, mode: 'insensitive' } }, - { name: { contains: search, mode: 'insensitive' } } - ] - } : {}; - - const [data, total] = await Promise.all([ - this.prisma.user.findMany({ - where, - skip: (page - 1) * limit, - take: limit, - orderBy: { [sortBy]: order }, - include: { profile: true } - }), - this.prisma.user.count({ where }) - ]); - - return { data, total }; - } - - async create(data: CreateUserDto): Promise { - return this.prisma.user.create({ - data: { - email: data.email, - password: data.password, - name: data.name, - profile: data.bio ? { - create: { bio: data.bio } - } : undefined - }, - include: { profile: true } - }); - } - - async update(id: string, data: UpdateUserDto): Promise { - return this.prisma.user.update({ - where: { id }, - data, - include: { profile: true } - }); - } - - async delete(id: string): Promise { - await this.prisma.user.delete({ - where: { id } - }); - } -} -``` - -## Transactions - -```typescript -// Transaction example -export class TransferService { - async transferFunds( - fromAccountId: string, - toAccountId: string, - amount: number - ) { - return await this.prisma.$transaction(async (tx) => { - // Check balance - const fromAccount = await tx.account.findUnique({ - where: { id: fromAccountId } - }); - - if (!fromAccount || fromAccount.balance < amount) { - throw new Error('Insufficient funds'); - } - - // Deduct from sender - const updatedFrom = await tx.account.update({ - where: { id: fromAccountId }, - data: { balance: { decrement: amount } } - }); - - // Add to receiver - const updatedTo = await tx.account.update({ - where: { id: toAccountId }, - data: { balance: { increment: amount } } - }); - - // Create transaction record - const transaction = await tx.transaction.create({ - data: { - fromAccountId, - toAccountId, - amount, - type: 'TRANSFER', - status: 'COMPLETED' - } - }); - - return transaction; - }, { - maxWait: 5000, - timeout: 10000, - }); - } -} -``` - -## Migrations - -### Migration Workflow - -The following diagram shows the typical migration workflow from development to production: - -```mermaid -flowchart TD - Start([Start Migration]) --> EditSchema[Edit schema.prisma] - EditSchema --> CreateMigration[Run: prisma migrate dev] - CreateMigration --> GenerateSQL[Prisma generates SQL] - GenerateSQL --> ReviewSQL{Review SQL?} - ReviewSQL -->|Yes| CheckSQL[Check migration SQL] - ReviewSQL -->|No| ApplyDev[Apply to dev database] - CheckSQL --> ApplyDev - ApplyDev --> GenerateClient[Generate Prisma Client] - GenerateClient --> TestDev[Test in development] - TestDev --> TestPass{Tests pass?} - TestPass -->|No| FixIssues[Fix issues] - FixIssues --> EditSchema - TestPass -->|Yes| CommitMigration[Commit migration files] - CommitMigration --> DeployProd[Deploy to production] - DeployProd --> RunDeploy[Run: prisma migrate deploy] - RunDeploy --> End([Migration Complete]) - - style Start fill:#e1f5e1 - style End fill:#e1f5e1 - style TestPass fill:#fff4e1 - style ReviewSQL fill:#fff4e1 -``` - -```bash -# Create migration -npx prisma migrate dev --name add_user_table - -# Apply migrations -npx prisma migrate deploy - -# Reset database -npx prisma migrate reset - -# Generate Prisma Client -npx prisma generate -``` - -### Migration Files - -```sql --- migrations/20240101000000_add_user_table/migration.sql -CREATE TABLE "users" ( - "id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "name" TEXT, - "password" TEXT NOT NULL, - "role" TEXT NOT NULL DEFAULT 'USER', - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - -CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); -CREATE INDEX "users_createdAt_idx" ON "users"("createdAt"); -``` - -## Query Optimization - -### Query Execution Flow - -The following sequence diagram illustrates how Prisma queries flow from the application layer to the database: - -```mermaid -sequenceDiagram - participant App as Application - participant Repo as Repository - participant Client as Prisma Client - participant Pool as Connection Pool - participant DB as PostgreSQL - - App->>Repo: findById(id) - Repo->>Client: prisma.user.findUnique() - Client->>Client: Validate query - Client->>Client: Generate SQL - Client->>Pool: Request connection - Pool->>DB: Execute SQL query - DB-->>Pool: Return results - Pool-->>Client: Return data - Client->>Client: Transform to TypeScript types - Client-->>Repo: Return typed result - Repo-->>App: Return User | null - - Note over App,DB: Prisma ensures type safety
throughout the flow -``` - -```typescript -// Optimized queries -export class OptimizedUserRepository { - // Select only needed fields - async findUsersLight() { - return this.prisma.user.findMany({ - select: { - id: true, - email: true, - name: true - } - }); - } - - // Use pagination - async findPaginated(cursor?: string) { - return this.prisma.user.findMany({ - take: 10, - skip: cursor ? 1 : 0, - cursor: cursor ? { id: cursor } : undefined, - orderBy: { createdAt: 'desc' } - }); - } - - // Batch operations - async createMany(users: CreateUserDto[]) { - return this.prisma.user.createMany({ - data: users, - skipDuplicates: true - }); - } - - // Use raw SQL for complex queries - async getStatistics() { - return this.prisma.$queryRaw` - SELECT - COUNT(*) as total, - COUNT(CASE WHEN role = 'ADMIN' THEN 1 END) as admins, - COUNT(CASE WHEN created_at > NOW() - INTERVAL '30 days' THEN 1 END) as new_users - FROM users - `; - } -} -``` - -## Seeding - -```typescript -// prisma/seed.ts -import { PrismaClient } from '@prisma/client'; -import bcrypt from 'bcrypt'; - -const prisma = new PrismaClient(); - -async function main() { - // Create admin user - const adminPassword = await bcrypt.hash('admin123', 10); - const admin = await prisma.user.upsert({ - where: { email: 'admin@goodgo.com' }, - update: {}, - create: { - email: 'admin@goodgo.com', - name: 'Admin User', - password: adminPassword, - role: 'ADMIN' - } - }); - - // Create test users - const testUsers = Array.from({ length: 10 }, (_, i) => ({ - email: `user${i}@example.com`, - name: `Test User ${i}`, - password: bcrypt.hashSync('password123', 10) - })); - - await prisma.user.createMany({ - data: testUsers, - skipDuplicates: true - }); - - console.log('Database seeded successfully'); -} - -main() - .catch(console.error) - .finally(() => prisma.$disconnect()); -``` - -## Neon PostgreSQL Configuration - -```typescript -// .env -DATABASE_URL="postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require" - -// Connection pooling for serverless -DIRECT_URL="postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require" -``` - -## Testing with Prisma - -```typescript -// __tests__/user.repository.test.ts -import { mockDeep, mockReset } from 'jest-mock-extended'; -import { PrismaClient } from '@prisma/client'; - -jest.mock('../src/lib/prisma', () => ({ - __esModule: true, - prisma: mockDeep() -})); - -describe('UserRepository', () => { - beforeEach(() => { - mockReset(prismaMock); - }); - - it('should create user', async () => { - const user = { id: '1', email: 'test@example.com' }; - prismaMock.user.create.mockResolvedValue(user); - - const result = await repository.create({ - email: 'test@example.com', - password: 'password' - }); - - expect(result).toEqual(user); - }); -}); -``` - -## Best Practices - -1. **Schema Design** - - Use appropriate field types - - Add indexes for frequently queried fields - - Use relations instead of storing JSON - - Implement soft deletes when needed - -2. **Performance** - - Use select to fetch only needed fields - - Implement pagination for large datasets - - Use connection pooling - - Cache frequently accessed data - -3. **Security** - - Never expose sensitive fields - - Use parameterized queries - - Validate input before database operations - - Implement row-level security - -4. **Maintenance** - - Keep migrations small and focused - - Test migrations before production - - Backup before major changes - - Monitor query performance \ No newline at end of file diff --git a/docs/en/skills/deployment-kubernetes.md b/docs/en/skills/deployment-kubernetes.md deleted file mode 100644 index b55581ba..00000000 --- a/docs/en/skills/deployment-kubernetes.md +++ /dev/null @@ -1,618 +0,0 @@ ---- -name: deployment-kubernetes -description: Kubernetes deployment patterns for GoodGo microservices. Use when deploying to staging/production, creating K8s manifests, configuring HPA, setting up ingress, or troubleshooting K8s deployments. ---- - -# Kubernetes Deployment Patterns - -## When to Use This Skill - -Use this skill when: -- Deploying services to staging/production environments -- Creating or updating Kubernetes manifests -- Configuring autoscaling (HPA/VPA) -- Setting up ingress and load balancing -- Managing secrets and configmaps -- Troubleshooting deployment issues -- Implementing health checks and probes -- Setting up monitoring and logging - -## Core Concepts - -### Kubernetes Architecture - -The following diagram illustrates the key Kubernetes components and their relationships in a typical GoodGo service deployment: - -```mermaid -graph TB - subgraph External["External Traffic"] - Client[Client Request] - end - - subgraph IngressLayer["Ingress Layer"] - Ingress[Ingress
api.goodgo.com] - end - - subgraph ServiceLayer["Service Layer"] - Service[Service
ClusterIP] - end - - subgraph DeploymentLayer["Deployment Layer"] - Deployment[Deployment
iam-service] - HPA[HorizontalPodAutoscaler
HPA] - end - - subgraph PodLayer["Pod Layer"] - Pod1[Pod 1
Container] - Pod2[Pod 2
Container] - Pod3[Pod 3
Container] - end - - subgraph ConfigLayer["Configuration Layer"] - ConfigMap[ConfigMap
app-config] - Secret[Secret
database-secrets] - end - - Client -->|HTTPS| Ingress - Ingress -->|Route /auth| Service - Service -->|Load Balance| Pod1 - Service -->|Load Balance| Pod2 - Service -->|Load Balance| Pod3 - - Deployment -->|Manages| Pod1 - Deployment -->|Manages| Pod2 - Deployment -->|Manages| Pod3 - - HPA -->|Scales| Deployment - - Pod1 -->|Reads| ConfigMap - Pod2 -->|Reads| ConfigMap - Pod3 -->|Reads| ConfigMap - - Pod1 -->|Reads| Secret - Pod2 -->|Reads| Secret - Pod3 -->|Reads| Secret -``` - -### Deployment Strategy -- Rolling updates for zero-downtime deployments -- Resource limits and requests for stability -- Health checks (liveness/readiness probes) -- Horizontal Pod Autoscaler (HPA) for auto-scaling -- ConfigMaps for configuration -- Secrets for sensitive data - -### Pod Lifecycle - -Pods go through various states during their lifecycle. Health checks (liveness and readiness probes) determine pod availability: - -```mermaid -stateDiagram-v2 - [*] --> Pending: Pod Created - - Pending --> ContainerCreating: Scheduler Assigned - ContainerCreating --> Running: Containers Started - - Running --> Running: Liveness Check Pass - Running --> Restarting: Liveness Check Fail (3x) - Restarting --> Running: Container Restarted - - Running --> Ready: Readiness Check Pass - Ready --> Running: Readiness Check Fail (3x) - - Ready --> Terminating: Pod Deleted - Terminating --> [*]: Cleanup Complete - - note right of Ready - Pod receives traffic - from Service - end note - - note right of Running - Liveness probe checks - if container is alive - end note - - note right of Restarting - Container restarted - after 3 failures - end note -``` - -### Service Discovery Flow - -Kubernetes provides built-in service discovery through DNS. The following diagram shows how requests flow from external clients to pods: - -```mermaid -sequenceDiagram - participant Client - participant Ingress - participant Service - participant Pod1 - participant Pod2 - participant Pod3 - - Client->>Ingress: HTTPS Request
api.goodgo.com/auth/login - Ingress->>Ingress: TLS Termination - Ingress->>Ingress: Path Routing
/auth → iam-service - - Ingress->>Service: HTTP Request
iam-service:80 - Service->>Service: DNS Resolution
iam-service.goodgo.svc.cluster.local - - Service->>Service: Endpoint Selection
Load Balancing - - alt Pod1 Selected - Service->>Pod1: Forward Request - Pod1->>Pod1: Process Request - Pod1->>Service: Response - else Pod2 Selected - Service->>Pod2: Forward Request - Pod2->>Pod2: Process Request - Pod2->>Service: Response - else Pod3 Selected - Service->>Pod3: Forward Request - Pod3->>Pod3: Process Request - Pod3->>Service: Response - end - - Service->>Ingress: Response - Ingress->>Client: HTTPS Response -``` - -## Service Deployment Manifest - -```yaml -# kubernetes/iam-service.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: iam-service - namespace: goodgo - labels: - app: iam-service - version: v1 -spec: - replicas: 3 - selector: - matchLabels: - app: iam-service - template: - metadata: - labels: - app: iam-service - version: v1 - spec: - containers: - - name: iam-service - image: goodgo/iam-service:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3000 - name: http - env: - - name: NODE_ENV - value: "production" - - name: PORT - value: "3000" - - name: DATABASE_URL - valueFrom: - secretKeyRef: - name: database-secrets - key: url - - name: JWT_SECRET - valueFrom: - secretKeyRef: - name: auth-secrets - key: jwt-secret - - name: REDIS_URL - valueFrom: - configMapKeyRef: - name: redis-config - key: url - resources: - requests: - memory: "256Mi" - cpu: "250m" - limits: - memory: "512Mi" - cpu: "500m" - livenessProbe: - httpGet: - path: /health - port: 3000 - initialDelaySeconds: 30 - periodSeconds: 10 - readinessProbe: - httpGet: - path: /ready - port: 3000 - initialDelaySeconds: 5 - periodSeconds: 5 ---- -apiVersion: v1 -kind: Service -metadata: - name: iam-service - namespace: goodgo -spec: - type: ClusterIP - selector: - app: iam-service - ports: - - port: 80 - targetPort: 3000 - protocol: TCP -``` - -## Horizontal Pod Autoscaler - -```yaml -# kubernetes/hpa.yaml -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: iam-service-hpa - namespace: goodgo -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: iam-service - minReplicas: 2 - maxReplicas: 10 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 70 - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: 80 - behavior: - scaleDown: - stabilizationWindowSeconds: 300 - policies: - - type: Percent - value: 50 - periodSeconds: 60 - scaleUp: - stabilizationWindowSeconds: 0 - policies: - - type: Percent - value: 100 - periodSeconds: 15 -``` - -## ConfigMap & Secrets - -```yaml -# kubernetes/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: app-config - namespace: goodgo -data: - NODE_ENV: "production" - LOG_LEVEL: "info" - REDIS_URL: "redis://redis-service:6379" - METRICS_ENABLED: "true" - ---- -# kubernetes/secrets.yaml (example - use sealed-secrets in production) -apiVersion: v1 -kind: Secret -metadata: - name: database-secrets - namespace: goodgo -type: Opaque -stringData: - url: "postgresql://user:pass@postgres:5432/db" - ---- -apiVersion: v1 -kind: Secret -metadata: - name: auth-secrets - namespace: goodgo -type: Opaque -stringData: - jwt-secret: "your-secret-key" - refresh-secret: "your-refresh-secret" -``` - -## Ingress Configuration - -```yaml -# kubernetes/ingress.yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: api-ingress - namespace: goodgo - annotations: - kubernetes.io/ingress.class: nginx - cert-manager.io/cluster-issuer: letsencrypt-prod - nginx.ingress.kubernetes.io/rate-limit: "100" - nginx.ingress.kubernetes.io/ssl-redirect: "true" -spec: - tls: - - hosts: - - api.goodgo.com - secretName: api-tls-secret - rules: - - host: api.goodgo.com - http: - paths: - - path: /auth - pathType: Prefix - backend: - service: - name: iam-service - port: - number: 80 - - path: /users - pathType: Prefix - backend: - service: - name: user-service - port: - number: 80 -``` - -## Database Deployment (Development Only) - -```yaml -# kubernetes/postgres.yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: postgres - namespace: goodgo -spec: - serviceName: postgres - replicas: 1 - selector: - matchLabels: - app: postgres - template: - metadata: - labels: - app: postgres - spec: - containers: - - name: postgres - image: postgres:14-alpine - ports: - - containerPort: 5432 - env: - - name: POSTGRES_DB - value: goodgo - - name: POSTGRES_USER - value: postgres - - name: POSTGRES_PASSWORD - valueFrom: - secretKeyRef: - name: postgres-secret - key: password - volumeMounts: - - name: postgres-storage - mountPath: /var/lib/postgresql/data - volumeClaimTemplates: - - metadata: - name: postgres-storage - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 10Gi -``` - -## Deployment Scripts - -```bash -#!/bin/bash -# scripts/deploy-k8s.sh - -# Set namespace -NAMESPACE="goodgo" -ENVIRONMENT="${1:-staging}" - -# Create namespace if not exists -kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - - -# Apply configurations -echo "Applying ConfigMaps..." -kubectl apply -f kubernetes/configmap-$ENVIRONMENT.yaml - -echo "Applying Secrets..." -kubectl apply -f kubernetes/secrets-$ENVIRONMENT.yaml - -echo "Deploying services..." -kubectl apply -f kubernetes/iam-service.yaml -kubectl apply -f kubernetes/user-service.yaml - -echo "Configuring autoscaling..." -kubectl apply -f kubernetes/hpa.yaml - -echo "Setting up ingress..." -kubectl apply -f kubernetes/ingress.yaml - -# Wait for rollout -kubectl rollout status deployment/iam-service -n $NAMESPACE -kubectl rollout status deployment/user-service -n $NAMESPACE - -echo "Deployment complete!" -``` - -## Health Check Implementation - -```typescript -// src/modules/health/health.controller.ts -export class HealthController { - constructor( - private prisma: PrismaClient, - private redis: Redis - ) {} - - // Liveness probe - is the service alive? - async liveness(req: Request, res: Response) { - res.status(200).json({ status: 'ok' }); - } - - // Readiness probe - is the service ready to accept traffic? - async readiness(req: Request, res: Response) { - try { - // Check database connection - await this.prisma.$queryRaw`SELECT 1`; - - // Check Redis connection - await this.redis.ping(); - - res.status(200).json({ - status: 'ready', - checks: { - database: 'ok', - redis: 'ok' - } - }); - } catch (error) { - res.status(503).json({ - status: 'not ready', - error: error.message - }); - } - } -} -``` - -## Monitoring with Prometheus - -```yaml -# kubernetes/servicemonitor.yaml -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: iam-service-monitor - namespace: goodgo -spec: - selector: - matchLabels: - app: iam-service - endpoints: - - port: http - path: /metrics - interval: 30s -``` - -## Common Commands - -```bash -# Deploy to staging -kubectl apply -f kubernetes/ -n goodgo-staging - -# Check deployment status -kubectl get deployments -n goodgo -kubectl get pods -n goodgo -kubectl get svc -n goodgo - -# View logs -kubectl logs -f deployment/iam-service -n goodgo -kubectl logs -f pod-name -n goodgo --tail=100 - -# Scale manually -kubectl scale deployment iam-service --replicas=5 -n goodgo - -# Update image -kubectl set image deployment/iam-service iam-service=goodgo/iam-service:v1.2.3 -n goodgo - -# Rollback -kubectl rollout undo deployment/iam-service -n goodgo - -# Port forward for debugging -kubectl port-forward service/iam-service 3000:80 -n goodgo - -# Execute command in pod -kubectl exec -it pod-name -n goodgo -- /bin/sh - -# View HPA status -kubectl get hpa -n goodgo -kubectl describe hpa iam-service-hpa -n goodgo - -# View resource usage -kubectl top nodes -kubectl top pods -n goodgo -``` - -## Troubleshooting - -### Pod Not Starting - -```bash -# Check pod status -kubectl describe pod pod-name -n goodgo - -# Check events -kubectl get events -n goodgo --sort-by='.lastTimestamp' - -# Check logs -kubectl logs pod-name -n goodgo --previous -``` - -### ImagePullBackOff - -```bash -# Check image name and tag -kubectl describe pod pod-name -n goodgo | grep -i image - -# Check image pull secrets -kubectl get secrets -n goodgo -``` - -### CrashLoopBackOff - -```bash -# Check logs of crashed container -kubectl logs pod-name -n goodgo --previous - -# Check resource limits -kubectl describe pod pod-name -n goodgo | grep -A 5 Limits -``` - -## Best Practices - -1. **Resource Management** - - Always set resource requests and limits - - Monitor actual usage and adjust accordingly - - Use HPA for automatic scaling - -2. **Configuration** - - Use ConfigMaps for non-sensitive config - - Use Secrets for sensitive data - - Never hardcode configuration in images - -3. **Health Checks** - - Implement both liveness and readiness probes - - Set appropriate timeouts and thresholds - - Include dependency checks in readiness probe - -4. **Deployment** - - Use rolling updates for zero-downtime - - Set maxSurge and maxUnavailable appropriately - - Test deployments in staging first - -5. **Security** - - Run containers as non-root user - - Use network policies to restrict traffic - - Regularly update base images - - Use sealed-secrets or external secret manager - -6. **Monitoring** - - Expose metrics endpoint - - Set up alerts for critical issues - - Monitor resource usage and performance \ No newline at end of file diff --git a/docs/en/skills/documentation.md b/docs/en/skills/documentation.md deleted file mode 100644 index b75b697e..00000000 --- a/docs/en/skills/documentation.md +++ /dev/null @@ -1,507 +0,0 @@ ---- -name: documentation -description: Guidelines for writing technical documentation in the GoodGo project. Use when creating or updating README files, guides, architecture docs, or API documentation. Ensures bilingual (EN/VI) consistency and proper structure. ---- - -# Documentation Writing Guidelines - -## Documentation Structure - -The project follows a structured documentation hierarchy organized by location and content type: - -``` -docs/ -├── en/ # English documentation -│ ├── guides/ # How-to guides -│ │ ├── getting-started.md -│ │ ├── development.md -│ │ ├── deployment.md -│ │ └── local-development.md -│ ├── architecture/ # System design docs -│ │ ├── system-design.md -│ │ └── service-communication.md -│ ├── api/ # API documentation -│ │ └── openapi/ -│ └── runbooks/ # Operational guides -│ ├── incident-response.md -│ └── rollback-procedure.md -├── vi/ # Vietnamese documentation (mirror structure) -└── README.md # Documentation index -``` - -### Documentation Structure Diagram - -The following diagram illustrates where different types of documentation should be placed: - -```mermaid -graph TD - Start[Documentation Need] --> TypeDecision{Documentation Type?} - - TypeDecision -->|Project-Level| ProjectDocs[Project-Level Documentation] - TypeDecision -->|Service/Package| ServiceDocs[Service/Package Documentation] - TypeDecision -->|Deployment| DeployDocs[Deployment Documentation] - TypeDecision -->|Infrastructure| InfraDocs[Infrastructure Documentation] - - ProjectDocs --> ProjectLoc["docs/en/
docs/vi/"] - ProjectLoc --> ProjectSub{Content Type?} - ProjectSub -->|Guides| GuidesLoc["guides/
(getting-started.md,
deployment.md)"] - ProjectSub -->|Architecture| ArchLoc["architecture/
(system-design.md)"] - ProjectSub -->|API Specs| APILoc["api/openapi/
(*.yaml)"] - ProjectSub -->|Runbooks| RunbookLoc["runbooks/
(incident-response.md)"] - - ServiceDocs --> ServiceLoc["services/[name]/README.md
packages/[name]/README.md"] - ServiceLoc --> ServiceFormat[Format: Side-by-side bilingual] - - DeployDocs --> DeployLoc["deployments/[env]/README.md"] - DeployLoc --> DeployFormat[Format: Technical, operations-focused] - - InfraDocs --> InfraLoc["infra/[component]/README.md"] - InfraLoc --> InfraFormat[Format: Side-by-side bilingual] - - style ProjectDocs fill:#e1f5ff - style ServiceDocs fill:#fff4e1 - style DeployDocs fill:#ffe1f5 - style InfraDocs fill:#e1ffe1 -``` - -## Where to Put Documentation - -### Project-Level Documentation -- **Location**: `docs/en/` and `docs/vi/` -- **Examples**: Getting started, deployment guides, architecture -- **Format**: Markdown with bilingual support - -### Service/Package Documentation -- **Location**: `services/[service-name]/README.md` or `packages/[package-name]/README.md` -- **Content**: Service-specific setup, API endpoints, configuration -- **Format**: Single README with bilingual sections - -### Deployment Documentation -- **Location**: `deployments/[environment]/README.md` -- **Content**: Environment-specific deployment instructions -- **Format**: Technical, operations-focused - -### Infrastructure Documentation -- **Location**: `infra/[component]/README.md` -- **Content**: Infrastructure component configuration and usage -- **Examples**: `infra/traefik/README.md`, `infra/observability/README.md` - -## Bilingual Documentation Rules - -### Format Options - -**Option 1: Side-by-side (Recommended for short content)** -```markdown -# Service Name / Tên Dịch Vụ - -This is a description. -Đây là mô tả. -``` - -**Option 2: Separate files (Recommended for long content)** -``` -docs/ -├── en/ -│ └── guides/ -│ └── deployment.md -└── vi/ - └── guides/ - └── deployment.md -``` - -**Option 3: Sections (For mixed content)** -```markdown -# English Section - -Content in English... - ---- - -# Phần Tiếng Việt - -Nội dung bằng tiếng Việt... -``` - -### When to Use Each Format - -- **Side-by-side**: README files, short guides, configuration docs -- **Separate files**: Long guides (>200 lines), architecture docs, runbooks -- **Sections**: API documentation, technical specifications - -### Bilingual Format Decision Flow - -Use the following decision tree to choose the appropriate bilingual format: - -```mermaid -flowchart TD - Start[Creating Documentation] --> CheckLength{Content Length?} - - CheckLength -->|Short
< 200 lines| CheckLocation{Document Location?} - CheckLength -->|Long
> 200 lines| SeparateFiles[Use Separate Files Format] - - CheckLocation -->|README files
Service/Package docs
Infrastructure docs| SideBySide[Use Side-by-side Format] - CheckLocation -->|docs/guides/
Short configuration docs| SideBySide - - CheckLength -->|Medium| CheckType{Content Type?} - CheckType -->|API Documentation
Technical Specifications| Sections[Use Sections Format] - CheckType -->|Mixed Content| Sections - - SeparateFiles --> SeparateAction["Create docs/en/[path]/file.md
Create docs/vi/[path]/file.md
(Mirror structure)"] - SideBySide --> SideBySideAction["Single file with
EN / VI inline
Example: 'Title / Tiêu Đề'"] - Sections --> SectionsAction["Single file with
--- separator
EN section then VI section"] - - SeparateAction --> Done[Documentation Complete] - SideBySideAction --> Done - SectionsAction --> Done - - style SideBySide fill:#e1f5ff - style SeparateFiles fill:#fff4e1 - style Sections fill:#ffe1f5 - style Done fill:#e1ffe1 -``` - -## Documentation Templates - -### Service README Template - -```markdown -# Service Name / Tên Dịch Vụ - -> **EN**: Brief description in English -> **VI**: Mô tả ngắn gọn bằng tiếng Việt - -## Features / Tính Năng - -- Feature 1 / Tính năng 1 -- Feature 2 / Tính năng 2 - -## Prerequisites / Yêu Cầu - -- Node.js 20+ -- PostgreSQL (Neon) -- Redis - -## Quick Start / Bắt Đầu Nhanh - -```bash -# Install dependencies / Cài đặt dependencies -pnpm install - -# Setup environment / Thiết lập môi trường -cp .env.example .env - -# Start service / Khởi động service -pnpm dev -``` - -## Configuration / Cấu Hình - -| Variable | Description / Mô Tả | Default | Required | -|----------|---------------------|---------|----------| -| PORT | Server port / Cổng server | 5000 | No | - -## API Endpoints - -See [API Documentation](../../docs/api/openapi/service-name.yaml) - -## Development / Phát Triển - -[Development instructions...] - -## Testing / Kiểm Thử - -```bash -pnpm test -``` - -## Deployment / Triển Khai - -See [Deployment Guide](../../docs/en/guides/deployment.md) -``` - -### Guide Template (docs/en/guides/) - -```markdown -# Guide Title - -**Last Updated**: 2024-01-01 -**Difficulty**: Beginner/Intermediate/Advanced - -## Overview - -Brief overview of what this guide covers. - -## Prerequisites - -- Requirement 1 -- Requirement 2 - -## Step-by-Step Instructions - -### Step 1: Title - -Description and commands... - -```bash -command here -``` - -### Step 2: Title - -Description and commands... - -## Troubleshooting - -### Issue 1 - -**Problem**: Description -**Solution**: Steps to fix - -## Next Steps - -- Link to related guide -- Link to another resource - -## Resources - -- [Related Doc](../path/to/doc.md) -- [External Link](https://example.com) -``` - -### Architecture Document Template - -```markdown -# Component Architecture - -## Overview - -High-level description of the component. - -## Architecture Diagram - -```mermaid -graph TD - A[Component A] --> B[Component B] - B --> C[Component C] -``` - -## Components - -### Component Name - -**Purpose**: What it does -**Technology**: Tech stack -**Dependencies**: What it depends on - -## Data Flow - -1. Step 1 -2. Step 2 -3. Step 3 - -## Design Decisions - -### Decision 1 - -**Context**: Why this decision was needed -**Decision**: What was decided -**Consequences**: Impact of the decision - -## Deployment - -How this component is deployed. - -## Monitoring - -How to monitor this component. -``` - -## Writing Style Guidelines - -### Technical Writing Principles - -1. **Clear and Concise**: Use simple language, avoid jargon -2. **Action-Oriented**: Start with verbs (Install, Configure, Deploy) -3. **Structured**: Use headings, lists, and tables -4. **Examples**: Provide code examples and commands -5. **Visual**: Use diagrams where helpful - -### Code Examples - -```markdown -# Good: With context and explanation -Install dependencies using pnpm: - -```bash -pnpm install -``` - -# Bad: No context -```bash -pnpm install -``` -``` - -### Commands - -- Always show the full command -- Include comments for clarity -- Show expected output when helpful - -```bash -# Good -docker-compose up -d -# Expected output: Creating network, Starting containers... - -# Bad -docker-compose up -``` - -### Links - -- Use relative links for internal docs -- Use descriptive link text (not "click here") - -```markdown -# Good -See the [Deployment Guide](../guides/deployment.md) for details. - -# Bad -Click [here](../guides/deployment.md) for more info. -``` - -## Documentation Checklist - -### Before Writing - -- [ ] Determine correct location (docs/ vs service README) -- [ ] Choose bilingual format (side-by-side vs separate) -- [ ] Review existing docs for consistency - -### While Writing - -- [ ] Use clear, concise language -- [ ] Include code examples -- [ ] Add diagrams where helpful -- [ ] Provide troubleshooting section -- [ ] Link to related documentation - -### After Writing - -- [ ] Test all commands and code examples -- [ ] Check all links work -- [ ] Ensure bilingual consistency -- [ ] Update documentation index (docs/README.md) -- [ ] Request review from team - -## Common Mistakes to Avoid - -### ❌ Don't - -- Write documentation in only one language -- Put detailed guides in service README (use docs/) -- Use absolute paths in links -- Assume prior knowledge -- Skip code examples -- Forget to update when code changes - -### ✅ Do - -- Maintain bilingual documentation -- Use appropriate location (docs/ vs README) -- Use relative links -- Explain prerequisites -- Provide working examples -- Keep docs up-to-date with code - -## Documentation Maintenance - -### When to Update Documentation - -- New feature added -- API changes -- Configuration changes -- Deployment process changes -- Bug fixes affecting usage -- Architecture changes - -### Version Documentation - -For major changes, consider: -- Adding "Last Updated" date -- Creating versioned docs (v1/, v2/) -- Maintaining changelog - -## Tools and Resources - -### Markdown Tools - -- **Mermaid**: For diagrams -- **Tables Generator**: For complex tables -- **Markdown Linter**: For consistency - -### Documentation Testing - -```bash -# Check for broken links -find docs -name "*.md" -exec markdown-link-check {} \; - -# Lint markdown files -markdownlint docs/**/*.md -``` - -## Examples from Project - -### Good Documentation Examples - -- `docs/en/guides/getting-started.md` - Clear step-by-step guide -- `services/_template/README.md` - Comprehensive service README -- `deployments/local/README.md` - Operations-focused deployment guide - -### Documentation Locations Reference - -| Content Type | Location | Format | -|--------------|----------|--------| -| Getting Started | `docs/en/guides/getting-started.md` | Separate files | -| Service Setup | `services/[name]/README.md` | Side-by-side | -| Deployment | `docs/en/guides/deployment.md` | Separate files | -| Architecture | `docs/en/architecture/` | Separate files | -| API Specs | `docs/en/api/openapi/` | OpenAPI YAML | -| Runbooks | `docs/en/runbooks/` | Separate files | -| Infrastructure | `infra/[component]/README.md` | Side-by-side | -| Environment Config | `deployments/[env]/README.md` | Technical only | - -## Quick Reference - -### File Naming - -- Use kebab-case: `getting-started.md` -- Be descriptive: `local-development.md` not `dev.md` -- Match EN and VI filenames - -### Heading Levels - -```markdown -# H1: Document Title (only one per file) -## H2: Major Sections -### H3: Subsections -#### H4: Details (use sparingly) -``` - -### Bilingual Patterns - -```markdown -# Pattern 1: Inline -Description / Mô tả - -# Pattern 2: After slash -PORT=5000 # Server port / Cổng server - -# Pattern 3: Table -| Variable | Description / Mô Tả | - -# Pattern 4: Code comments -# EN: Install dependencies -# VI: Cài đặt dependencies -pnpm install -``` diff --git a/docs/en/skills/error-handling-patterns.md b/docs/en/skills/error-handling-patterns.md deleted file mode 100644 index fc243ee9..00000000 --- a/docs/en/skills/error-handling-patterns.md +++ /dev/null @@ -1,460 +0,0 @@ ---- -name: error-handling-patterns -description: Error handling patterns and conventions for GoodGo microservices. Use when implementing error handling, creating custom error classes, handling exceptions, standardizing error responses, or debugging error scenarios. ---- - -# Error Handling Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing error handling in services, controllers, or repositories -- Creating custom error classes for specific error scenarios -- Standardizing error responses across APIs -- Handling exceptions from external services or database operations -- Implementing error middleware and global error handlers -- Debugging error scenarios and improving error messages -- Distinguishing between operational and programming errors - -## Core Concepts - -### Error Types - -1. **Operational Errors**: Expected errors that occur during normal operation - - Examples: Validation errors, authentication failures, resource not found - - Should be handled gracefully and return appropriate HTTP status codes - - Safe to expose error details to clients (with caution) - -2. **Programming Errors**: Unexpected errors due to bugs in code - - Examples: Null pointer exceptions, type errors, logic bugs - - Should be logged with full details for debugging - - Should return generic error messages to clients (hide implementation details) - -### Error Propagation Flow - -The following diagram illustrates how errors propagate through the application layers: - -```mermaid -flowchart TD - A[Request] --> B[Controller] - B --> C[Service Layer] - C --> D[Repository Layer] - D --> E{Error Occurs?} - E -->|Yes| F[Throw HttpError] - E -->|No| G[Return Data] - F --> H[Error Middleware] - H --> I{Error Type?} - I -->|HttpError| J[Extract Status/Code] - I -->|Prisma Error| K[Map to HttpError] - I -->|Zod Error| L[Map to ValidationError] - I -->|Unknown| M[Map to InternalServerError] - J --> N[Log Error] - K --> N - L --> N - M --> N - N --> O{Is Operational?} - O -->|Yes| P[Log as Warning] - O -->|No| Q[Log as Error] - P --> R[Format Response] - Q --> R - R --> S{Is Production?} - S -->|Yes & 5xx| T[Generic Message] - S -->|No or < 5xx| U[Detailed Message] - T --> V[Send Response] - U --> V - G --> V -``` - -### Error Hierarchy Structure - -The error class hierarchy shows the relationship between different error types: - -```mermaid -classDiagram - class Error { - <> - +message: string - +stack: string - } - class HttpError { - +statusCode: number - +errorCode: string - +isOperational: boolean - +details?: any - +toApiResponse() - } - class NotFoundError { - +statusCode: 404 - } - class BadRequestError { - +statusCode: 400 - } - class ValidationError { - +statusCode: 422 - } - class UnauthorizedError { - +statusCode: 401 - } - class ForbiddenError { - +statusCode: 403 - } - class ConflictError { - +statusCode: 409 - } - class RateLimitError { - +statusCode: 429 - } - class InternalServerError { - +statusCode: 500 - } - class ServiceUnavailableError { - +statusCode: 503 - } - class DatabaseError { - +statusCode: 500 - } - class ExternalServiceError { - +statusCode: 502 - } - - Error <|-- HttpError - HttpError <|-- NotFoundError - HttpError <|-- BadRequestError - HttpError <|-- ValidationError - HttpError <|-- UnauthorizedError - HttpError <|-- ForbiddenError - HttpError <|-- ConflictError - HttpError <|-- RateLimitError - HttpError <|-- InternalServerError - HttpError <|-- ServiceUnavailableError - HttpError <|-- DatabaseError - HttpError <|-- ExternalServiceError -``` - -### Error Handling Decision Tree - -Use this decision tree to determine which error class to use: - -```mermaid -flowchart TD - A[Error Occurs] --> B{Error Type?} - B -->|Resource Not Found| C[NotFoundError
404] - B -->|Invalid Input| D{Validation?} - B -->|Authentication| E{Type?} - B -->|Resource Conflict| F[ConflictError
409] - B -->|Rate Limit| G[RateLimitError
429] - B -->|Database| H[DatabaseError
500] - B -->|External Service| I[ExternalServiceError
502] - B -->|Service Unavailable| J[ServiceUnavailableError
503] - B -->|Unknown/Programming| K[InternalServerError
500] - D -->|Schema Validation| L[ValidationError
422] - D -->|Bad Request Format| M[BadRequestError
400] - E -->|No Token/Invalid| N[UnauthorizedError
401] - E -->|No Permission| O[ForbiddenError
403] - C --> P[Set isOperational: true] - L --> P - M --> P - N --> P - O --> P - F --> P - G --> P - H --> Q{Is Operational?} - I --> Q - J --> Q - K --> R[Set isOperational: false] - Q -->|Yes| P - Q -->|No| R - P --> S[Include Error Code] - R --> S - S --> T[Add Context Details] - T --> U[Throw Error] -``` - -### Error Code System - -The platform uses a centralized error code system (`ErrorCode` enum) that: -- Provides unique identifiers for each error type -- Maps to HTTP status codes consistently -- Enables error tracking and analytics -- Supports internationalization - -Error codes follow the pattern: `{CATEGORY}_{NUMBER}` -- `AUTH_001` - Authentication errors -- `VALIDATION_001` - Validation errors -- `RESOURCE_001` - Resource errors -- `DB_001` - Database errors - -## Patterns - -### Base Error Class: HttpError - -All custom errors extend the `HttpError` base class: - -```typescript -export class HttpError extends Error { - public readonly statusCode: number; - public readonly errorCode: string; - public readonly isOperational: boolean; - public readonly details?: any; - - constructor( - message: string, - statusCode: number = 500, - errorCode: string = 'INTERNAL_ERROR', - isOperational: boolean = true, - details?: any - ) { - super(message); - this.statusCode = statusCode; - this.errorCode = errorCode; - this.isOperational = isOperational; - this.details = details; - Error.captureStackTrace(this, this.constructor); - } - - toApiResponse() { - return { - success: false, - error: { - code: this.errorCode, - message: this.message, - ...(this.details && { details: this.details }), - }, - timestamp: new Date().toISOString(), - }; - } -} -``` - -### Standard Error Classes - -Use these predefined error classes for common scenarios: - -**Resource Errors:** -- `NotFoundError` - 404: Resource not found -- `ConflictError` - 409: Resource conflict (e.g., duplicate) - -**Validation Errors:** -- `ValidationError` - 422: Input validation failed -- `BadRequestError` - 400: Invalid request - -**Authentication/Authorization:** -- `UnauthorizedError` - 401: Authentication required -- `ForbiddenError` - 403: Access denied - -**System Errors:** -- `InternalServerError` - 500: Internal server error (programming error) -- `ServiceUnavailableError` - 503: Service temporarily unavailable -- `DatabaseError` - 500: Database operation failed -- `ExternalServiceError` - 502: External service error - -**Rate Limiting:** -- `RateLimitError` - 429: Too many requests - -### Error Code Enum - -Centralized error codes in `ErrorCode` enum: - -```typescript -export enum ErrorCode { - // Authentication & Authorization - UNAUTHORIZED = 'AUTH_001', - FORBIDDEN = 'AUTH_002', - INVALID_TOKEN = 'AUTH_003', - TOKEN_EXPIRED = 'AUTH_004', - - // Validation - VALIDATION_ERROR = 'VALIDATION_001', - INVALID_FORMAT = 'VALIDATION_002', - - // Resources - NOT_FOUND = 'RESOURCE_001', - ALREADY_EXISTS = 'RESOURCE_002', - CONFLICT = 'RESOURCE_003', - - // Database - DATABASE_ERROR = 'DB_001', - CONSTRAINT_VIOLATION = 'DB_004', - - // System - INTERNAL_ERROR = 'SYS_001', - RATE_LIMIT_EXCEEDED = 'SYS_003', -} -``` - -### Using Errors in Services - -```typescript -import { NotFoundError, ConflictError } from '../errors/http-error'; -import { ErrorCode } from '../errors/error-codes'; - -export class UserService { - async getUserById(id: string) { - const user = await this.repository.findById(id); - - if (!user) { - throw new NotFoundError('User', { id }); - } - - return user; - } - - async createUser(data: CreateUserInput) { - const existing = await this.repository.findByEmail(data.email); - - if (existing) { - throw new ConflictError('User with this email already exists'); - } - - return await this.repository.create(data); - } -} -``` - -### Error Middleware Pattern - -Global error handler middleware processes all errors: - -```typescript -export const errorHandler = ( - err: any, - req: express.Request, - res: express.Response, - _next: express.NextFunction -): void => { - let statusCode = 500; - let errorCode = ErrorCode.INTERNAL_ERROR; - let message = 'Internal server error'; - let isOperational = false; - - // Handle HttpError instances - if (err instanceof HttpError) { - statusCode = err.statusCode; - errorCode = err.errorCode as ErrorCode; - message = err.message; - isOperational = err.isOperational; - } - // Handle Prisma errors - else if (err.code === 'P2002') { - statusCode = 409; - errorCode = ErrorCode.CONSTRAINT_VIOLATION; - message = 'Resource already exists'; - isOperational = true; - } - // Handle Zod validation errors - else if (err.name === 'ZodError') { - statusCode = 422; - errorCode = ErrorCode.VALIDATION_ERROR; - message = 'Validation failed'; - // Extract validation details - } - - // Log error - if (!isOperational || statusCode >= 500) { - logger.error('Unhandled error', { error: err, statusCode, errorCode }); - } else { - logger.warn('Operational error', { error: err, statusCode, errorCode }); - } - - // Send response - const response = { - success: false, - error: { - code: errorCode, - message: isProduction && statusCode >= 500 - ? 'Internal server error' - : message, - }, - timestamp: new Date().toISOString(), - }; - - res.status(statusCode).json(response); -}; -``` - -### Async Error Wrapper - -Wrap async route handlers to catch promise rejections: - -```typescript -export const asyncHandler = (fn: Function) => { - return (req: express.Request, res: express.Response, next: express.NextFunction) => { - Promise.resolve(fn(req, res, next)).catch(next); - }; -}; - -// Usage -router.get('/users/:id', asyncHandler(async (req, res) => { - const user = await userService.getUserById(req.params.id); - res.json({ success: true, data: user }); -})); -``` - -### Error Response Format - -Standardized error response format: - -```typescript -{ - success: false, - error: { - code: "RESOURCE_001", - message: "User not found", - details?: { - // Optional additional details (not in production for 5xx errors) - } - }, - timestamp: "2024-01-01T00:00:00.000Z" -} -``` - -## Best Practices - -1. **Use Specific Error Classes**: Use the most specific error class available -2. **Include Context**: Provide helpful error messages with context -3. **Mark Operational Errors**: Set `isOperational: true` for expected errors -4. **Don't Expose Internal Details**: Hide implementation details in production -5. **Log Appropriately**: Use `logger.error()` for programming errors, `logger.warn()` for operational errors -6. **Handle Database Errors**: Map Prisma errors to appropriate HTTP errors -7. **Use Error Codes**: Always use `ErrorCode` enum for consistency -8. **Validate Early**: Validate input early to catch errors before processing - -## Common Mistakes - -1. **Not Using Error Classes**: Using generic `Error` instead of specific error classes -2. **Exposing Stack Traces**: Including stack traces in production responses -3. **Ignoring Errors**: Not handling errors in async operations -4. **Generic Error Messages**: Using vague error messages without context -5. **Not Logging**: Forgetting to log errors for debugging -6. **Wrong HTTP Status Codes**: Using incorrect status codes for error types -7. **Not Using Error Middleware**: Handling errors manually instead of using middleware - -## Troubleshooting - -### Error Not Caught by Middleware - -**Problem**: Error not being caught by error middleware -**Solution**: Ensure error middleware is added last, after all routes. Use `asyncHandler` for async route handlers. - -### Generic Error Messages in Production - -**Problem**: Generic "Internal server error" shown even for operational errors -**Solution**: Check `isOperational` flag is set correctly. Verify error middleware handles all error types. - -### Error Code Not Found - -**Problem**: Error code not in `ErrorCode` enum -**Solution**: Add error code to enum following naming convention. Update `ERROR_CODE_TO_STATUS` mapping. - -### Stack Traces Exposed - -**Problem**: Stack traces visible in API responses -**Solution**: Ensure production environment checks are in place. Use error middleware to filter stack traces. - -## Resources - -- [Error Classes](../../services/iam-service/src/errors/http-error.ts) - Base error classes -- [Error Codes](../../services/iam-service/src/errors/error-codes.ts) - Error code definitions -- [Error Middleware](../../services/iam-service/src/middlewares/error.middleware.ts) - Global error handler -- [API Design](../api-design/SKILL.md) - API response formats -- [Security](../security/SKILL.md) - Security error handling diff --git a/docs/en/skills/event-driven-architecture.md b/docs/en/skills/event-driven-architecture.md deleted file mode 100644 index 6dbb8c5f..00000000 --- a/docs/en/skills/event-driven-architecture.md +++ /dev/null @@ -1,452 +0,0 @@ ---- -name: event-driven-architecture -description: Event-driven architecture patterns with Apache Kafka for GoodGo microservices. Use when implementing async communication, event publishing/consuming, event sourcing, CQRS, or integrating event streams with HTTP endpoints. ---- - -# Event-Driven Architecture Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing asynchronous communication between services -- Decoupling services for better scalability -- Publishing domain events for downstream consumers -- Consuming events from other services -- Implementing event sourcing patterns -- Implementing CQRS (Command Query Responsibility Segregation) -- Exposing event streams via HTTP (SSE/WebSocket) -- Handling eventual consistency across services -- Building reactive systems that respond to changes -- Integrating with Apache Kafka message broker - -## Core Concepts - -### Event-Driven vs Request-Response - -**Request-Response (Synchronous):** -- Client waits for response -- Tight coupling between services -- Blocking operations -- Immediate consistency -- Use Traefik API Gateway for HTTP/REST - -**Event-Driven (Asynchronous):** -- Fire-and-forget publishing -- Loose coupling between services -- Non-blocking operations -- Eventual consistency -- Use Kafka for message broker - -### Kafka Fundamentals - -- **Topics**: Named streams of events (e.g., `user.created`, `order.placed`) -- **Partitions**: Physical division of topics for parallelism and scaling -- **Consumer Groups**: Groups of consumers that work together to process events -- **Producers**: Services that publish events to topics -- **Consumers**: Services that subscribe to topics and process events - -#### Consumer Groups Architecture - -The following diagram illustrates how consumer groups distribute work across partitions: - -```mermaid -graph TB - subgraph Topic["Topic: user.created"] - P0["Partition 0"] - P1["Partition 1"] - P2["Partition 2"] - end - - subgraph ConsumerGroup["Consumer Group: notification-service"] - C1["Consumer 1"] - C2["Consumer 2"] - end - - subgraph ConsumerGroup2["Consumer Group: analytics-service"] - C3["Consumer 3"] - C4["Consumer 4"] - C5["Consumer 5"] - end - - P0 --> C1 - P1 --> C2 - P2 --> C1 - - P0 --> C3 - P1 --> C4 - P2 --> C5 - - style Topic fill:#e1f5ff - style ConsumerGroup fill:#fff4e1 - style ConsumerGroup2 fill:#e8f5e9 -``` - -**Key Points:** -- Each partition is consumed by only one consumer per consumer group -- Multiple consumer groups can independently consume from the same topic -- Consumers in a group automatically rebalance when members join or leave -- More partitions enable better parallelism within a consumer group - -### Traefik Integration - -Traefik serves dual purpose: -- **API Gateway**: Routes synchronous HTTP/REST requests -- **Event Streaming Gateway**: Routes SSE/WebSocket connections to event streaming endpoints - -Services publish events to Kafka, then expose SSE/WebSocket endpoints that consume from Kafka for HTTP clients. - -## Key Patterns - -### Event Publishing - -```typescript -// src/core/events/event-publisher.ts -import { producer } from '../config/kafka.config'; -import { logger } from '@goodgo/logger'; -import { v4 as uuidv4 } from 'uuid'; - -export class EventPublisher { - async publish( - topic: string, - event: Omit, - options?: { partitionKey?: string } - ): Promise { - const fullEvent: T = { - ...event, - eventId: uuidv4(), - timestamp: new Date().toISOString(), - source: this.serviceName, - } as T; - - await producer.send({ - topic, - messages: [{ - key: options?.partitionKey || fullEvent.eventId, - value: JSON.stringify(fullEvent), - headers: { - 'event-type': event.eventType, - 'event-version': event.eventVersion, - }, - }], - }); - } -} -``` - -#### Event Publishing Flow - -The following sequence diagram shows how events are published from a service to Kafka: - -```mermaid -sequenceDiagram - participant Service as Service Layer - participant Publisher as EventPublisher - participant Kafka as Kafka Broker - participant Topic as Topic Partition - - Service->>Publisher: publish(topic, event, options) - activate Publisher - Publisher->>Publisher: Generate eventId - Publisher->>Publisher: Add timestamp & source - Publisher->>Publisher: Determine partition key - Publisher->>Kafka: send({ topic, messages }) - activate Kafka - Kafka->>Topic: Route to partition - activate Topic - Topic-->>Kafka: Acknowledge - deactivate Topic - Kafka-->>Publisher: Success - deactivate Kafka - Publisher-->>Service: Complete (fire-and-forget) - deactivate Publisher - Note over Service,Publisher: Non-blocking operation -``` - -**Key Points:** -- Publishing is asynchronous and non-blocking -- Partition key determines which partition receives the event -- Events are acknowledged by Kafka before completion -- Fire-and-forget pattern prevents blocking request handlers - -### Event Consuming - -```typescript -// src/core/events/event-consumer.ts -import { kafka } from '../config/kafka.config'; - -export class EventConsumer { - private handlers: Map = new Map(); - - on(eventType: string, handler: EventHandler): void { - if (!this.handlers.has(eventType)) { - this.handlers.set(eventType, []); - } - this.handlers.get(eventType)!.push(handler); - } - - async start(topics: string[]): Promise { - await this.consumer.connect(); - await this.consumer.subscribe({ topics, fromBeginning: false }); - - await this.consumer.run({ - eachMessage: async ({ topic, partition, message }) => { - const event: BaseEvent = JSON.parse(message.value?.toString() || '{}'); - const handlers = this.handlers.get(event.eventType) || []; - await Promise.all(handlers.map(h => h.handle(event))); - }, - }); - } -} -``` - -### Outbox Pattern for Transactional Publishing - -The Outbox pattern ensures transactional consistency by storing events in the database within the same transaction as business data, then publishing them asynchronously. - -#### Outbox Pattern Flow - -The following sequence diagram illustrates the outbox pattern workflow: - -```mermaid -sequenceDiagram - participant Service as Service Layer - participant DB as Database - participant Outbox as Outbox Table - participant Processor as Outbox Processor - participant Publisher as EventPublisher - participant Kafka as Kafka Broker - - Service->>DB: Begin Transaction - activate DB - Service->>DB: Create business entity - Service->>Outbox: Insert event (status: PENDING) - Outbox-->>DB: Stored - Service->>DB: Commit Transaction - deactivate DB - Note over Service,DB: Event stored atomically with business data - - loop Polling Interval - Processor->>Outbox: Find PENDING events - Outbox-->>Processor: Return events - Processor->>Publisher: publish(event) - activate Publisher - Publisher->>Kafka: Send to topic - Kafka-->>Publisher: Acknowledge - Publisher-->>Processor: Success - deactivate Publisher - Processor->>Outbox: Update status to PUBLISHED - end -``` - -**Key Points:** -- Events are stored in the database within the same transaction as business data -- A separate background process (Outbox Processor) publishes events to Kafka -- Ensures at-least-once delivery guarantee -- Prevents lost events if Kafka is temporarily unavailable - -```typescript -// Store event in database within transaction -await prisma.outboxEvent.create({ - data: { - eventType: 'user.created', - eventData: userData, - topic: 'user.created', - status: 'PENDING', - }, -}); - -// Separate process publishes from outbox to Kafka -async function processOutbox() { - const events = await prisma.outboxEvent.findMany({ - where: { status: 'PENDING' }, - }); - - for (const event of events) { - await eventPublisher.publish(event.topic, event.eventData); - await prisma.outboxEvent.update({ - where: { id: event.id }, - data: { status: 'PUBLISHED' }, - }); - } -} -``` - -### SSE Endpoint for Event Streaming - -```typescript -// src/modules/events/events.controller.ts -async streamEvents(req: Request, res: Response): Promise { - res.setHeader('Content-Type', 'text/event-stream'); - res.setHeader('Cache-Control', 'no-cache'); - res.setHeader('Connection', 'keep-alive'); - - const topic = req.query.topic as string; - const consumer = kafka.consumer({ groupId: `sse-${Date.now()}` }); - - await consumer.connect(); - await consumer.subscribe({ topic, fromBeginning: false }); - - await consumer.run({ - eachMessage: async ({ message }) => { - const event = JSON.parse(message.value?.toString() || '{}'); - res.write(`data: ${JSON.stringify(event)}\n\n`); - }, - }); - - req.on('close', async () => { - await consumer.disconnect(); - }); -} -``` - -## Event Structure - -```typescript -interface BaseEvent { - eventId: string; - eventType: string; - eventVersion: string; - timestamp: string; - source: string; - correlationId?: string; - traceId?: string; - data: unknown; -} -``` - -## Best Practices - -### Event Naming Conventions - -- **Event Type**: `{domain}.{action}.v{version}` (e.g., `user.created.v1`) -- **Topic**: `{domain}.{entity}.{action}` (e.g., `user.created`) -- Use lowercase with dots as separators -- Keep names descriptive and consistent - -### Partition Key Selection - -- Use entity ID for ordering guarantees (same entity → same partition) -- Use correlation ID for request tracing -- Use user ID for user-scoped events -- Avoid high-cardinality keys (distributes evenly) - -### Event Ordering Guarantees - -- Kafka guarantees ordering **per partition** -- Use partition key to ensure related events go to same partition -- Events in different partitions have no ordering guarantee -- Don't rely on global ordering across all events - -### Error Handling - -- Implement Dead Letter Queue (DLQ) for failed events -- Use retry with exponential backoff -- Log all event processing failures -- Monitor consumer lag and DLQ size - -### Observability - -- Log all published and consumed events -- Track metrics: events published/consumed, processing duration, consumer lag -- Add distributed tracing to event flows -- Include correlation IDs for request tracking - -## Infrastructure Setup - -### Docker Compose (Local) - -```yaml -services: - kafka: - image: confluentinc/cp-kafka:7.4.0 - ports: - - "9092:9092" - environment: - KAFKA_BROKER_ID: 1 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 - - schema-registry: - image: confluentinc/cp-schema-registry:7.4.0 - ports: - - "8081:8081" - environment: - SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: kafka:9092 -``` - -## Testing - -### Unit Testing - -```typescript -import { EventPublisher } from '../event-publisher'; -import { producer } from '../../config/kafka.config'; - -jest.mock('../../config/kafka.config'); - -describe('EventPublisher', () => { - it('should publish event successfully', async () => { - const publisher = new EventPublisher(); - const mockSend = jest.fn().mockResolvedValue({}); - (producer.send as jest.Mock) = mockSend; - - await publisher.publish('user.created', { - eventType: 'user.created', - eventVersion: '1.0.0', - data: { userId: '123' }, - }); - - expect(mockSend).toHaveBeenCalled(); - }); -}); -``` - -### Integration Testing with Test Containers - -```typescript -import { KafkaContainer } from '@testcontainers/kafka'; - -describe('Event Flow E2E', () => { - let kafkaContainer: StartedKafkaContainer; - - beforeAll(async () => { - kafkaContainer = await new KafkaContainer().start(); - process.env.KAFKA_BROKERS = kafkaContainer.getBootstrapServer(); - }); - - it('should publish and consume event', async () => { - // Test implementation - }); -}); -``` - -## Common Use Cases - -### User Created Event Flow - -1. Auth Service creates user in database -2. Publishes `user.created` event to Kafka -3. Notification Service consumes event and sends welcome email -4. Analytics Service consumes event and updates metrics - -### Order Processing with Multiple Consumers - -1. Order Service publishes `order.placed` event -2. Payment Service processes payment -3. Inventory Service reserves items -4. Notification Service sends confirmation - -## Related Skills - -- [Resilience Patterns](./resilience-patterns.md) - Circuit breaker, retry patterns -- [Error Handling Patterns](./error-handling-patterns.md) - Error handling best practices -- [Observability & Monitoring](./observability-monitoring.md) - Logging, metrics, tracing -- [Middleware Patterns](./middleware-patterns.md) - SSE endpoint middleware -- [Project Rules](./project-rules.md) - GoodGo coding standards - -## Resources - -- [KafkaJS Documentation](https://kafka.js.org/) - Node.js Kafka client -- [Confluent Schema Registry](https://docs.confluent.io/platform/current/schema-registry/index.html) - Schema versioning -- [Kafka Best Practices](https://kafka.apache.org/documentation/#best_practices) - Official Kafka documentation -- Skill Source: `.cursor/skills/event-driven-architecture/SKILL.md` diff --git a/docs/en/skills/infrastructure-as-code.md b/docs/en/skills/infrastructure-as-code.md deleted file mode 100644 index 280d06ca..00000000 --- a/docs/en/skills/infrastructure-as-code.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -name: infrastructure-as-code -description: Infrastructure as Code patterns for GoodGo platform including Terraform modules, Kubernetes operators, infrastructure testing, GitOps workflows, and multi-environment management. ---- - -# Infrastructure as Code Patterns - -## When to Use This Skill - -Use this skill when: -- Managing infrastructure with code -- Implementing Terraform modules -- Setting up GitOps workflows -- Creating Kubernetes operators -- Testing infrastructure changes - -## Infrastructure as Code Workflow - -The following diagram illustrates the complete IaC workflow from code changes to infrastructure deployment: - -```mermaid -flowchart TD - A[Developer writes IaC code] --> B[Commit to Git repository] - B --> C{Code Review} - C -->|Rejected| D[Fix issues] - D --> B - C -->|Approved| E[Merge to branch] - E --> F[CI/CD Pipeline triggers] - F --> G{Terraform or
Kubernetes?} - G -->|Terraform| H[Terraform Workflow] - G -->|Kubernetes| I[GitOps Workflow] - H --> J[terraform init] - J --> K[terraform validate] - K --> L[terraform plan] - L --> M{Plan review} - M -->|Issues found| D - M -->|Approved| N[terraform apply] - N --> O[Infrastructure updated] - I --> P[GitOps tool detects changes] - P --> Q[Sync to Kubernetes cluster] - Q --> O - O --> R[Health checks] - R --> S{Deployment successful?} - S -->|No| T[Rollback] - S -->|Yes| U[Monitor infrastructure] -``` - -## GitOps Flow - -GitOps enables automated synchronization of Kubernetes manifests from Git to clusters: - -```mermaid -sequenceDiagram - participant Dev as Developer - participant Git as Git Repository - participant ArgoCD as ArgoCD/Flux - participant K8s as Kubernetes Cluster - - Dev->>Git: Push manifest changes - Git->>ArgoCD: Detect changes (poll/webhook) - ArgoCD->>Git: Fetch latest manifests - ArgoCD->>ArgoCD: Compare desired vs actual state - alt Drift detected - ArgoCD->>K8s: Apply changes (sync) - K8s->>K8s: Update resources - K8s->>ArgoCD: Status update - else Auto-heal enabled - ArgoCD->>K8s: Self-heal (correct drift) - end - ArgoCD->>Git: Update sync status -``` - -## Terraform Execution Flow - -The Terraform workflow ensures safe and predictable infrastructure changes: - -```mermaid -flowchart LR - A[terraform init] --> B[Load providers & modules] - B --> C[terraform validate] - C --> D{Syntax valid?} - D -->|No| E[Fix errors] - E --> C - D -->|Yes| F[terraform plan] - F --> G[Read state] - G --> H[Build dependency graph] - H --> I[Calculate changes] - I --> J[Generate plan] - J --> K{Review plan} - K -->|Issues| L[Adjust code] - L --> F - K -->|Approved| M[terraform apply] - M --> N[Lock state] - N --> O[Execute changes] - O --> P{Success?} - P -->|No| Q[Rollback] - P -->|Yes| R[Update state] - R --> S[Unlock state] - S --> T[Save state to backend] -``` - -## Key Patterns - -### Terraform Modules - -Terraform modules enable reusable infrastructure components across environments: - -```hcl -# Reusable module -module "postgresql" { - source = "../../modules/postgresql" - database_name = "goodgo" - environment = "staging" -} -``` - -### Module Structure - -The following diagram shows the typical Terraform module structure and how modules are composed: - -```mermaid -graph TD - A[Module: postgresql] --> B[variables.tf
Input parameters] - A --> C[main.tf
Resource definitions] - A --> D[outputs.tf
Exported values] - E[Environment: staging] --> F[main.tf] - F --> G[Module: postgresql] - F --> H[Module: redis] - F --> I[Module: kubernetes-cluster] - G --> J[Output: database_url] - H --> K[Output: redis_url] - I --> L[Output: cluster_endpoint] - F --> M[terraform.tfvars
Environment config] -``` - -### GitOps with ArgoCD - -GitOps tools like ArgoCD and Flux automatically sync Kubernetes manifests from Git repositories: - -```yaml -# Automated sync from Git -spec: - source: - repoURL: https://github.com/goodgo/platform - path: deployments/production/kubernetes - syncPolicy: - automated: - prune: true - selfHeal: true -``` - -### Multi-Environment Management - -Managing infrastructure across multiple environments requires clear separation and consistent patterns: - -```mermaid -graph TB - subgraph "Git Repository" - A[infra/terraform] - end - subgraph "Modules (Reusable)" - B[modules/postgresql] - C[modules/redis] - D[modules/kubernetes-cluster] - end - subgraph "Environments" - E[environments/staging] - F[environments/production] - end - A --> B - A --> C - A --> D - A --> E - A --> F - E --> B - E --> C - E --> D - F --> B - F --> C - F --> D - E --> G[terraform.tfvars
staging config] - F --> H[terraform.tfvars
production config] - G --> I[Remote State Backend
staging/terraform.tfstate] - H --> J[Remote State Backend
production/terraform.tfstate] -``` - -## Infrastructure Testing - -Always validate infrastructure changes before applying them: - -```bash -# Validate Terraform syntax -terraform init -terraform validate - -# Preview changes -terraform plan -out=tfplan - -# Review plan before applying -terraform show tfplan -``` - -## Best Practices - -1. **Version Control**: Keep all infrastructure in version control -2. **Modules**: Create reusable Terraform modules for common components -3. **Testing**: Test infrastructure changes before applying to production -4. **GitOps**: Use GitOps (ArgoCD/Flux) for Kubernetes deployments -5. **Environment Isolation**: Separate environments completely with different state backends -6. **State Management**: Use remote state backends (S3, GCS) with state locking -7. **Secrets**: Never commit secrets - use environment variables or secrets managers - -### Common Mistakes to Avoid - -1. **Committing Secrets**: Never hardcode passwords or API keys -2. **Local State Only**: Always use remote state backends for team collaboration -3. **No State Locking**: Enable state locking to prevent concurrent modifications -4. **Direct Apply**: Always review `terraform plan` output before applying - -## Resources - -- [Terraform Documentation](https://www.terraform.io/docs) -- [Deployment Kubernetes](./deployment-kubernetes.md) -- Skill Source: `.cursor/skills/infrastructure-as-code/SKILL.md` diff --git a/docs/en/skills/inter-service-communication.md b/docs/en/skills/inter-service-communication.md deleted file mode 100644 index 4649c318..00000000 --- a/docs/en/skills/inter-service-communication.md +++ /dev/null @@ -1,280 +0,0 @@ ---- -name: inter-service-communication -description: Inter-service communication patterns for GoodGo microservices including gRPC, GraphQL, service-to-service authentication, protocol selection, and client patterns. Use when implementing service-to-service calls, choosing communication protocols, or building service clients. ---- - -# Inter-Service Communication Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing service-to-service communication -- Choosing between REST, gRPC, or GraphQL protocols -- Setting up gRPC services and clients -- Implementing GraphQL services and resolvers -- Implementing service-to-service authentication -- Building resilient service clients with circuit breakers -- Managing connection pooling for service clients -- Implementing request/response interceptors -- Handling service discovery for internal calls -- Optimizing inter-service communication performance - -## Core Concepts - -### Communication Protocol Options - -**HTTP/REST:** -- Human-readable, easy to debug -- Browser-compatible -- Standard HTTP semantics -- JSON payloads -- Good for external APIs - -**gRPC:** -- Binary protocol (Protocol Buffers) -- High performance, low latency -- Streaming support -- Strong typing with .proto files -- HTTP/2 based - -**GraphQL:** -- Flexible query language -- Single endpoint -- Client-controlled data fetching -- Strong typing with schema - -### Protocol Selection Guidelines - -Choose protocol based on use case, performance requirements, team expertise, and ecosystem needs. - -#### Protocol Selection Decision Tree - -```mermaid -flowchart TD - Start([Need Inter-Service Communication]) --> CheckExternal{External/Public API?} - - CheckExternal -->|Yes| UseREST[Use REST] - CheckExternal -->|No| CheckPerformance{High Performance
Required?} - - CheckPerformance -->|Yes| CheckStreaming{Need Streaming?} - CheckPerformance -->|No| CheckFlexible{Need Flexible
Queries?} - - CheckStreaming -->|Yes| UseGRPC[Use gRPC] - CheckStreaming -->|No| CheckLowLatency{Ultra Low
Latency?} - - CheckLowLatency -->|Yes| UseGRPC - CheckLowLatency -->|No| UseREST - - CheckFlexible -->|Yes| UseGraphQL[Use GraphQL] - CheckFlexible -->|No| UseREST - - UseREST --> RESTDesc["REST: External APIs
Browser clients
Simple CRUD"] - UseGRPC --> GRPCDesc["gRPC: Internal services
High performance
Streaming support"] - UseGraphQL --> GraphQLDesc["GraphQL: Complex queries
Mobile apps
Flexible data"] - - style UseREST fill:#e1f5ff - style UseGRPC fill:#fff4e1 - style UseGraphQL fill:#e8f5e9 -``` - -## Key Patterns - -### Service-to-Service Call Flow - -The following diagram illustrates the complete flow of a service-to-service call, including authentication, interceptors, and error handling: - -```mermaid -sequenceDiagram - participant ClientService as Client Service - participant ClientLib as Service Client
(HTTP/gRPC/GraphQL) - participant Interceptor as Request
Interceptor - participant Auth as Auth
Middleware - participant TargetService as Target Service - participant Logger as Logger - participant Metrics as Metrics - - ClientService->>ClientLib: Make request - ClientLib->>Interceptor: Add correlation ID
Add service auth header
Add request ID - Interceptor->>Logger: Log request start - Interceptor->>Metrics: Track request start - Interceptor->>TargetService: HTTP/gRPC/GraphQL Request
(with headers) - - TargetService->>Auth: Validate x-service-auth - alt Invalid Auth - Auth-->>TargetService: 403 Forbidden - TargetService-->>ClientLib: Error Response - ClientLib->>Logger: Log error - ClientLib->>Metrics: Track failure - ClientLib-->>ClientService: ServiceError - else Valid Auth - Auth->>TargetService: Authenticated - TargetService->>TargetService: Process request - TargetService-->>ClientLib: Success Response - ClientLib->>Logger: Log success
(with correlation ID) - ClientLib->>Metrics: Track success
(duration, status) - ClientLib-->>ClientService: Response Data - end -``` - -### HTTP/REST Service Client - -```typescript -// Base service client with circuit breaker and interceptors -import { ServiceClient } from '../../core/clients/service-client'; - -const notificationClient = new ServiceClient({ - baseURL: process.env.NOTIFICATION_SERVICE_URL || 'http://notification-service:5003', - serviceName: 'notification-service', - timeout: 5000, - enableCircuitBreaker: true, -}); - -// Usage -await notificationClient.post('/api/v1/notifications', { - userId, - message, -}); -``` - -### gRPC Service - -```typescript -// gRPC server implementation -import { UserGrpcServer } from './user.grpc.service'; - -const grpcServer = new UserGrpcServer(userService); -grpcServer.start(50051); - -// gRPC client -import { GrpcClient } from '../../core/clients/grpc-client'; - -const userGrpcClient = new GrpcClient({ - protoPath: './proto/user_service.proto', - packageName: 'goodgo.user.v1', - serviceName: 'UserService', - serverUrl: 'localhost:50051', -}); - -const user = await userGrpcClient.call('getUser', { user_id: '123' }); -``` - -### GraphQL Service - -```typescript -// GraphQL client -import { GraphQLServiceClient } from '../../core/clients/graphql-client'; - -const userGraphQLClient = new GraphQLServiceClient({ - endpoint: 'http://user-service:5002/graphql', -}); - -const GET_USER_QUERY = ` - query GetUser($id: ID!) { - user(id: $id) { - id - email - name - } - } -`; - -const user = await userGraphQLClient.query(GET_USER_QUERY, { id: '123' }); -``` - -### Service-to-Service Authentication - -The authentication flow ensures secure communication between services: - -```mermaid -sequenceDiagram - participant ClientService as Client Service - participant ServiceClient as Service Client - participant Env as Environment
Variables - participant TargetService as Target Service - participant AuthMiddleware as Auth
Middleware - - ClientService->>ServiceClient: Create client instance - ServiceClient->>Env: Read INTERNAL_API_KEY - Env-->>ServiceClient: API Key - - ClientService->>ServiceClient: Make request - ServiceClient->>ServiceClient: Auto-add x-service-auth
header with API key - - ServiceClient->>TargetService: HTTP Request
(x-service-auth: token) - - TargetService->>AuthMiddleware: Extract x-service-auth - AuthMiddleware->>AuthMiddleware: Compare with
INTERNAL_API_KEY - - alt Token Matches - AuthMiddleware->>TargetService: Auth Success - TargetService->>TargetService: Process request - TargetService-->>ServiceClient: 200 OK + Data - else Token Mismatch - AuthMiddleware->>TargetService: Auth Failed - TargetService-->>ServiceClient: 403 Forbidden
(INVALID_SERVICE_AUTH) - ServiceClient->>ServiceClient: Throw ServiceError - ServiceClient-->>ClientService: Error Response - end -``` - -#### Implementation - -```typescript -// Internal auth middleware -import { internalAuthMiddleware } from '../../middlewares/internal-auth.middleware'; - -router.use('/internal', internalAuthMiddleware); - -// Client automatically adds auth header -const client = new ServiceClient({ - baseURL: 'http://service:5000', - serviceName: 'service', -}); -// X-Service-Auth header is added automatically -``` - -## Best Practices - -### Protocol Selection - -- **REST**: External APIs, browser clients, simple CRUD -- **gRPC**: Internal services, high performance, streaming -- **GraphQL**: Complex queries, mobile apps, flexible data - -### Performance - -- Use connection pooling -- Enable HTTP keep-alive -- Set appropriate timeouts -- Implement circuit breakers - -### Security - -- Always authenticate internal calls -- Use TLS/mTLS -- Store secrets securely -- Implement rate limiting - -### Observability - -- Log with correlation IDs -- Track metrics (duration, success rate) -- Add distributed tracing -- Monitor service health - -## Testing - -```typescript -// Mock service client -const mockClient = createMockServiceClient(); -mockClient.get.mockResolvedValue({ id: '123' }); -``` - -## Resources - -- [gRPC Documentation](https://grpc.io/docs/) -- [GraphQL Documentation](https://graphql.org/learn/) -- [Protocol Buffers](https://developers.google.com/protocol-buffers) -- [Resilience Patterns](./resilience-patterns.md) -- [Security](./security.md) -- Skill Source: `.cursor/skills/inter-service-communication/SKILL.md` diff --git a/docs/en/skills/microservices-development-process.md b/docs/en/skills/microservices-development-process.md deleted file mode 100644 index c97c5674..00000000 --- a/docs/en/skills/microservices-development-process.md +++ /dev/null @@ -1,660 +0,0 @@ ---- -name: microservices-development-process -description: Standard development process for creating and maintaining microservices in GoodGo platform. Use when creating new services, migrating services, refactoring services, or planning service implementations. ---- - -# Microservices Development Process - -## When to Use This Skill - -Use this skill when: -- Creating a new microservice from scratch -- Migrating or refactoring an existing service -- Planning service implementation with multiple phases -- Ensuring comprehensive coverage of all development aspects -- Need structured approach to service development - -## Development Process Overview - -The microservices development process follows these phases: -1. **Planning & Impact Analysis** - Define scope, impact, dependencies -2. **Foundation Setup** - Service structure, configs, infrastructure -3. **Core Implementation** - Business logic, APIs, data layer -4. **Integration** - Routes, middleware, external services -5. **Testing** - Unit, integration, E2E tests -6. **Documentation** - API docs, README, guides -7. **Cleanup & Verification** - Remove temporary files, verify completeness -8. **Deployment** - Staging deployment, production deployment - -### Process Flow Diagram - -This diagram shows the complete 8-phase development process with decision points and feedback loops. - -```mermaid -graph TD - Start([Start: New Service Requirements]) --> Phase1[Phase 1: Planning & Impact Analysis] - Phase1 --> ImpactCheck{Impact Analysis
Complete?} - ImpactCheck -->|No| Phase1 - ImpactCheck -->|Yes| Phase2[Phase 2: Foundation Setup] - - Phase2 --> FoundationCheck{Service Starts
& Health Check Passes?} - FoundationCheck -->|No| Phase2 - FoundationCheck -->|Yes| Phase3[Phase 3: Core Implementation] - - Phase3 --> ImplementationCheck{Business Logic
Implemented?} - ImplementationCheck -->|No| Phase3 - ImplementationCheck -->|Yes| Phase4[Phase 4: Integration] - - Phase4 --> IntegrationCheck{Routes & Middleware
Working?} - IntegrationCheck -->|No| Phase4 - IntegrationCheck -->|Yes| Phase5[Phase 5: Testing] - - Phase5 --> TestCheck{Tests Pass
& Coverage Met?} - TestCheck -->|No| Phase5 - TestCheck -->|Yes| Phase6[Phase 6: Documentation] - - Phase6 --> DocCheck{Docs
Complete?} - DocCheck -->|No| Phase6 - DocCheck -->|Yes| Phase7[Phase 7: Cleanup & Verification] - - Phase7 --> VerificationCheck{All Checks
Pass?} - VerificationCheck -->|No| Phase7 - VerificationCheck -->|Yes| Phase8[Phase 8: Deployment] - - Phase8 --> DeployCheck{Staging
Deployed?} - DeployCheck -->|No| Phase8 - DeployCheck -->|Yes| Production{Deploy to
Production?} - Production -->|Yes| ProdDeploy[Production Deployment] - Production -->|No| Complete([Complete]) - ProdDeploy --> Complete - - style Phase1 fill:#e1f5ff - style Phase2 fill:#fff4e1 - style Phase3 fill:#f0e1ff - style Phase4 fill:#e1ffe1 - style Phase5 fill:#ffe1e1 - style Phase6 fill:#e1ffff - style Phase7 fill:#fff0e1 - style Phase8 fill:#ffe1f5 - style Complete fill:#d4edda -``` - -### Detailed Phase Flow - -This diagram breaks down the tasks within each phase and shows the sequential flow between phases. - -```mermaid -graph LR - subgraph Planning["Phase 1: Planning"] - P1A[Define Scope] --> P1B[Impact Analysis] - P1B --> P1C[Dependencies Map] - P1C --> P1D[Acceptance Criteria] - end - - subgraph Foundation["Phase 2: Foundation"] - F2A[Copy Template] --> F2B[Configure Package] - F2B --> F2C[Setup Database] - F2C --> F2D[Configure Docker] - F2D --> F2E[Setup Traefik] - end - - subgraph Implementation["Phase 3: Implementation"] - I3A[DTOs] --> I3B[Repository] - I3B --> I3C[Service] - I3C --> I3D[Controller] - I3D --> I3E[Module] - end - - subgraph Integration["Phase 4: Integration"] - IN4A[Register Routes] --> IN4B[Setup Middleware] - IN4B --> IN4C[External Services] - IN4C --> IN4D[Health Checks] - end - - subgraph Testing["Phase 5: Testing"] - T5A[Unit Tests] --> T5B[Integration Tests] - T5B --> T5C[E2E Tests] - T5C --> T5D[Coverage Check] - end - - subgraph Documentation["Phase 6: Documentation"] - D6A[README] --> D6B[API Docs] - D6B --> D6C[Architecture Docs] - end - - subgraph Cleanup["Phase 7: Cleanup"] - C7A[Remove Temp Files] --> C7B[Update References] - C7B --> C7C[Verify Everything] - end - - subgraph Deployment["Phase 8: Deployment"] - DEP8A[Staging] --> DEP8B[Verification] - DEP8B --> DEP8C[Production] - end - - Planning --> Foundation - Foundation --> Implementation - Implementation --> Integration - Integration --> Testing - Testing --> Documentation - Documentation --> Cleanup - Cleanup --> Deployment - - style Planning fill:#e1f5ff - style Foundation fill:#fff4e1 - style Implementation fill:#f0e1ff - style Integration fill:#e1ffe1 - style Testing fill:#ffe1e1 - style Documentation fill:#e1ffff - style Cleanup fill:#fff0e1 - style Deployment fill:#ffe1f5 -``` - -## Phase 1: Planning & Impact Analysis - -### Scope Definition - -Define clearly: -- **Service Purpose**: What business capability does it provide? -- **API Surface**: What endpoints are needed? -- **Data Models**: What data structures are required? -- **Dependencies**: What services/packages does it depend on? -- **Breaking Changes**: Any backward compatibility concerns? - -### Impact Analysis Checklist - -Before starting implementation, identify all affected areas: - -**Files to Create:** -- [ ] Service directory: `services/service-name/` -- [ ] Prisma schema: `services/service-name/prisma/schema.prisma` -- [ ] Dockerfile: `services/service-name/Dockerfile` -- [ ] Service README: `services/service-name/README.md` - -**Files to Update:** -- [ ] Root `package.json` workspace config -- [ ] `deployments/local/docker-compose.yml` - Add service -- [ ] `infra/traefik/dynamic/routes.yml` - Add routes -- [ ] `.github/workflows/ci-*.yml` - Add CI workflow (if needed) -- [ ] Documentation: `docs/en/guides/`, `docs/vi/guides/` -- [ ] Scripts: `scripts/db/*.sh`, `scripts/dev/*.sh` (if service-specific) - -**Infrastructure Changes:** -- [ ] Database: New schema/tables -- [ ] Redis: New cache keys/patterns (if needed) -- [ ] Traefik: New routes and services -- [ ] Observability: New service metrics/traces - -**Dependencies:** -- [ ] External: Database, Redis, third-party APIs -- [ ] Internal: Shared packages (@goodgo/logger, @goodgo/types, etc.) -- [ ] Other Services: List dependent services - -## Phase 2: Foundation Setup - -### Service Structure Creation - -**Template Usage:** -```bash -cp -r services/_template services/new-service-name -cd services/new-service-name -# Update package.json name to @goodgo/new-service-name -``` - -**Required Files:** -- Service structure from template -- `package.json` with correct name and dependencies -- `src/config/app.config.ts` - Configuration with Zod validation -- `.env.example` - Environment variables template -- `prisma/schema.prisma` - Database schema -- `Dockerfile` - Container configuration -- `jest.config.ts` - Test configuration - -### Database Setup - -```bash -# Create initial migration -cd services/service-name -pnpm prisma migrate dev --name init -pnpm prisma generate -``` - -### Docker & Infrastructure - -**Docker Compose Integration:** -Add service to `deployments/local/docker-compose.yml` with: -- Build context and dockerfile -- Environment variables -- Traefik labels for routing -- Health check configuration - -**Traefik Routes:** -Update `infra/traefik/dynamic/routes.yml` with: -- Router rules (PathPrefix) -- Service configuration -- Middleware chain (CORS, rate-limit, auth) - -### Acceptance Criteria for Phase 2 - -- [ ] Service directory created from template -- [ ] `package.json` configured correctly -- [ ] Environment variables defined -- [ ] Prisma schema created and migration run -- [ ] Service starts: `pnpm dev` (health check passes) -- [ ] Docker build succeeds -- [ ] Service accessible via Traefik -- [ ] No TypeScript errors: `pnpm typecheck` - -## Phase 3: Core Implementation - -### Module Structure - -Each feature module follows this pattern: -``` -modules/feature-name/ -├── feature.controller.ts # HTTP handlers -├── feature.service.ts # Business logic -├── feature.repository.ts # Data access -├── feature.dto.ts # Validation schemas (Zod) -├── feature.module.ts # Module registration -└── index.ts # Public exports -``` - -### Implementation Flow - -This diagram shows the step-by-step implementation order for each feature module within Phase 3. - -```mermaid -graph TD - Start[Start Implementation] --> DTOs[1. Create DTOs
Zod Validation Schemas] - DTOs --> Repo[2. Create Repository
Prisma Data Access] - Repo --> Service[3. Create Service
Business Logic] - Service --> Controller[4. Create Controller
HTTP Handlers] - Controller --> Module[5. Create Module
Wire Up Components] - Module --> Test[Manual Testing] - Test --> Pass{Tests Pass?} - Pass -->|No| Repo - Pass -->|Yes| Next[Next Feature Module] - - style DTOs fill:#e1f5ff - style Repo fill:#fff4e1 - style Service fill:#f0e1ff - style Controller fill:#e1ffe1 - style Module fill:#ffe1e1 -``` - -### Implementation Order - -1. **DTOs** - Zod schemas for request/response validation -2. **Repository** - Prisma-based data access, CRUD operations -3. **Service** - Business logic, error handling, validation -4. **Controller** - HTTP request handling, standardized responses -5. **Module** - Wire up components, export router - -### Code Patterns - -**Repository:** Extend base Repository, use Prisma client for data access -**Service:** Inject repository, implement business logic, use logger -**Controller:** Handle HTTP requests, validate with DTOs, call services -**Module:** Wire up dependencies, export router - -### Acceptance Criteria for Phase 3 - -- [ ] All DTOs defined with Zod validation -- [ ] Repository methods implemented -- [ ] Service business logic implemented -- [ ] Controllers handle requests correctly -- [ ] Modules configured properly -- [ ] No TypeScript errors -- [ ] Manual API testing successful - -## Phase 4: Integration - -### Route Registration - -Update `src/routes/index.ts`: -- Import feature modules -- Create router instances -- Register routes with path prefixes -- Mount to main app with `/api/v1/service-name` prefix - -### Middleware Setup - -**Required Middlewares (in order):** -1. Correlation middleware -2. Logging middleware -3. Metrics middleware -4. CORS middleware -5. Rate limiting middleware -6. Authentication middleware (if needed) -7. Error middleware (always last) - -### External Service Integration - -- HTTP clients: Use `@goodgo/http-client` for external APIs -- Redis caching: Implement cache patterns for frequently accessed data -- Error handling: Handle external service failures gracefully - -### Acceptance Criteria for Phase 4 - -- [ ] All routes registered and accessible -- [ ] Middlewares applied in correct order -- [ ] Error handling works for all scenarios -- [ ] External services integrated (if any) -- [ ] Caching implemented (if needed) -- [ ] Health check endpoint works: `/health` - -## Phase 5: Testing - -### Test Structure - -**Unit Tests:** Next to source files (`*.test.ts`), mock all dependencies -**Integration Tests:** `src/__tests__/`, test component interactions -**E2E Tests:** `src/__tests__/*.e2e.ts`, test full API workflows - -### Test Coverage Targets - -- Minimum: 70% coverage (branches, functions, lines, statements) -- Critical paths: 90%+ coverage -- Repositories: 80%+ coverage -- Services: 80%+ coverage -- Controllers: 70%+ coverage - -### Testing Checklist - -**Unit Tests:** -- [ ] Repository tests: All CRUD operations -- [ ] Service tests: Business logic, error handling -- [ ] Controller tests: Request/response handling -- [ ] DTO tests: Validation rules - -**Integration Tests:** -- [ ] Module integration: Controller → Service → Repository -- [ ] Database operations: Real Prisma client with test DB -- [ ] Middleware chain: Request flow through middlewares - -**E2E Tests:** -- [ ] API endpoints: Full request/response cycle -- [ ] Authentication: Protected routes -- [ ] Error scenarios: 400, 401, 403, 404, 500 -- [ ] Health checks: /health endpoint - -### Acceptance Criteria for Phase 5 - -- [ ] All unit tests pass: `pnpm test` -- [ ] Integration tests pass -- [ ] E2E tests pass -- [ ] Coverage meets thresholds: `pnpm test:coverage` -- [ ] No test warnings or errors -- [ ] Tests run in CI pipeline successfully - -## Phase 6: Documentation - -### Required Documentation - -**Service README:** -- Service overview (bilingual EN/VI) -- Features list -- Prerequisites -- Quick start guide -- Configuration reference (environment variables table) -- API endpoints overview -- Development guide -- Testing instructions - -**API Documentation:** -- Swagger/OpenAPI spec: `src/docs/swagger.ts` -- Document all endpoints -- Request/response schemas -- Examples - -**Architecture Documentation (if complex):** -- `ARCHITECTURE.en.md` / `ARCHITECTURE.vi.md` -- System design, data flow, component interactions - -### Documentation Checklist - -- [ ] README is comprehensive and bilingual -- [ ] Swagger docs accessible: `/api-docs` -- [ ] All endpoints appear in Swagger -- [ ] Examples are clear and accurate -- [ ] Environment variables documented -- [ ] Architecture docs created (if needed) - -### Acceptance Criteria for Phase 6 - -- [ ] README is comprehensive and bilingual -- [ ] Swagger docs accessible: `/api-docs` -- [ ] All endpoints documented with examples -- [ ] Documentation reviewed and accurate - -## Phase 7: Cleanup & Verification - -### Verification Process Flow - -This diagram illustrates the cleanup and verification workflow for Phase 7, including the decision point for migrations and the comprehensive verification steps. - -```mermaid -graph TD - Start[Start Cleanup] --> Remove[Remove Temporary Files] - Remove --> Update{Is Migration?} - Update -->|Yes| RefUpdate[Update References
grep & replace] - Update -->|No| Verify[Run Verification] - RefUpdate --> Verify - - Verify --> TypeCheck[TypeScript Check] - TypeCheck --> LintCheck[Lint Check] - LintCheck --> TestCheck[Test Check] - TestCheck --> BuildCheck[Build Check] - BuildCheck --> DockerCheck[Docker Build] - DockerCheck --> HealthCheck[Health Check] - HealthCheck --> TraefikCheck[Traefik Check] - TraefikCheck --> AllPass{All Pass?} - - AllPass -->|No| Fix[Fix Issues] - Fix --> Verify - AllPass -->|Yes| Complete[Phase Complete] - - style Remove fill:#ffe1e1 - style RefUpdate fill:#fff4e1 - style Verify fill:#e1ffe1 - style Complete fill:#d4edda -``` - -### Cleanup Checklist - -**Remove Temporary Files:** -- [ ] Remove backup directories (e.g., `service-name.backup/`) -- [ ] Remove temporary status files (e.g., `*_STATUS.md`, `*_CHECKLIST.md`) -- [ ] Remove debug/scratch files -- [ ] Clean up unused imports -- [ ] Remove commented-out code - -**Reference Updates (for migrations/renames):** -```bash -# Find all references -grep -r "old-service-name" . --exclude-dir=node_modules --exclude-dir=.git - -# Update checklist: -- [ ] Package names: `@goodgo/old-name` → `@goodgo/new-name` -- [ ] Service paths: `services/old-name` → `services/new-name` -- [ ] Docker images: `goodgo/old-name` → `goodgo/new-name` -- [ ] Deployment names: `old-name` → `new-name` -- [ ] Environment variables updated -- [ ] CI/CD workflows updated -- [ ] Scripts updated (if needed) -- [ ] Documentation updated (except historical context) -``` - -### Verification Steps - -**Comprehensive Verification:** -```bash -# 1. Service starts successfully -pnpm dev && curl http://localhost:5000/health - -# 2. Type checking passes -pnpm typecheck - -# 3. Linting passes -pnpm lint - -# 4. Tests pass with coverage -pnpm test && pnpm test:coverage - -# 5. Build succeeds -pnpm build - -# 6. Docker build succeeds -docker build -t service-name . - -# 7. Service accessible via Traefik -curl http://localhost/api/v1/service-name/health - -# 8. No broken references (if migration) -grep -r "old-reference" . --exclude-dir=node_modules -``` - -### Final Verification Checklist - -**Code Quality:** -- [ ] No TypeScript errors -- [ ] No linting errors -- [ ] No unused imports/variables -- [ ] Code follows project conventions -- [ ] Comments are clear (bilingual if needed) - -**Functionality:** -- [ ] Service starts without errors -- [ ] Health check works -- [ ] All API endpoints functional -- [ ] Database operations work -- [ ] External integrations work (if any) - -**Testing:** -- [ ] All tests pass -- [ ] Coverage meets requirements -- [ ] E2E tests verify full workflows - -**Documentation:** -- [ ] README is complete and accurate -- [ ] API documentation is up-to-date -- [ ] Code comments are helpful - -**Infrastructure:** -- [ ] Docker image builds -- [ ] Service works in Docker Compose -- [ ] Traefik routes configured correctly -- [ ] Environment variables documented - -**Cleanup:** -- [ ] Temporary files removed -- [ ] All references updated (if migration) -- [ ] No orphaned files - -### Acceptance Criteria for Phase 7 - -- [ ] All cleanup tasks completed -- [ ] All verification steps pass -- [ ] No broken references or links -- [ ] Code is production-ready -- [ ] Documentation is complete - -## Phase 8: Deployment - -### Staging Deployment - -**Pre-deployment Checklist:** -- [ ] Database migrations tested: `pnpm prisma migrate deploy` -- [ ] Environment variables configured in staging -- [ ] Kubernetes manifests reviewed -- [ ] Secrets configured in Kubernetes -- [ ] Health checks configured - -**Deployment Steps:** -```bash -# 1. Build and push Docker image -docker build -t goodgo/service-name:latest . -docker push goodgo/service-name:latest - -# 2. Apply Kubernetes configs -kubectl apply -f deployments/staging/kubernetes/service-name.yaml -kubectl apply -f deployments/staging/kubernetes/service-name-configmap.yaml - -# 3. Wait for rollout -kubectl rollout status deployment/service-name -n staging - -# 4. Verify deployment -kubectl get pods -n staging -l app=service-name -curl https://staging-api.example.com/api/v1/service-name/health -``` - -### Production Deployment - -**Pre-production Checklist:** -- [ ] Staging tests passed -- [ ] Database backup created -- [ ] Rollback plan documented -- [ ] Monitoring dashboards ready -- [ ] Alerting configured - -### Acceptance Criteria for Phase 8 - -- [ ] Service deployed to staging successfully -- [ ] All staging tests pass -- [ ] Monitoring shows healthy metrics -- [ ] Production deployment completed (if applicable) -- [ ] Post-deployment verification successful - -## Rollback Strategy - -### When to Rollback - -- Critical errors in staging/production -- Performance degradation -- Data integrity issues -- Security vulnerabilities discovered - -### Rollback Steps - -```bash -# 1. Identify previous working version -kubectl rollout history deployment/service-name -n staging - -# 2. Rollback to previous version -kubectl rollout undo deployment/service-name -n staging - -# 3. Verify rollback -kubectl rollout status deployment/service-name -n staging - -# 4. Database rollback (if needed) -# Revert migrations if schema changes were made -``` - -## Best Practices Summary - -1. **Always Plan First**: Complete impact analysis before coding -2. **Follow Phases**: Don't skip verification steps -3. **Test Early**: Write tests alongside implementation -4. **Document as You Go**: Don't leave documentation for the end -5. **Clean Up Regularly**: Remove temporary files during development -6. **Verify Comprehensively**: Use checklists to ensure nothing is missed -7. **Plan for Rollback**: Always have a rollback strategy - -## Common Pitfalls to Avoid - -1. **Skipping Impact Analysis**: Leads to missing updates in scripts/configs -2. **No Verification Steps**: Misses broken references or incomplete implementation -3. **Deferring Cleanup**: Accumulates technical debt -4. **Incomplete Testing**: Missing edge cases and error scenarios -5. **Poor Documentation**: Makes maintenance difficult -6. **No Rollback Plan**: Difficult to recover from failures - -## Resources - -- [Project Rules](../project-rules/SKILL.md) - Architecture and conventions -- [API Design](../api-design/SKILL.md) - API design patterns -- [Testing Patterns](../testing-patterns/SKILL.md) - Testing best practices -- [Documentation](../documentation/SKILL.md) - Documentation guidelines -- [Database Prisma](../database-prisma/SKILL.md) - Prisma patterns -- Service Template: `services/_template/` diff --git a/docs/en/skills/middleware-patterns.md b/docs/en/skills/middleware-patterns.md deleted file mode 100644 index b5af1845..00000000 --- a/docs/en/skills/middleware-patterns.md +++ /dev/null @@ -1,413 +0,0 @@ ---- -name: middleware-patterns -description: Express middleware patterns and best practices for GoodGo microservices. Use when creating custom middleware, organizing middleware chains, handling request/response transformation, or implementing cross-cutting concerns. ---- - -# Middleware Patterns - -## When to Use This Skill - -Use this skill when: -- Creating custom Express middleware -- Organizing middleware chains and ordering -- Implementing authentication/authorization middleware -- Creating request/response transformation middleware -- Handling cross-cutting concerns (logging, metrics, validation) -- Implementing async middleware patterns -- Testing middleware implementations - -## Core Concepts - -### Middleware Function Signature - -Express middleware functions have this signature: - -```typescript -(req: Request, res: Response, next: NextFunction) => void | Promise -``` - -### Middleware Types - -1. **Application-level**: Applied to all routes (`app.use()`) -2. **Router-level**: Applied to specific routes (`router.use()`) -3. **Route-level**: Applied to specific route handlers - -### Middleware Execution Order - -**Critical**: Middleware order matters! Execution flows top-to-bottom: - -``` -Request → Middleware 1 → Middleware 2 → ... → Route Handler → Response -``` - -The following diagram illustrates the complete middleware chain flow in GoodGo services: - -```mermaid -flowchart TD - Start([HTTP Request]) --> Security["Security Middleware
Helmet, CORS"] - Security --> RateLimit["Rate Limiting
Middleware"] - RateLimit --> Correlation["Correlation ID
Middleware"] - Correlation --> BodyParsing["Body Parsing
JSON, URLEncoded, Cookies"] - BodyParsing --> Logging["Request Logging
Middleware"] - Logging --> Metrics["Metrics Collection
Middleware"] - Metrics --> Routes["Route Handlers"] - Routes -->|Success| Response([HTTP Response]) - Routes -->|Error| ErrorHandler["Error Handler
Middleware"] - Routes -->|Not Found| NotFound["Not Found
Handler"] - ErrorHandler --> Response - NotFound --> Response -``` - -## Patterns - -### Middleware Chain Order - -Standard middleware order in GoodGo services: - -```typescript -// 1. Security (Helmet, CORS) -app.use(helmet()); -app.use(cors({ ... })); - -// 2. Rate Limiting -app.use('/api', rateLimitMiddleware); - -// 3. Correlation ID (early for tracing) -app.use(correlationMiddleware()); - -// 4. Body Parsing -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); -app.use(cookieParser()); - -// 5. Request Logging -app.use(loggerMiddleware); - -// 6. Metrics -app.use(metricsMiddleware); - -// 7. Routes -app.use(createRouter()); - -// 8. Error Handling (ALWAYS LAST) -app.use(notFoundHandler); -app.use(errorHandler); -``` - -### Correlation Middleware Pattern - -Adds correlation ID for request tracing: - -```typescript -export const correlationMiddleware = () => { - return (req: Request, res: Response, next: NextFunction) => { - const correlationId = req.headers['x-correlation-id'] || generateId(); - req.correlationId = correlationId; - res.setHeader('x-correlation-id', correlationId); - next(); - }; -}; -``` - -### Authentication Middleware Pattern - -Verifies JWT tokens and attaches user to request: - -```typescript -export const authenticate = () => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - const token = extractToken(req); - if (!token) { - return res.status(401).json({ error: 'Unauthorized' }); - } - - const payload = await jwtService.verify(token); - req.user = payload; - next(); - } catch (error) { - return res.status(401).json({ error: 'Invalid token' }); - } - }; -}; -``` - -The following sequence diagram illustrates the authentication middleware flow: - -```mermaid -sequenceDiagram - participant Client - participant AuthMW as Authentication Middleware - participant JWTService as JWT Service - participant RouteHandler as Route Handler - - Client->>AuthMW: HTTP Request with Token - AuthMW->>AuthMW: Extract token from headers - alt Token exists - AuthMW->>JWTService: Verify token - alt Token valid - JWTService-->>AuthMW: Payload (user data) - AuthMW->>AuthMW: Attach user to req.user - AuthMW->>RouteHandler: next() - Continue - RouteHandler->>Client: HTTP Response (200) - else Token invalid - JWTService-->>AuthMW: Verification error - AuthMW->>Client: HTTP Response (401 Unauthorized) - end - else No token - AuthMW->>Client: HTTP Response (401 Unauthorized) - end -``` - -### Validation Middleware Pattern - -Validates request data using Zod: - -```typescript -export const validateDto = (schema: AnyZodObject, property: 'body' | 'query' | 'params' = 'body') => { - return (req: Request, res: Response, next: NextFunction) => { - try { - const validatedData = schema.parse(req[property]); - (req as any)[property] = validatedData; - next(); - } catch (error) { - if (error instanceof ZodError) { - return res.status(400).json({ - success: false, - error: { - code: 'VALIDATION_ERROR', - details: error.errors, - }, - }); - } - next(error); - } - }; -}; -``` - -The following sequence diagram shows the validation middleware flow: - -```mermaid -sequenceDiagram - participant Client - participant ValidateMW as Validation Middleware - participant ZodSchema as Zod Schema - participant RouteHandler as Route Handler - - Client->>ValidateMW: HTTP Request with Data - ValidateMW->>ValidateMW: Extract data from req[property] - ValidateMW->>ZodSchema: schema.parse(data) - alt Validation successful - ZodSchema-->>ValidateMW: Validated data - ValidateMW->>ValidateMW: Replace req[property] with validated data - ValidateMW->>RouteHandler: next() - Continue - RouteHandler->>Client: HTTP Response (200) - else Validation failed - ZodSchema-->>ValidateMW: ZodError with details - ValidateMW->>ValidateMW: Format error response - ValidateMW->>Client: HTTP Response (400 Validation Error) - end -``` - -### Conditional Middleware - -Apply middleware conditionally: - -```typescript -const conditionalAuth = (options: { optional?: boolean } = {}) => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - const token = extractToken(req); - if (token) { - const payload = await jwtService.verify(token); - req.user = payload; - } else if (!options.optional) { - return res.status(401).json({ error: 'Unauthorized' }); - } - next(); - } catch (error) { - if (!options.optional) { - return res.status(401).json({ error: 'Invalid token' }); - } - next(); - } - }; -}; -``` - -### Async Middleware Pattern - -Handle async operations properly: - -```typescript -export const asyncMiddleware = (fn: Function) => { - return (req: Request, res: Response, next: NextFunction) => { - Promise.resolve(fn(req, res, next)).catch(next); - }; -}; - -// Usage -app.get('/users', asyncMiddleware(async (req, res) => { - const users = await userService.findAll(); - res.json({ success: true, data: users }); -})); -``` - -The following sequence diagram illustrates async middleware error handling: - -```mermaid -sequenceDiagram - participant Client - participant AsyncMW as Async Middleware Wrapper - participant AsyncHandler as Async Route Handler - participant ErrorHandler as Error Handler - - Client->>AsyncMW: HTTP Request - AsyncMW->>AsyncHandler: Execute async function - alt Async operation succeeds - AsyncHandler->>AsyncHandler: Process request - AsyncHandler->>Client: HTTP Response (200) - else Async operation fails - AsyncHandler-->>AsyncMW: Promise rejection (Error) - AsyncMW->>ErrorHandler: next(error) - ErrorHandler->>ErrorHandler: Format error response - ErrorHandler->>Client: HTTP Response (500 Error) - end -``` - -### Request/Response Transformation - -Transform request or response data: - -```typescript -export const transformResponse = () => { - return (req: Request, res: Response, next: NextFunction) => { - const originalJson = res.json.bind(res); - - res.json = function(data: any) { - const transformed = { - success: true, - data, - timestamp: new Date().toISOString(), - }; - return originalJson(transformed); - }; - - next(); - }; -}; -``` - -The following sequence diagram shows how request and response transformation middleware works: - -```mermaid -sequenceDiagram - participant Client - participant TransformMW as Transform Middleware - participant RouteHandler as Route Handler - participant Response as Response Object - - Client->>TransformMW: HTTP Request - Note over TransformMW: Intercept res.json() - TransformMW->>TransformMW: Store original res.json - TransformMW->>TransformMW: Override res.json with transformation logic - TransformMW->>RouteHandler: next() - Continue chain - - RouteHandler->>RouteHandler: Process request, Generate data - RouteHandler->>Response: res.json(rawData) - - Note over Response: Transformed res.json executes - Response->>Response: Wrap data: success, data, timestamp - Response->>Client: HTTP Response (transformed) -``` - -### Logging Middleware Pattern - -Log request details: - -```typescript -export const requestLogger = (req: Request, res: Response, next: NextFunction) => { - const startTime = Date.now(); - - res.on('finish', () => { - const duration = Date.now() - startTime; - logger.info('Request completed', { - method: req.method, - url: req.url, - statusCode: res.statusCode, - duration, - correlationId: req.correlationId, - }); - }); - - next(); -}; -``` - -The following sequence diagram illustrates how logging middleware tracks request lifecycle: - -```mermaid -sequenceDiagram - participant Client - participant LogMW as Logging Middleware - participant RouteHandler as Route Handler - participant Logger as Logger Service - participant Response as Response Object - - Client->>LogMW: HTTP Request - LogMW->>LogMW: Record startTime = Date.now() - LogMW->>RouteHandler: next() - Continue chain - - RouteHandler->>RouteHandler: Process request - RouteHandler->>Response: res.json(data) or res.send() - - Response->>Response: Set statusCode, send response - Response->>Response: Emit 'finish' event - - Response->>LogMW: 'finish' event triggered - LogMW->>LogMW: Calculate duration = Date.now() - startTime - LogMW->>Logger: logger.info('Request completed', {
method, url, statusCode,
duration, correlationId}) - Logger->>Logger: Write structured log entry - - Response->>Client: HTTP Response -``` - -## Best Practices - -1. **Order Matters**: Place middleware in correct order (security → correlation → parsing → logging → routes → errors) -2. **Error Handling**: Always handle errors and call `next(error)` for error middleware -3. **Async Support**: Wrap async middleware properly to catch promise rejections -4. **Early Returns**: Use early returns for validation failures (don't call `next()`) -5. **Request Extension**: Use TypeScript declaration merging to extend Request type -6. **Conditional Logic**: Use middleware factories for conditional middleware -7. **Reusability**: Create reusable middleware functions -8. **Performance**: Keep middleware lightweight, avoid heavy operations - -## Common Mistakes - -1. **Wrong Order**: Placing middleware in incorrect order (e.g., error handler before routes) -2. **Not Calling Next**: Forgetting to call `next()` or `next(error)` -3. **Async Errors**: Not handling promise rejections in async middleware -4. **Early Return Issues**: Calling `next()` after sending response -5. **Type Safety**: Not extending Express Request type properly -6. **Performance**: Doing heavy operations in middleware - -## Troubleshooting - -### Middleware Not Executing - -**Problem**: Middleware not being called -**Solution**: Check middleware order, ensure it's added before routes. Verify `next()` is called. - -### Async Errors Not Caught - -**Problem**: Unhandled promise rejections in async middleware -**Solution**: Use `asyncHandler` wrapper or wrap async code in try-catch with `next(error)`. - -## Resources - -- [Correlation Middleware](../../services/iam-service/src/middlewares/correlation.middleware.ts) -- [Auth Middleware](../../services/iam-service/src/middlewares/auth.middleware.ts) -- [Validation Middleware](../../services/iam-service/src/middlewares/validation.middleware.ts) -- [Error Handling](../error-handling-patterns/SKILL.md) - Error middleware patterns diff --git a/docs/en/skills/observability-monitoring.md b/docs/en/skills/observability-monitoring.md deleted file mode 100644 index 8622cc3a..00000000 --- a/docs/en/skills/observability-monitoring.md +++ /dev/null @@ -1,658 +0,0 @@ ---- -name: observability-monitoring -description: Observability and monitoring patterns for GoodGo microservices. Use when adding metrics, implementing logging, setting up tracing, creating health checks, or debugging production issues. ---- - -# Observability & Monitoring Patterns - -## When to Use This Skill - -Use this skill when: -- Setting up logging infrastructure -- Implementing metrics collection -- Adding distributed tracing -- Creating health check endpoints -- Setting up monitoring dashboards -- Debugging production issues -- Implementing alerting rules -- Analyzing performance bottlenecks - -## Core Concepts - -### Three Pillars of Observability -1. **Logs**: Event records for debugging -2. **Metrics**: Numerical measurements over time -3. **Traces**: Request flow across services - -### Tech Stack -- **Logging**: Winston, Pino -- **Metrics**: Prometheus + Grafana -- **Tracing**: OpenTelemetry + Jaeger -- **APM**: DataDog or New Relic (optional) - -### Observability Stack Architecture - -The observability stack consists of three pillars working together to provide comprehensive visibility into system behavior: - -```mermaid -graph TB - subgraph "Application Layer" - App[Microservice] - end - - subgraph "Three Pillars of Observability" - Logs[Logs
Winston/Pino] - Metrics[Metrics
Prometheus] - Traces[Traces
OpenTelemetry] - end - - subgraph "Aggregation & Storage" - Loki[Loki
Log Aggregation] - Prom[Prometheus
Metrics Storage] - Jaeger[Jaeger
Trace Storage] - end - - subgraph "Visualization & Alerting" - Grafana[Grafana
Dashboards] - AlertManager[AlertManager
Alerts] - end - - App -->|Structured Logs| Logs - App -->|HTTP Metrics| Metrics - App -->|Distributed Spans| Traces - - Logs -->|Collect| Loki - Metrics -->|Scrape /metrics| Prom - Traces -->|Export| Jaeger - - Loki -->|Query| Grafana - Prom -->|Query| Grafana - Prom -->|Alerts| AlertManager - Jaeger -->|Query| Grafana - - style App fill:#e1f5ff - style Logs fill:#fff4e1 - style Metrics fill:#e1ffe1 - style Traces fill:#ffe1f5 - style Grafana fill:#e1e1ff -``` - -## Structured Logging - -```typescript -// src/lib/logger.ts -import winston from 'winston'; - -const logFormat = winston.format.combine( - winston.format.timestamp(), - winston.format.errors({ stack: true }), - winston.format.json() -); - -export const logger = winston.createLogger({ - level: process.env.LOG_LEVEL || 'info', - format: logFormat, - defaultMeta: { - service: process.env.SERVICE_NAME || 'unknown', - environment: process.env.NODE_ENV || 'development' - }, - transports: [ - new winston.transports.Console({ - format: process.env.NODE_ENV === 'development' - ? winston.format.combine( - winston.format.colorize(), - winston.format.simple() - ) - : logFormat - }), - // Production: Send to log aggregation service - ...(process.env.NODE_ENV === 'production' - ? [new winston.transports.Http({ - host: 'logs.example.com', - path: '/collect', - ssl: true - })] - : []) - ] -}); - -// Request logger middleware -export const requestLogger = (req: Request, res: Response, next: NextFunction) => { - const start = Date.now(); - - res.on('finish', () => { - const duration = Date.now() - start; - - logger.info('HTTP Request', { - method: req.method, - url: req.url, - status: res.statusCode, - duration, - ip: req.ip, - userAgent: req.get('user-agent'), - correlationId: req.headers['x-correlation-id'] - }); - }); - - next(); -}; -``` - -### Logging Flow - -The logging flow shows how requests are logged with correlation IDs and flow through the system: - -```mermaid -sequenceDiagram - participant Client - participant Service as Microservice - participant Logger as Winston/Pino Logger - participant Aggregator as Log Aggregator
(Loki) - participant Dashboard as Grafana Dashboard - - Client->>Service: HTTP Request
(with x-correlation-id) - Service->>Service: Generate/Extract
Correlation ID - Service->>Logger: Log Request Start
{correlationId, method, url} - Service->>Service: Process Request - Service->>Logger: Log Business Event
{correlationId, event, data} - Service->>Client: HTTP Response
(with x-correlation-id) - Service->>Logger: Log Request End
{correlationId, status, duration} - - Logger->>Aggregator: Send Structured Logs
(JSON format) - Aggregator->>Dashboard: Index & Store Logs - Dashboard->>Dashboard: Query by correlationId
to trace request flow -``` - -## Metrics Collection - -```typescript -// src/lib/metrics.ts -import { Registry, Counter, Histogram, Gauge } from 'prom-client'; - -export const register = new Registry(); - -// HTTP metrics -export const httpRequestDuration = new Histogram({ - name: 'http_request_duration_seconds', - help: 'Duration of HTTP requests in seconds', - labelNames: ['method', 'route', 'status'], - buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10] -}); - -export const httpRequestTotal = new Counter({ - name: 'http_requests_total', - help: 'Total number of HTTP requests', - labelNames: ['method', 'route', 'status'] -}); - -// Business metrics -export const userRegistrations = new Counter({ - name: 'user_registrations_total', - help: 'Total number of user registrations', - labelNames: ['type'] -}); - -export const activeUsers = new Gauge({ - name: 'active_users', - help: 'Number of active users', - labelNames: ['status'] -}); - -// Register metrics -register.registerMetric(httpRequestDuration); -register.registerMetric(httpRequestTotal); -register.registerMetric(userRegistrations); -register.registerMetric(activeUsers); - -// Metrics middleware -export const metricsMiddleware = (req: Request, res: Response, next: NextFunction) => { - const start = Date.now(); - - res.on('finish', () => { - const duration = (Date.now() - start) / 1000; - const route = req.route?.path || req.path; - - httpRequestDuration - .labels(req.method, route, res.statusCode.toString()) - .observe(duration); - - httpRequestTotal - .labels(req.method, route, res.statusCode.toString()) - .inc(); - }); - - next(); -}; - -// Metrics endpoint -export const metricsHandler = async (req: Request, res: Response) => { - res.set('Content-Type', register.contentType); - res.end(await register.metrics()); -}; -``` - -### Metrics Collection Flow - -Metrics are collected from services and exposed to Prometheus for monitoring and alerting: - -```mermaid -graph LR - subgraph "Service Instance" - App[Application] - Middleware[Metrics Middleware] - Registry[Prometheus Registry] - Endpoint[/metrics Endpoint] - end - - subgraph "Metrics Types" - Counter[Counter
http_requests_total] - Gauge[Gauge
active_users] - Histogram[Histogram
request_duration] - end - - subgraph "Collection" - Prometheus[Prometheus
Scraper] - end - - subgraph "Storage & Query" - PromDB[(Prometheus
Time Series DB)] - end - - subgraph "Visualization" - Grafana[Grafana
Dashboards] - Alerts[AlertManager
Rules] - end - - App -->|HTTP Request| Middleware - Middleware -->|Record| Counter - Middleware -->|Record| Histogram - App -->|Update| Gauge - - Counter --> Registry - Gauge --> Registry - Histogram --> Registry - Registry --> Endpoint - - Prometheus -->|Scrape every 15s| Endpoint - Prometheus -->|Store| PromDB - - PromDB -->|Query| Grafana - PromDB -->|Evaluate| Alerts - Alerts -->|Trigger| Grafana - - style App fill:#e1f5ff - style Prometheus fill:#ffe1e1 - style Grafana fill:#e1e1ff -``` - -## Distributed Tracing - -```typescript -// src/lib/tracing.ts -import { NodeSDK } from '@opentelemetry/sdk-node'; -import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; -import { Resource } from '@opentelemetry/resources'; -import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'; -import { JaegerExporter } from '@opentelemetry/exporter-jaeger'; - -export const initTracing = () => { - const jaegerExporter = new JaegerExporter({ - endpoint: process.env.JAEGER_ENDPOINT || 'http://localhost:14268/api/traces', - }); - - const sdk = new NodeSDK({ - resource: new Resource({ - [SemanticResourceAttributes.SERVICE_NAME]: process.env.SERVICE_NAME || 'unknown', - [SemanticResourceAttributes.SERVICE_VERSION]: process.env.SERVICE_VERSION || '1.0.0', - }), - traceExporter: jaegerExporter, - instrumentations: [getNodeAutoInstrumentations()] - }); - - sdk.start(); - - process.on('SIGTERM', () => { - sdk.shutdown() - .then(() => console.log('Tracing terminated')) - .catch((error) => console.log('Error terminating tracing', error)) - .finally(() => process.exit(0)); - }); -}; - -// Custom span creation -import { trace, SpanStatusCode } from '@opentelemetry/api'; - -export const tracedOperation = async (name: string, fn: Function) => { - const tracer = trace.getTracer('application'); - const span = tracer.startSpan(name); - - try { - const result = await fn(); - span.setStatus({ code: SpanStatusCode.OK }); - return result; - } catch (error) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message - }); - span.recordException(error); - throw error; - } finally { - span.end(); - } -}; -``` - -### Distributed Tracing Flow - -Distributed tracing tracks requests across multiple services using OpenTelemetry: - -```mermaid -sequenceDiagram - participant Client - participant Gateway as API Gateway - participant ServiceA as Service A
(User Service) - participant ServiceB as Service B
(Order Service) - participant DB as Database - participant Jaeger as Jaeger
Collector - - Client->>Gateway: Request
(Trace ID: abc123) - Gateway->>Gateway: Create Root Span
Span: gateway.request - Gateway->>ServiceA: HTTP Call
(Trace ID: abc123,
Span ID: span-1) - - ServiceA->>ServiceA: Create Child Span
Span: user.getById - ServiceA->>DB: Query User
(Trace ID: abc123,
Span ID: span-2) - DB-->>ServiceA: User Data - ServiceA->>ServiceA: End Span span-2 - ServiceA->>ServiceB: HTTP Call
(Trace ID: abc123,
Span ID: span-3) - - ServiceB->>ServiceB: Create Child Span
Span: order.getByUserId - ServiceB->>DB: Query Orders
(Trace ID: abc123,
Span ID: span-4) - DB-->>ServiceB: Orders Data - ServiceB->>ServiceB: End Span span-4 - ServiceB->>ServiceB: End Span span-3 - ServiceB-->>ServiceA: Response - ServiceA->>ServiceA: End Span span-1 - ServiceA-->>Gateway: Response - Gateway->>Gateway: End Span gateway.request - Gateway-->>Client: Final Response - - ServiceA->>Jaeger: Export Spans
(Trace ID: abc123) - ServiceB->>Jaeger: Export Spans
(Trace ID: abc123) - Gateway->>Jaeger: Export Spans
(Trace ID: abc123) - - Note over Jaeger: All spans linked by
Trace ID: abc123 -``` - -## Health Checks - -```typescript -// src/modules/health/health.controller.ts -export class HealthController { - constructor( - private prisma: PrismaClient, - private redis: Redis - ) {} - - // Liveness probe - is the service running? - async liveness(req: Request, res: Response) { - res.json({ - status: 'ok', - timestamp: new Date().toISOString() - }); - } - - // Readiness probe - is the service ready for traffic? - async readiness(req: Request, res: Response) { - const checks = await this.runHealthChecks(); - const isHealthy = Object.values(checks).every(check => check.status === 'healthy'); - - res.status(isHealthy ? 200 : 503).json({ - status: isHealthy ? 'ready' : 'not ready', - checks, - timestamp: new Date().toISOString() - }); - } - - // Detailed health check - async health(req: Request, res: Response) { - const checks = await this.runHealthChecks(); - const isHealthy = Object.values(checks).every(check => check.status === 'healthy'); - - res.status(isHealthy ? 200 : 503).json({ - status: isHealthy ? 'healthy' : 'unhealthy', - version: process.env.SERVICE_VERSION || '1.0.0', - uptime: process.uptime(), - checks, - timestamp: new Date().toISOString() - }); - } - - private async runHealthChecks() { - const checks: Record = {}; - - // Database check - try { - const start = Date.now(); - await this.prisma.$queryRaw`SELECT 1`; - checks.database = { - status: 'healthy', - responseTime: Date.now() - start - }; - } catch (error) { - checks.database = { - status: 'unhealthy', - error: error.message - }; - } - - // Redis check - try { - const start = Date.now(); - await this.redis.ping(); - checks.redis = { - status: 'healthy', - responseTime: Date.now() - start - }; - } catch (error) { - checks.redis = { - status: 'unhealthy', - error: error.message - }; - } - - // Memory check - const memUsage = process.memoryUsage(); - checks.memory = { - status: memUsage.heapUsed < 500 * 1024 * 1024 ? 'healthy' : 'warning', - heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024), - heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024), - rss: Math.round(memUsage.rss / 1024 / 1024) - }; - - return checks; - } -} -``` - -## Error Tracking - -```typescript -// src/lib/error-tracking.ts -import * as Sentry from '@sentry/node'; - -export const initErrorTracking = () => { - if (process.env.SENTRY_DSN) { - Sentry.init({ - dsn: process.env.SENTRY_DSN, - environment: process.env.NODE_ENV, - tracesSampleRate: 0.1, - beforeSend(event, hint) { - // Filter sensitive data - if (event.request?.cookies) { - delete event.request.cookies; - } - return event; - } - }); - } -}; - -// Error handler middleware -export const errorHandler = ( - err: Error, - req: Request, - res: Response, - next: NextFunction -) => { - // Log error - logger.error('Unhandled error', { - error: err.message, - stack: err.stack, - url: req.url, - method: req.method, - correlationId: req.headers['x-correlation-id'] - }); - - // Report to Sentry - Sentry.captureException(err, { - tags: { - service: process.env.SERVICE_NAME - }, - user: { - id: req.user?.id - } - }); - - // Send response - res.status(500).json({ - success: false, - error: { - code: 'INTERNAL_ERROR', - message: process.env.NODE_ENV === 'production' - ? 'Internal server error' - : err.message - } - }); -}; -``` - -## Performance Monitoring - -```typescript -// src/middlewares/performance.middleware.ts -export const performanceMiddleware = (req: Request, res: Response, next: NextFunction) => { - const start = process.hrtime.bigint(); - - res.on('finish', () => { - const end = process.hrtime.bigint(); - const duration = Number(end - start) / 1000000; // Convert to milliseconds - - // Log slow requests - if (duration > 1000) { - logger.warn('Slow request detected', { - method: req.method, - url: req.url, - duration, - threshold: 1000 - }); - } - - // Add to response header - res.set('X-Response-Time', `${duration}ms`); - }); - - next(); -}; -``` - -## Grafana Dashboard Config - -```json -{ - "dashboard": { - "title": "Service Metrics", - "panels": [ - { - "title": "Request Rate", - "targets": [{ - "expr": "rate(http_requests_total[5m])" - }] - }, - { - "title": "Request Duration", - "targets": [{ - "expr": "histogram_quantile(0.95, http_request_duration_seconds)" - }] - }, - { - "title": "Error Rate", - "targets": [{ - "expr": "rate(http_requests_total{status=~\"5..\"}[5m])" - }] - }, - { - "title": "Active Users", - "targets": [{ - "expr": "active_users" - }] - } - ] - } -} -``` - -## Alerting Rules - -```yaml -# prometheus/alerts.yml -groups: - - name: service_alerts - rules: - - alert: HighErrorRate - expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05 - for: 5m - annotations: - summary: "High error rate detected" - description: "Error rate is above 5% for 5 minutes" - - - alert: HighLatency - expr: histogram_quantile(0.95, http_request_duration_seconds) > 1 - for: 5m - annotations: - summary: "High latency detected" - description: "95th percentile latency is above 1s" - - - alert: ServiceDown - expr: up{job="service"} == 0 - for: 1m - annotations: - summary: "Service is down" - description: "Service has been down for 1 minute" -``` - -## Best Practices - -1. **Logging** - - Use structured logging (JSON format) - - Include correlation IDs for request tracing - - Log at appropriate levels (ERROR, WARN, INFO, DEBUG) - - Avoid logging sensitive data - -2. **Metrics** - - Use standard metric types (Counter, Gauge, Histogram) - - Keep cardinality low (avoid high-cardinality labels) - - Define SLIs and SLOs for critical paths - - Monitor business metrics, not just technical ones - -3. **Tracing** - - Add traces for critical operations - - Include relevant context in spans - - Sample appropriately to control costs - - Use distributed tracing for microservices - -4. **Alerting** - - Alert on symptoms, not causes - - Include runbook links in alerts - - Avoid alert fatigue with proper thresholds - - Test alerting rules regularly \ No newline at end of file diff --git a/docs/en/skills/performance-optimization.md b/docs/en/skills/performance-optimization.md deleted file mode 100644 index 6a13b6cf..00000000 --- a/docs/en/skills/performance-optimization.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -name: performance-optimization -description: Performance optimization patterns for GoodGo microservices including database query optimization, memory leak detection, profiling, connection pooling, and caching strategies. ---- - -# Performance Optimization Patterns - -## When to Use This Skill - -Use this skill when: -- Optimizing database queries -- Detecting and fixing memory leaks -- Profiling application performance -- Optimizing connection pooling -- Improving caching strategies -- Identifying N+1 query problems - -## Performance Optimization Workflow - -The performance optimization process follows a systematic approach to identify, analyze, and resolve performance bottlenecks. - -```mermaid -flowchart TD - Start([Start Optimization]) --> Identify[Identify Performance Issue] - Identify --> Monitor[Monitor Metrics] - Monitor --> Profiling[Profile Application] - Profiling --> Analyze[Analyze Results] - Analyze --> IdentifyBottleneck{Identify Bottleneck} - - IdentifyBottleneck -->|Database| OptimizeDB[Optimize Queries] - IdentifyBottleneck -->|Memory| OptimizeMem[Fix Memory Leaks] - IdentifyBottleneck -->|Network| OptimizeNet[Optimize Connections] - IdentifyBottleneck -->|Cache| OptimizeCache[Improve Caching] - - OptimizeDB --> Implement[Implement Optimization] - OptimizeMem --> Implement - OptimizeNet --> Implement - OptimizeCache --> Implement - - Implement --> Test[Test Changes] - Test --> Verify{Performance Improved?} - Verify -->|Yes| Monitor - Verify -->|No| Analyze - Monitor --> Threshold{Meets Targets?} - Threshold -->|Yes| Complete([Optimization Complete]) - Threshold -->|No| Profiling -``` - -## Query Optimization Flow - -Database query optimization is a critical aspect of performance. This flow shows how to systematically optimize queries. - -```mermaid -flowchart TD - Start([Query Performance Issue]) --> Analyze[Analyze Query Performance] - Analyze --> CheckIndexes[Check Indexes] - CheckIndexes --> Explain[Run EXPLAIN ANALYZE] - Explain --> IdentifyIssues{Identify Issues} - - IdentifyIssues -->|N+1 Queries| FixN1[Use Include/Join] - IdentifyIssues -->|Missing Index| AddIndex[Add Database Index] - IdentifyIssues -->|Slow Query| OptimizeQuery[Rewrite Query] - IdentifyIssues -->|Unbounded| AddPagination[Add Pagination] - - FixN1 --> VerifyQuery[Verify Query Performance] - AddIndex --> VerifyQuery - OptimizeQuery --> VerifyQuery - AddPagination --> VerifyQuery - - VerifyQuery --> TestQuery{Query Time < 50ms?} - TestQuery -->|Yes| Complete([Optimization Complete]) - TestQuery -->|No| Analyze - - style FixN1 fill:#e1f5e1 - style AddIndex fill:#e1f5e1 - style OptimizeQuery fill:#e1f5e1 - style AddPagination fill:#e1f5e1 -``` - -## Profiling Process - -The profiling process helps identify performance bottlenecks through systematic data collection and analysis. - -```mermaid -flowchart TD - Start([Start Profiling]) --> Setup[Setup Profiling Tools] - Setup --> ChooseTool{Choose Profiling Type} - - ChooseTool -->|CPU| CPUProf[CPU Profiling] - ChooseTool -->|Memory| MemProf[Memory Profiling] - ChooseTool -->|Database| DBProf[Database Query Profiling] - - CPUProf --> CollectCPU[Collect CPU Metrics] - MemProf --> CollectMem[Collect Memory Metrics] - DBProf --> CollectDB[Collect Query Metrics] - - CollectCPU --> Analyze[Analyze Profiling Data] - CollectMem --> Analyze - CollectDB --> Analyze - - Analyze --> IdentifyHotspots[Identify Hotspots] - IdentifyHotspots --> Prioritize[Prioritize Issues] - Prioritize --> Optimize[Optimize Critical Paths] - Optimize --> ReProfile[Re-run Profiling] - ReProfile --> Compare{Performance Improved?} - Compare -->|Yes| Complete([Profiling Complete]) - Compare -->|No| IdentifyHotspots - - style CPUProf fill:#e3f2fd - style MemProf fill:#e3f2fd - style DBProf fill:#e3f2fd -``` - -## Key Patterns - -### Database Query Optimization - -```typescript -// Avoid N+1 queries -// Bad: Multiple queries -for (const user of users) { - user.orders = await orderRepository.findByUserId(user.id); -} - -// Good: Single query with join -const users = await userRepository.findAll({ - include: { orders: true }, -}); -``` - -### Memory Profiling - -```typescript -// Monitor memory usage -const profiler = new MemoryProfiler(); -profiler.start(); // Monitor every minute -``` - -### Batch Operations - -```typescript -// Batch database operations -await batchOperations.batchCreate(items, 100); // Process 100 at a time -``` - -## Best Practices - -1. Use indexes, avoid N+1 queries -2. Monitor memory usage, detect leaks -3. Cache frequently accessed data -4. Configure connection pools appropriately -5. Profile regularly to identify bottlenecks - -## Resources - -- [Caching Patterns](./caching-patterns.md) -- [Observability & Monitoring](./observability-monitoring.md) -- Skill Source: `.cursor/skills/performance-optimization/SKILL.md` diff --git a/docs/en/skills/project-rules.md b/docs/en/skills/project-rules.md deleted file mode 100644 index 8219bf1e..00000000 --- a/docs/en/skills/project-rules.md +++ /dev/null @@ -1,400 +0,0 @@ ---- -name: project-rules -description: GoodGo Microservices Platform coding standards and architecture patterns. Use when working with services, apps, packages, or infrastructure. ---- - -# GoodGo Project Rules - -## Architecture - -**Monorepo Structure:** -- **Apps**: Next.js (web) + Flutter (mobile) -- **Services**: Node.js/TypeScript microservices (Express) -- **Packages**: Shared libraries (logger, types, http-client, auth-sdk, tracing) -- **Infrastructure**: Traefik (API Gateway), Redis, Neon PostgreSQL, Observability -- **Deployments**: Local (Docker Compose), Staging/Production (Kubernetes) - -**Template Location**: `services/_template/` - Use as starting point for new services - -### Monorepo Architecture - -```mermaid -graph TB - subgraph apps[Apps Layer] - webAdmin[web-admin
Next.js Admin] - webClient[web-client
Next.js Client] - appAdmin[app-admin
Flutter Admin] - appClient[app-client
Flutter Client] - end - - subgraph gateway[API Gateway] - traefik[Traefik
Path-based Routing] - end - - subgraph services[Services Layer] - iamService[iam-service
IAM Service] - templateService[_template
Service Template] - otherServices[Other Services
Node.js/TypeScript] - end - - subgraph packages[Shared Packages] - loggerPackage[@goodgo/logger
Centralized Logging] - typesPackage[@goodgo/types
TypeScript Types] - httpClientPackage[@goodgo/http-client
API Client] - authSdkPackage[@goodgo/auth-sdk
Auth Utilities] - tracingPackage[@goodgo/tracing
OpenTelemetry] - configPackage[@goodgo/config
Shared Configs] - end - - subgraph infrastructure[Infrastructure] - postgres[Neon PostgreSQL
Database] - redis[Redis
Cache] - prometheus[Prometheus
Metrics] - grafana[Grafana
Dashboards] - loki[Loki
Log Aggregation] - end - - subgraph deployments[Deployments] - dockerCompose[Docker Compose
Local Development] - kubernetes[Kubernetes
Staging/Production] - end - - webAdmin --> traefik - webClient --> traefik - appAdmin --> traefik - appClient --> traefik - traefik --> iamService - traefik --> otherServices - iamService --> packages - otherServices --> packages - iamService --> postgres - otherServices --> postgres - iamService --> redis - otherServices --> redis - services --> prometheus - services --> loki - prometheus --> grafana - loki --> grafana - services --> deployments -``` - -## Tech Stack - -**Frontend:** -- Next.js 14+ (App Router), TypeScript, Tailwind CSS, Zustand -- Flutter 3.x with Provider pattern -- Use `@goodgo/types` and `@goodgo/http-client` - -**Backend:** -- Node.js 20+, TypeScript 5+, Express -- Prisma ORM + Neon PostgreSQL -- Zod validation, `@goodgo/logger`, `@goodgo/tracing`, `@goodgo/auth-sdk` - -**Infrastructure:** -- Traefik (path-based routing), Redis (cache), Prometheus + Grafana + Loki - -## Project Structure - -**Service:** `src/{config,modules,middlewares,routes,main.ts}` + `prisma/` + `Dockerfile` -**Package:** `src/index.ts` + `package.json` + `tsconfig.json` + `README.md` -**App:** `src/{app,services/api,stores}` + `Dockerfile` - -### Detailed Structure Diagram - -```mermaid -graph TB - subgraph service[Service Structure] - serviceRoot[service-name/] - serviceSrc[src/] - serviceConfig[config/
Configuration] - serviceModules[modules/
Feature Modules] - serviceMiddlewares[middlewares/
Express Middlewares] - serviceRoutes[routes/
Route Definitions] - serviceMain[main.ts
Entry Point] - servicePrisma[prisma/
Schema & Migrations] - serviceDockerfile[Dockerfile
Container Definition] - servicePackageJson[package.json
Dependencies] - - serviceRoot --> serviceSrc - serviceRoot --> servicePrisma - serviceRoot --> serviceDockerfile - serviceRoot --> servicePackageJson - serviceSrc --> serviceConfig - serviceSrc --> serviceModules - serviceSrc --> serviceMiddlewares - serviceSrc --> serviceRoutes - serviceSrc --> serviceMain - end - - subgraph package[Package Structure] - packageRoot[package-name/] - packageSrc[src/] - packageIndex[index.ts
Main Export] - packagePackageJson[package.json
Package Metadata] - packageTsconfig[tsconfig.json
TypeScript Config] - packageReadme[README.md
Documentation] - - packageRoot --> packageSrc - packageRoot --> packagePackageJson - packageRoot --> packageTsconfig - packageRoot --> packageReadme - packageSrc --> packageIndex - end - - subgraph app[App Structure - Next.js] - appRoot[app-name/] - appSrc[src/] - appApp[app/
Next.js App Router] - appServicesApi[services/api/
API Clients] - appStores[stores/
State Management] - appDockerfile[Dockerfile
Container Definition] - appPackageJson[package.json
Dependencies] - - appRoot --> appSrc - appRoot --> appDockerfile - appRoot --> appPackageJson - appSrc --> appApp - appSrc --> appServicesApi - appSrc --> appStores - end - - subgraph module[Module Structure inside modules/] - moduleRoot[modules/feature-name/] - moduleController[feature.controller.ts
HTTP Handlers] - moduleService[feature.service.ts
Business Logic] - moduleRepository[feature.repository.ts
Data Access] - moduleDto[feature.dto.ts
Zod Schemas] - moduleTypes[feature.types.ts
TypeScript Types] - moduleTest[feature.controller.test.ts
Unit Tests] - - moduleRoot --> moduleController - moduleRoot --> moduleService - moduleRoot --> moduleRepository - moduleRoot --> moduleDto - moduleRoot --> moduleTypes - moduleRoot --> moduleTest - moduleController --> moduleService - moduleService --> moduleRepository - end - - serviceModules --> moduleRoot -``` - -## Naming Conventions - -- **Services/Packages**: `kebab-case` (e.g., `auth-service`, `http-client`) -- **Files**: `kebab-case.type.ts` (e.g., `user.controller.ts`) -- **Components**: `PascalCase.tsx` (React), `snake_case.dart` (Flutter) -- **Classes**: `PascalCase`, **Functions**: `camelCase`, **Constants**: `UPPER_SNAKE_CASE` -- **Package Names**: `@goodgo/package-name` - -## Workflows - -**New Service:** -1. Copy `services/_template/` -2. Update `package.json` name to `@goodgo/service-name` -3. Add to `deployments/local/docker-compose.yml` with Traefik labels -4. Configure Prisma schema if needed -5. Add health check endpoint - -**New Package:** -1. Create in `packages/`, export from `src/index.ts` -2. Add to `pnpm-workspace.yaml` -3. Use TypeScript strict mode - -**Dependencies:** -```bash -pnpm --filter @goodgo/service-name add package-name -pnpm --filter @goodgo/service-name add @goodgo/logger # workspace -pnpm --filter @goodgo/service-name add -D @types/pkg # dev -``` - -**Database:** -```bash -pnpm --filter @goodgo/service-name prisma migrate dev -pnpm --filter @goodgo/service-name prisma generate -``` - -## Code Standards - -**TypeScript:** -- Strict mode, no `any` (use `unknown`) -- Zod for runtime validation -- Export shared types from `@goodgo/types` - -**API Responses:** -```typescript -// Success: { success: true, data: any } -// Error: { success: false, error: { code, message, details? } } -``` - -**Logging:** -```typescript -import { logger } from '@goodgo/logger'; -logger.info('Message', { context }); -logger.error('Error', { error, context }); -``` - -**Environment:** -- Use `.env.example` template, never commit `.env` -- Validate with Zod at startup -- Document all vars in README - -## Testing - -- **Unit**: Place tests next to source (`*.test.ts`), use Jest, mock dependencies, >80% coverage -- **Integration**: Test API endpoints, use test database, cleanup after -- **Commands**: `pnpm test`, `pnpm --filter @goodgo/service-name test`, `pnpm test:coverage` - -## Docker - -**Multi-stage Build Pattern:** -```dockerfile -FROM node:20-alpine AS builder -# ... build stage -FROM node:20-alpine -# ... production stage with non-root user -``` - -**Image Naming:** `goodgo/service-name:version` (semantic versioning) - -## Git Workflow - -**Branches:** `feature/`, `fix/`, `hotfix/`, `release/` - -**Commits:** Conventional Commits format -``` -type(scope): subject -``` -Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` - -**PRs:** Use template, link issues, ensure CI passes, squash merge to main - -## CI/CD - -**GitHub Actions:** PR (lint, test, build) → `develop` (staging) → `main` (production) - -**Deployment Checklist:** Tests pass, no lint errors, env vars set, migrations applied, docs updated, monitoring configured - -## Security - -**Auth:** JWT (15min access, 7d refresh), httpOnly cookies, use `@goodgo/auth-sdk` -**Authorization:** RBAC, check permissions at service level, middleware for routes -**Data:** bcrypt (cost 12), HTTPS, sanitize inputs, Zod validation -**Secrets:** Environment variables, Kubernetes secrets, never hardcode, rotate regularly - -## Performance - -**Backend:** Redis caching, connection pooling, pagination, database indexes, rate limiting -**Frontend:** Next.js Image optimization, code splitting, lazy loading, React.memo, bundle optimization -**Database:** Prisma optimization, indexes, transactions, soft deletes - -## Observability - -**Metrics:** Prometheus (request count, duration, errors), set alerts -**Logging:** `@goodgo/logger` with trace IDs, levels (error, warn, info, debug), Loki aggregation -**Tracing:** OpenTelemetry via `@goodgo/tracing`, trace cross-service requests - -## Documentation - -**Code:** JSDoc for public APIs, inline comments for complex logic, README per service/package -**API:** OpenAPI/Swagger specs in `docs/api/openapi/`, document endpoints with examples -**Architecture:** System design in `docs/architecture/`, service communication, data flows, ADRs - -## Architecture Patterns - -**Modular Structure:** Controller → Service → Repository pattern -**DTO Validation:** Zod schemas with type inference -**Error Handling:** Custom error classes, global error middleware -**Dependency Injection:** Constructor injection for testability - -**Example Module:** -```typescript -// DTO with Zod -export const CreateFeatureDto = z.object({ - name: z.string().min(1), - email: z.string().email() -}); -export type CreateFeatureDto = z.infer; - -// Controller -export class FeatureController { - constructor(private service: FeatureService) {} - async create(req: Request, res: Response, next: NextFunction) { - try { - const dto = CreateFeatureDto.parse(req.body); - const result = await this.service.create(dto); - res.json({ success: true, data: result }); - } catch (error) { next(error); } - } -} - -// Service -export class FeatureService { - constructor(private repository: FeatureRepository) {} - async create(dto: CreateFeatureDto) { - return this.repository.create(dto); - } -} - -// Repository -export class FeatureRepository extends BaseRepository { - async create(data: CreateFeatureDto) { - return this.prisma.feature.create({ data }); - } -} -``` - -## Deployment & Traefik - -**Service Registration:** -Services are deployed via `deployments/local/docker-compose.yml` and auto-discovered by Traefik: - -```yaml -services: - my-service: - build: - context: ../.. - dockerfile: services/my-service/Dockerfile - labels: - - "traefik.enable=true" - - "traefik.http.routers.my-service.rule=PathPrefix(`/api/v1/my-service`)" - - "traefik.http.services.my-service.loadbalancer.server.port=5002" - - "traefik.http.services.my-service.loadbalancer.healthcheck.path=/health/live" -``` - -**Traefik Configuration:** -- **Location**: `infra/traefik/` (platform-level, not per-service) -- **Static Config**: `traefik.yml` - Entry points, providers, dashboard -- **Dynamic Config**: `dynamic/middlewares.yml`, `dynamic/routes.yml` -- **Dashboard**: http://localhost:8080 - -**Access Points:** -- API: `http://localhost/api/v1/service-name` -- Health: `http://localhost/api/v1/service-name/health` -- Docs: `http://localhost/api/v1/service-name/api-docs` - -## Troubleshooting - -**Common Issues:** -- Port conflicts: Check `deployments/local/docker-compose.yml` -- Database: Verify `DATABASE_URL` in `.env.local` -- Module not found: Run `pnpm install` -- Type errors: Run `pnpm --filter @goodgo/service-name prisma generate` - -**Debug:** -```bash -cd deployments/local -docker-compose logs -f service-name -docker-compose ps -docker-compose up -d --build -``` - -## Resources - -- [Architecture Docs](../../docs/architecture/) -- [API Specs](../../docs/api/openapi/) -- [Development Guide](../../docs/guides/development.md) -- [Deployment Guide](../../docs/guides/deployment.md) -- [Neon Database Guide](../../docs/guides/neon-database.md) -- [Contributing Guide](../../CONTRIBUTING.md) diff --git a/docs/en/skills/repository-pattern.md b/docs/en/skills/repository-pattern.md deleted file mode 100644 index b1a41d84..00000000 --- a/docs/en/skills/repository-pattern.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -name: repository-pattern -description: Repository pattern implementation and best practices for GoodGo microservices. Use when implementing data access layers, extending BaseRepository, writing database queries, handling transactions, or optimizing database operations. ---- - -# Repository Pattern - -## When to Use This Skill - -Use this skill when: -- Implementing data access layers for new modules -- Extending BaseRepository for specific entity types -- Writing custom database queries -- Handling database transactions -- Optimizing database queries and operations -- Testing repository implementations -- Organizing data access code - -## Core Concepts - -### Repository Pattern Benefits - -1. **Abstraction**: Separates business logic from data access -2. **Testability**: Easy to mock repositories for testing -3. **Maintainability**: Centralized database operations -4. **Consistency**: Standardized CRUD operations -5. **Type Safety**: TypeScript generics provide type safety - -### Repository Architecture - -The repository pattern creates an abstraction layer between the service layer and data access layer, providing a clean separation of concerns. - -```mermaid -graph TB - Controller["Controller
(HTTP Handler)"] --> Service["Service Layer
(Business Logic)"] - Service --> Repository["Repository
(Data Access)"] - Repository --> Prisma["Prisma Client
(ORM)"] - Prisma --> Database[("Database
(PostgreSQL)")] - - style Controller fill:#e1f5ff - style Service fill:#fff4e1 - style Repository fill:#e8f5e9 - style Prisma fill:#f3e5f5 - style Database fill:#ffebee -``` - -### BaseRepository Class - -The `BaseRepository` abstract class provides common database operations that can be extended: - -- `findById(id)` - Find entity by ID -- `findByUnique(field, value)` - Find by unique field -- `findAll(options)` - Find all with filtering, pagination, sorting -- `create(data)` - Create new entity -- `update(id, data)` - Update entity -- `delete(id)` - Delete entity -- `count(where)` - Count entities -- `exists(id)` - Check if entity exists -- `transaction(callback)` - Execute transaction - -### Class Hierarchy - -Repositories extend the `BaseRepository` abstract class, inheriting common CRUD operations while allowing custom query methods. - -```mermaid -classDiagram - class BaseRepository { - <> - #prisma: PrismaClient - #modelName: string - +findById(id: string) Promise~T~null~ - +findByUnique(field: string, value: any) Promise~T~null~ - +findAll(options?: any) Promise~T[]~ - +create(data: CreateInput) Promise~T~ - +update(id: string, data: UpdateInput) Promise~T~ - +delete(id: string) Promise~boolean~ - +count(where?: any) Promise~number~ - +exists(id: string) Promise~boolean~ - +transaction(callback: Function) Promise~R~ - } - - class IRepository { - <> - +findById(id: string) Promise~T~null~ - +findByUnique(field: string, value: any) Promise~T~null~ - +findAll(options?: any) Promise~T[]~ - +create(data: CreateInput) Promise~T~ - +update(id: string, data: UpdateInput) Promise~T~ - +delete(id: string) Promise~boolean~ - +count(where?: any) Promise~number~ - +exists(id: string) Promise~boolean~ - } - - class UserRepository { - +findByEmail(email: string) Promise~User~null~ - +findByUsername(username: string) Promise~User~null~ - +findWithPermissions(userId: string) Promise~User~null~ - +findActiveUsers(organizationId?: string) Promise~User[]~ - } - - class ProductRepository { - +findByCategory(categoryId: string) Promise~Product[]~ - +findActiveProducts() Promise~Product[]~ - } - - BaseRepository <|-- UserRepository : extends - BaseRepository <|-- ProductRepository : extends - IRepository <|.. UserRepository : implements -``` - -**Note**: Specific repositories like `UserRepository` and `ProductRepository` extend `BaseRepository` and can implement the `IRepository` interface for additional type safety. They inherit all base CRUD methods and add domain-specific query methods. - -## Patterns - -### Extending BaseRepository - -```typescript -import { PrismaClient, User } from '@prisma/client'; -import { BaseRepository } from '../modules/common/repository'; - -export class UserRepository extends BaseRepository { - constructor(prisma: PrismaClient) { - super(prisma, 'user'); - } - - // Add custom methods - async findByEmail(email: string): Promise { - return this.prisma.user.findUnique({ - where: { email }, - }); - } - - async findByUsername(username: string): Promise { - return this.prisma.user.findUnique({ - where: { username }, - }); - } -} -``` - -### Custom Query Methods - -Add domain-specific query methods: - -```typescript -export class UserRepository extends BaseRepository { - // Find user with related data - async findWithPermissions(userId: string): Promise { - return this.prisma.user.findUnique({ - where: { id: userId }, - include: { - userRoles: { - include: { role: true }, - }, - userPermissions: { - include: { permission: true }, - }, - }, - }); - } - - // Complex query with filtering - async findActiveUsers(organizationId?: string): Promise { - return this.prisma.user.findMany({ - where: { - isActive: true, - ...(organizationId && { organizationId }), - }, - orderBy: { createdAt: 'desc' }, - }); - } -} -``` - -### Using Repository Interface - -Implement the `IRepository` interface for type safety: - -```typescript -import { IRepository } from '../modules/common/repository'; - -export class UserRepository - extends BaseRepository - implements IRepository { - - // Implementation... -} -``` - -### Error Handling - -BaseRepository handles errors automatically: - -```typescript -async findById(id: string): Promise { - try { - // Database operation - const entity = await this.prisma.user.findUnique({ where: { id } }); - return entity; - } catch (error: any) { - logger.error(`Failed to find ${this.modelName} by ID`, { error, id }); - throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error }); - } -} -``` - -### Transactions - -Use repository transaction method for multiple operations: - -```typescript -await repository.transaction(async (tx) => { - const user = await tx.user.create({ data: userData }); - await tx.userProfile.create({ data: { userId: user.id, ...profileData } }); - return user; -}); -``` - -**Transaction Flow**: - -```mermaid -sequenceDiagram - participant Service - participant Repository - participant Prisma as Prisma Client - participant DB as Database - - Service->>Repository: transaction(callback) - Repository->>Prisma: $transaction(callback) - Prisma->>DB: BEGIN TRANSACTION - - Note over Service,DB: All operations use transaction client (tx) - - Service->>Repository: tx.user.create(data) - Repository->>Prisma: tx.user.create(data) - Prisma->>DB: INSERT INTO users ... - DB-->>Prisma: User created - Prisma-->>Repository: User entity - Repository-->>Service: User entity - - Service->>Repository: tx.userProfile.create(data) - Repository->>Prisma: tx.userProfile.create(data) - Prisma->>DB: INSERT INTO user_profiles ... - DB-->>Prisma: Profile created - Prisma-->>Repository: Profile entity - Repository-->>Service: Profile entity - - alt All operations succeed - Prisma->>DB: COMMIT - DB-->>Prisma: Transaction committed - Prisma-->>Repository: Success result - Repository-->>Service: Success result - else Error occurs - Prisma->>DB: ROLLBACK - DB-->>Prisma: Transaction rolled back - Prisma-->>Repository: Error thrown - Repository-->>Service: DatabaseError thrown - end -``` - -**Important**: All operations within the transaction callback must use the transaction client (`tx`) parameter, not the main Prisma client, to ensure atomicity. - -### Query Options - -Use Prisma query options in findAll: - -```typescript -// Pagination -const users = await userRepository.findAll({ - skip: (page - 1) * limit, - take: limit, -}); - -// Filtering -const activeUsers = await userRepository.findAll({ - where: { isActive: true }, -}); - -// Sorting -const recentUsers = await userRepository.findAll({ - orderBy: { createdAt: 'desc' }, -}); - -// Including relations -const usersWithRoles = await userRepository.findAll({ - include: { userRoles: true }, -}); -``` - -## Best Practices - -1. **Extend BaseRepository**: Always extend BaseRepository instead of implementing from scratch -2. **Custom Methods**: Add domain-specific query methods in repository subclasses -3. **Type Safety**: Use TypeScript generics for type safety -4. **Error Handling**: Let BaseRepository handle common errors, handle domain-specific errors in custom methods -5. **Logging**: BaseRepository handles logging automatically -6. **Transactions**: Use repository transaction method for multi-step operations -7. **Query Optimization**: Use Prisma query options (select, include) to optimize queries -8. **Single Responsibility**: Each repository handles one entity type -9. **Dependency Injection**: Inject PrismaClient in constructor for testability - -## Common Mistakes - -1. **Not Extending BaseRepository**: Implementing CRUD from scratch instead of extending -2. **Business Logic in Repository**: Putting business logic in repository instead of service layer -3. **Exposing Prisma Client**: Exposing raw Prisma client instead of using repository methods -4. **Missing Error Handling**: Not handling errors in custom query methods -5. **Over-fetching Data**: Using `include` unnecessarily, fetching too much data -6. **No Type Safety**: Not using TypeScript generics properly -7. **Transaction Mistakes**: Not using repository transaction method for related operations - -## Troubleshooting - -### Type Errors with Prisma - -**Problem**: TypeScript errors when using Prisma client methods -**Solution**: Ensure Prisma client is generated: `pnpm prisma generate`. Use proper type assertions if needed. - -### Transaction Rollback Issues - -**Problem**: Transaction not rolling back on error -**Solution**: Ensure all operations in transaction callback use the transaction client (`tx`) parameter, not the main Prisma client. - -### Performance Issues - -**Problem**: Slow queries or N+1 query problems -**Solution**: Use `include` to fetch related data in single query. Use `select` to limit fields. Add database indexes. - -## Resources - -- [BaseRepository](../../services/iam-service/src/modules/common/repository.ts) - Base repository implementation -- [User Repository](../../services/iam-service/src/repositories/user.repository.ts) - Example repository -- [Database Prisma](../database-prisma/SKILL.md) - Prisma ORM patterns -- [Error Handling](../error-handling-patterns/SKILL.md) - Error handling in repositories diff --git a/docs/en/skills/resilience-patterns.md b/docs/en/skills/resilience-patterns.md deleted file mode 100644 index 147eb049..00000000 --- a/docs/en/skills/resilience-patterns.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -name: resilience-patterns -description: Resilience patterns for GoodGo microservices including circuit breaker, retry strategies, timeout handling, and graceful degradation. Use when implementing fault tolerance, handling external service failures, or improving system reliability. ---- - -# Resilience Patterns - -## When to Use This Skill - -Use this skill when: -- Implementing circuit breaker patterns for external services -- Adding retry logic for transient failures -- Setting timeout handling for long-running operations -- Implementing graceful degradation strategies -- Handling external service failures -- Improving system fault tolerance - -## Core Concepts - -### Resilience Patterns - -1. **Circuit Breaker**: Prevents cascading failures by stopping calls to failing services -2. **Retry**: Automatically retries failed operations with backoff -3. **Timeout**: Sets maximum time limits for operations -4. **Bulkhead**: Isolates failures to prevent spread -5. **Graceful Degradation**: Provides fallback behavior when services fail - -## Patterns - -### Circuit Breaker Pattern - -Protects against cascading failures: - -The circuit breaker has three states that transition based on error rates and timeouts: - -```mermaid -stateDiagram-v2 - [*] --> CLOSED: Initial State - CLOSED --> OPEN: Errors exceed threshold
(errorThresholdPercentage: 50%) - OPEN --> HALF_OPEN: Reset timeout expires
(resetTimeout: 30s) - HALF_OPEN --> CLOSED: Request succeeds - HALF_OPEN --> OPEN: Request fails - CLOSED --> [*]: Normal operation - OPEN --> [*]: Circuit open (rejecting requests) - HALF_OPEN --> [*]: Testing recovery -``` - -**Circuit Breaker States:** -- **CLOSED**: Normal operation, requests pass through -- **OPEN**: Circuit is open, requests are immediately rejected -- **HALF-OPEN**: Testing if service has recovered, allows limited requests - -```typescript -import CircuitBreaker from 'opossum'; -import { logger } from '@goodgo/logger'; - -export const createCircuitBreaker = ( - action: (...args: TArgs) => Promise, - name: string, - options: Partial = {} -): CircuitBreaker => { - const breaker = new CircuitBreaker(action, { - timeout: 3000, - errorThresholdPercentage: 50, - resetTimeout: 30000, - ...options, - name, - }); - - breaker.on('open', () => { - logger.warn(`Circuit Breaker OPEN: ${name}`); - }); - - breaker.on('halfOpen', () => { - logger.info(`Circuit Breaker HALF-OPEN: ${name}`); - }); - - breaker.on('close', () => { - logger.info(`Circuit Breaker CLOSED: ${name}`); - }); - - return breaker; -}; - -// Usage -const externalApiBreaker = createCircuitBreaker( - async (data) => await externalApi.call(data), - 'external-api' -); - -try { - const result = await externalApiBreaker.fire(requestData); -} catch (error) { - // Handle circuit breaker error or fallback -} -``` - -### Retry Pattern - -Retry transient failures with exponential backoff: - -The retry pattern attempts an operation multiple times with increasing delays between attempts: - -```mermaid -flowchart TD - Start([Start Operation]) --> Attempt[Attempt Operation] - Attempt --> Success{Success?} - Success -->|Yes| Return([Return Result]) - Success -->|No| CheckRetries{Attempt < Max Retries?} - CheckRetries -->|No| ThrowError([Throw Error]) - CheckRetries -->|Yes| CalculateDelay[Calculate Delay:
baseDelay × 2^attempt] - CalculateDelay --> Wait[Wait for Delay] - Wait --> IncrementAttempt[Increment Attempt] - IncrementAttempt --> Attempt - - style Start fill:#e1f5e1 - style Return fill:#e1f5e1 - style ThrowError fill:#ffe1e1 - style CalculateDelay fill:#fff4e1 -``` - -**Exponential Backoff Example:** -- Attempt 1: 1s delay -- Attempt 2: 2s delay -- Attempt 3: 4s delay -- Attempt 4: 8s delay - -```typescript -async function retryWithBackoff( - fn: () => Promise, - maxRetries: number = 3, - baseDelay: number = 1000 -): Promise { - for (let attempt = 0; attempt <= maxRetries; attempt++) { - try { - return await fn(); - } catch (error) { - if (attempt === maxRetries) throw error; - - const delay = baseDelay * Math.pow(2, attempt); - await new Promise(resolve => setTimeout(resolve, delay)); - } - } - throw new Error('Retry exhausted'); -} -``` - -### Timeout Pattern - -Set maximum time limits: - -The timeout pattern uses Promise.race to enforce maximum execution time: - -```mermaid -sequenceDiagram - participant Client - participant TimeoutWrapper - participant Operation - participant TimeoutTimer - - Client->>TimeoutWrapper: Execute with timeout - TimeoutWrapper->>Operation: Start operation - TimeoutWrapper->>TimeoutTimer: Start timeout timer - - alt Operation completes first - Operation-->>TimeoutWrapper: Return result - TimeoutWrapper-->>Client: Return result - TimeoutWrapper->>TimeoutTimer: Cancel timer - else Timeout expires first - TimeoutTimer-->>TimeoutWrapper: Timeout error - TimeoutWrapper->>Operation: (Operation may continue) - TimeoutWrapper-->>Client: Reject with timeout error - end -``` - -**Timeout Behavior:** -- Uses `Promise.race()` to compete operation vs timeout -- First to resolve/reject wins -- Operation may continue after timeout, but result is ignored - -```typescript -async function withTimeout( - promise: Promise, - timeoutMs: number -): Promise { - const timeout = new Promise((_, reject) => { - setTimeout(() => reject(new Error('Operation timeout')), timeoutMs); - }); - - return Promise.race([promise, timeout]); -} - -// Usage -try { - const result = await withTimeout( - externalService.call(), - 5000 // 5 second timeout - ); -} catch (error) { - if (error.message === 'Operation timeout') { - // Handle timeout - } -} -``` - -### Graceful Degradation - -Provide fallback behavior: - -```typescript -async function getDataWithFallback() { - try { - return await primaryDataSource.get(); - } catch (error) { - logger.warn('Primary source failed, using fallback', { error }); - return await fallbackDataSource.get(); - } -} -``` - -## Best Practices - -1. **Circuit Breaker**: Use for external service calls -2. **Retry**: Retry only transient failures (network, timeout) -3. **Timeout**: Set appropriate timeouts for all external calls -4. **Fallback**: Always provide fallback behavior -5. **Monitoring**: Monitor circuit breaker states and retry rates -6. **Logging**: Log all resilience actions for debugging - -## Common Mistakes - -1. **Retrying Non-Retryable Errors**: Retrying 4xx errors (client errors) -2. **No Timeout**: Missing timeouts on external calls -3. **No Fallback**: No graceful degradation strategy -4. **Too Many Retries**: Excessive retries causing performance issues - -## Resources - -- [Circuit Breaker](../../services/iam-service/src/modules/common/circuit-breaker.ts) - Circuit breaker implementation diff --git a/docs/en/skills/security.md b/docs/en/skills/security.md deleted file mode 100644 index c7a082dc..00000000 --- a/docs/en/skills/security.md +++ /dev/null @@ -1,925 +0,0 @@ ---- -name: security -description: Security best practices and patterns for GoodGo microservices platform. Use when implementing authentication, authorization, data protection, input validation, rate limiting, secrets management, or security testing across all services. ---- - -# Security Patterns for GoodGo Microservices - -## When to Use This Skill - -Use this skill when: -- Implementing authentication and authorization in any service -- Protecting sensitive data (PII, credentials, tokens) -- Validating user inputs and file uploads -- Implementing rate limiting and DDoS protection -- Setting up audit logging and security monitoring -- Encrypting data at rest and in transit -- Managing secrets and credentials -- Implementing security testing -- Handling security incidents -- Designing secure API endpoints - -## Core Security Principles - -1. **Defense in Depth**: Multiple layers of security controls -2. **Least Privilege**: Grant minimum required permissions -3. **Fail Secure**: Default to deny access -4. **Separation of Duties**: Critical operations require multiple approvals -5. **Audit Everything**: Log all security-relevant events -6. **Encrypt Sensitive Data**: PII, tokens, credentials must be encrypted -7. **Validate All Inputs**: Never trust user input -8. **Principle of Least Exposure**: Minimize attack surface -9. **Secure by Default**: Security built-in, not bolted on -10. **Assume Breach**: Design for detection and response - -## Authentication & Authorization - -### JWT Token Validation - -The following diagram illustrates the authentication flow when a client makes a request with a JWT token: - -```mermaid -sequenceDiagram - participant Client - participant Middleware as Auth Middleware - participant JWTService as JWT Service - participant Request as Express Request - - Client->>Middleware: HTTP Request with Token - Middleware->>Middleware: Extract token from
Authorization header or cookie - - alt Token not found - Middleware->>Client: 401 Unauthorized
(AUTH_REQUIRED) - else Token found - Middleware->>JWTService: verifyAccessToken(token) - - alt Token invalid or expired - JWTService->>Middleware: Verification failed - Middleware->>Client: 401 Unauthorized
(INVALID_TOKEN) - else Token valid - JWTService->>Middleware: Payload (sub, email, roles, permissions) - Middleware->>Request: Attach user to req.user - Middleware->>Client: Continue to next middleware - end - end -``` - -```typescript -// src/middlewares/auth.middleware.ts -import { Request, Response, NextFunction } from 'express'; -import { jwtService } from '@goodgo/auth-sdk'; -import { logger } from '@goodgo/logger'; - -export const authenticate = () => { - return async (req: Request, res: Response, next: NextFunction) => { - try { - // Extract token from Authorization header or cookie - let token: string | null = null; - - const authHeader = req.headers.authorization; - if (authHeader?.startsWith('Bearer ')) { - token = authHeader.substring(7); - } else if (req.cookies?.access_token) { - token = req.cookies.access_token; - } - - if (!token) { - return res.status(401).json({ - success: false, - error: { code: 'AUTH_REQUIRED', message: 'Authentication required' } - }); - } - - // Verify token - const payload = await jwtService.verifyAccessToken(token); - - // Attach user to request - req.user = { - id: payload.sub, - userId: payload.sub, - email: payload.email, - roles: payload.roles || [], - permissions: payload.permissions || [] - }; - - next(); - } catch (error) { - logger.warn('Authentication failed', { error: error.message }); - return res.status(401).json({ - success: false, - error: { code: 'INVALID_TOKEN', message: 'Invalid or expired token' } - }); - } - }; -}; -``` - -### Role-Based Authorization - -The authorization decision flow determines whether a user has the required permissions to access a resource: - -```mermaid -flowchart TD - Start([Request Received]) --> CheckAuth{User
Authenticated?} - - CheckAuth -->|No| Return401[Return 401
AUTH_REQUIRED] - CheckAuth -->|Yes| CheckType{Authorization
Type?} - - CheckType -->|Role-Based| CheckRole{User has
Required Role?} - CheckType -->|Permission-Based| CheckPermission{User has
Resource:Action
Permission?} - CheckType -->|Ownership| CheckOwnership{Resource ID
matches User ID?} - - CheckRole -->|No| LogDenial[Log Permission Denied
with user roles] - CheckPermission -->|No| LogDenial - CheckOwnership -->|No| LogDenial - - LogDenial --> Return403[Return 403
FORBIDDEN] - - CheckRole -->|Yes| Allow[Allow Request
Continue to Handler] - CheckPermission -->|Yes| Allow - CheckOwnership -->|Yes| Allow - - Return401 --> End([End]) - Return403 --> End - Allow --> End - - style CheckAuth fill:#e1f5ff - style CheckType fill:#e1f5ff - style Return401 fill:#ffebee - style Return403 fill:#ffebee - style Allow fill:#e8f5e9 -``` - -```typescript -// src/middlewares/rbac.middleware.ts -export const requireRole = (...allowedRoles: string[]) => { - return (req: Request, res: Response, next: NextFunction) => { - if (!req.user) { - return res.status(401).json({ - success: false, - error: { code: 'AUTH_REQUIRED', message: 'Authentication required' } - }); - } - - const userRoles = req.user.roles || []; - const hasRole = userRoles.some(role => allowedRoles.includes(role)); - - if (!hasRole) { - logger.warn('Access denied - insufficient role', { - userId: req.user.id, - userRoles, - requiredRoles: allowedRoles - }); - - return res.status(403).json({ - success: false, - error: { code: 'FORBIDDEN', message: 'Insufficient permissions' } - }); - } - - next(); - }; -}; - -// Permission-based authorization -export const requirePermission = (resource: string, action: string) => { - return async (req: Request, res: Response, next: NextFunction) => { - if (!req.user) { - return res.status(401).json({ - success: false, - error: { code: 'AUTH_REQUIRED', message: 'Authentication required' } - }); - } - - const permission = `${resource}:${action}`; - const hasPermission = req.user.permissions?.includes(permission); - - if (!hasPermission) { - logger.warn('Access denied - insufficient permission', { - userId: req.user.id, - required: permission - }); - - return res.status(403).json({ - success: false, - error: { code: 'FORBIDDEN', message: 'Insufficient permissions' } - }); - } - - next(); - }; -}; - -// Usage in routes -router.post( - '/api/v1/users', - authenticate(), - requirePermission('users', 'create'), - userController.create -); -``` - -### Resource Ownership Validation - -```typescript -// Ensure users can only access their own resources -export const requireOwnership = (resourceIdParam: string = 'id') => { - return (req: Request, res: Response, next: NextFunction) => { - const resourceId = req.params[resourceIdParam]; - const userId = req.user?.id; - - if (resourceId !== userId) { - logger.warn('Access denied - resource ownership mismatch', { - userId, - resourceId - }); - - return res.status(403).json({ - success: false, - error: { code: 'FORBIDDEN', message: 'Access denied' } - }); - } - - next(); - }; -}; -``` - -## Data Protection - -### Encryption Service - -The encryption and decryption flow for protecting sensitive data at rest: - -```mermaid -sequenceDiagram - participant Service - participant EncryptionService - participant Crypto as Node.js Crypto - participant Database - - Note over Service,Database: Encryption Flow - Service->>EncryptionService: encrypt(plaintext) - EncryptionService->>Crypto: Generate random IV
(16 bytes) - EncryptionService->>Crypto: Create cipher
(AES-256-GCM) - EncryptionService->>Crypto: Encrypt plaintext - Crypto->>EncryptionService: Encrypted data + Auth Tag - EncryptionService->>Service: Format: iv:tag:ciphertext - Service->>Database: Store encrypted data - - Note over Service,Database: Decryption Flow - Service->>Database: Retrieve encrypted data - Database->>Service: iv:tag:ciphertext - Service->>EncryptionService: decrypt(encryptedText) - EncryptionService->>EncryptionService: Split iv, tag, ciphertext - EncryptionService->>Crypto: Create decipher
(AES-256-GCM) - EncryptionService->>Crypto: Set auth tag - EncryptionService->>Crypto: Decrypt ciphertext - Crypto->>EncryptionService: Plaintext - EncryptionService->>Service: Return plaintext -``` - -```typescript -// src/core/security/encryption.service.ts -import crypto from 'crypto'; - -const ALGORITHM = 'aes-256-gcm'; -const IV_LENGTH = 16; -const TAG_LENGTH = 16; - -export class EncryptionService { - private getKey(): Buffer { - const secret = process.env.ENCRYPTION_KEY; - if (!secret || secret.length < 32) { - throw new Error('ENCRYPTION_KEY must be at least 32 characters'); - } - return crypto.scryptSync(secret, 'salt', 32); - } - - encrypt(text: string): string { - const key = this.getKey(); - const iv = crypto.randomBytes(IV_LENGTH); - const cipher = crypto.createCipheriv(ALGORITHM, key, iv); - - let encrypted = cipher.update(text, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - const tag = cipher.getAuthTag(); - - return `${iv.toString('hex')}:${tag.toString('hex')}:${encrypted}`; - } - - decrypt(encryptedText: string): string { - const [ivHex, tagHex, encrypted] = encryptedText.split(':'); - const iv = Buffer.from(ivHex, 'hex'); - const tag = Buffer.from(tagHex, 'hex'); - - const key = this.getKey(); - const decipher = crypto.createDecipheriv(ALGORITHM, key, iv); - decipher.setAuthTag(tag); - - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - return decrypted; - } -} - -// Usage: Encrypt PII before storing -const encryption = new EncryptionService(); -const encryptedPhone = encryption.encrypt(user.phone); -``` - -### Password Hashing - -The password hashing and verification flow: - -```mermaid -sequenceDiagram - participant User - participant Service - participant PasswordService - participant Bcrypt - participant Database - - Note over User,Database: Registration/Password Change - User->>Service: Submit password - Service->>PasswordService: hash(password) - PasswordService->>Bcrypt: bcrypt.hash(password, 12) - Bcrypt->>Bcrypt: Generate salt - Bcrypt->>Bcrypt: Hash with cost factor 12 - Bcrypt->>PasswordService: Hashed password - PasswordService->>Service: Return hash - Service->>Database: Store passwordHash - Service->>Service: Sanitize password
before logging - - Note over User,Database: Login Verification - User->>Service: Submit credentials - Service->>Database: Fetch user by email - Database->>Service: User with passwordHash - Service->>PasswordService: verify(password, hash) - PasswordService->>Bcrypt: bcrypt.compare(password, hash) - Bcrypt->>PasswordService: Boolean result - PasswordService->>Service: Return verification result - - alt Password matches - Service->>User: Authentication success - else Password mismatch - Service->>User: Invalid credentials
(generic error) - end -``` - -```typescript -// Always use bcrypt with appropriate cost factor -import bcrypt from 'bcrypt'; - -const SALT_ROUNDS = 12; // Production: 12, Development: 10 - -export class PasswordService { - async hash(password: string): Promise { - return bcrypt.hash(password, SALT_ROUNDS); - } - - async verify(password: string, hash: string): Promise { - return bcrypt.compare(password, hash); - } - - // Never log passwords - sanitizeForLogging(data: any): any { - const sanitized = { ...data }; - if (sanitized.password) sanitized.password = '[REDACTED]'; - if (sanitized.passwordHash) sanitized.passwordHash = '[REDACTED]'; - return sanitized; - } -} -``` - -### Token Hashing - -```typescript -// Hash tokens before storing in database -import crypto from 'crypto'; - -export class TokenService { - hashToken(token: string): string { - const salt = process.env.TOKEN_SALT || 'default-salt-change-in-production'; - return crypto - .createHash('sha256') - .update(token + salt) - .digest('hex'); - } - - generateSecureToken(length: number = 32): string { - return crypto.randomBytes(length).toString('hex'); - } -} -``` - -## Input Validation - -### Zod Schema Validation - -```typescript -// Always validate inputs with Zod -import { z } from 'zod'; - -// DTO with validation -export const CreateUserDto = z.object({ - email: z.string().email('Invalid email format'), - password: z.string() - .min(8, 'Password must be at least 8 characters') - .regex(/[A-Z]/, 'Password must contain uppercase letter') - .regex(/[a-z]/, 'Password must contain lowercase letter') - .regex(/[0-9]/, 'Password must contain number') - .regex(/[^A-Za-z0-9]/, 'Password must contain special character'), - phone: z.string() - .regex(/^\+[1-9]\d{1,14}$/, 'Invalid phone format (E.164)') - .optional(), - name: z.string().min(1).max(255) -}); - -// In controller -export class UserController { - async create(req: Request, res: Response) { - try { - const dto = CreateUserDto.parse(req.body); - const user = await this.service.create(dto); - res.status(201).json({ success: true, data: user }); - } catch (error) { - if (error instanceof z.ZodError) { - return res.status(400).json({ - success: false, - error: { - code: 'VALIDATION_ERROR', - message: 'Invalid input data', - details: error.errors - } - }); - } - throw error; - } - } -} -``` - -### File Upload Validation - -```typescript -// Validate file uploads -import fileType from 'file-type'; - -export class FileValidationService { - private readonly MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB - private readonly ALLOWED_TYPES = ['image/jpeg', 'image/png', 'application/pdf']; - - async validateFile(file: Express.Multer.File): Promise { - // Size check - if (file.size > this.MAX_FILE_SIZE) { - throw new HttpError(400, 'FILE_TOO_LARGE', 'File exceeds maximum size'); - } - - // Type check - if (!this.ALLOWED_TYPES.includes(file.mimetype)) { - throw new HttpError(400, 'INVALID_FILE_TYPE', 'File type not allowed'); - } - - // Content validation (prevent MIME type spoofing) - const type = await fileType.fromBuffer(file.buffer); - if (!type || !this.ALLOWED_TYPES.includes(type.mime)) { - throw new HttpError(400, 'INVALID_FILE_CONTENT', 'File content mismatch'); - } - - // TODO: Add virus scanning for production - } -} -``` - -### SQL Injection Prevention - -```typescript -// Always use Prisma parameterized queries (automatic) -// Never use string concatenation for queries - -// ❌ BAD - Never do this -const query = `SELECT * FROM users WHERE email = '${email}'`; - -// ✅ GOOD - Use Prisma -const user = await prisma.user.findUnique({ - where: { email } -}); - -// ✅ GOOD - For dynamic queries -const where: any = {}; -if (email) where.email = email; -if (status) where.status = status; - -const users = await prisma.user.findMany({ where }); -``` - -## Rate Limiting - -```typescript -// Implement rate limiting for all endpoints -import rateLimit from 'express-rate-limit'; -import RedisStore from 'rate-limit-redis'; -import Redis from 'ioredis'; - -const redis = new Redis(process.env.REDIS_URL); - -// Standard rate limit -export const standardLimiter = rateLimit({ - store: new RedisStore({ - client: redis, - prefix: 'rl:standard:' - }), - windowMs: 15 * 60 * 1000, // 15 minutes - max: 100, // 100 requests per window - message: 'Too many requests, please try again later', - standardHeaders: true, - legacyHeaders: false -}); - -// Strict rate limit for sensitive operations -export const strictLimiter = rateLimit({ - store: new RedisStore({ - client: redis, - prefix: 'rl:strict:' - }), - windowMs: 60 * 60 * 1000, // 1 hour - max: 10, - message: 'Rate limit exceeded for this operation' -}); - -// Login-specific rate limit -export const loginLimiter = rateLimit({ - store: new RedisStore({ - client: redis, - prefix: 'rl:login:' - }), - windowMs: 15 * 60 * 1000, - max: 5, // 5 login attempts per 15 minutes - skipSuccessfulRequests: true, - message: 'Too many login attempts, please try again later' -}); - -// Usage -router.post('/api/v1/auth/login', loginLimiter, authController.login); -router.post('/api/v1/users', authenticate(), strictLimiter, userController.create); -``` - -## Error Handling Security - -```typescript -// Sanitize error messages to prevent information disclosure -export class SecureErrorHandler { - handleError(error: Error, req: Request, res: Response) { - const isDev = process.env.NODE_ENV === 'development'; - const isProd = process.env.NODE_ENV === 'production'; - - // Log full error internally - logger.error('Request error', { - error: error.message, - stack: error.stack, - path: req.path, - method: req.method, - userId: req.user?.id - }); - - // Don't expose user existence - if (error.message.includes('user not found') || - error.message.includes('invalid credentials')) { - return res.status(401).json({ - success: false, - error: { - code: 'INVALID_CREDENTIALS', - message: 'Invalid email or password' - } - }); - } - - // Validation errors - safe to expose - if (error instanceof z.ZodError) { - return res.status(400).json({ - success: false, - error: { - code: 'VALIDATION_ERROR', - message: 'Invalid input data', - details: error.errors - } - }); - } - - // Generic errors for production - if (isProd) { - return res.status(500).json({ - success: false, - error: { - code: 'INTERNAL_ERROR', - message: 'An error occurred. Please try again later.' - } - }); - } - - // Detailed errors only in development - return res.status(500).json({ - success: false, - error: { - code: 'INTERNAL_ERROR', - message: error.message, - stack: isDev ? error.stack : undefined - } - }); - } -} -``` - -## Secrets Management - -```typescript -// Never hardcode secrets -// Always use environment variables with validation -import { z } from 'zod'; - -const secretsSchema = z.object({ - JWT_SECRET: z.string().min(32, 'JWT_SECRET must be at least 32 characters'), - JWT_REFRESH_SECRET: z.string().min(32), - DATABASE_URL: z.string().url(), - REDIS_URL: z.string().url().optional(), - ENCRYPTION_KEY: z.string().min(32).optional() -}); - -export const secrets = secretsSchema.parse(process.env); - -// For production, use secret management: -// - AWS Secrets Manager -// - HashiCorp Vault -// - Kubernetes Secrets -// - Azure Key Vault - -// Rotate secrets regularly (quarterly recommended) -``` - -## Audit Logging - -```typescript -// Log all security-relevant events -export class AuditService { - async logSecurityEvent( - event: string, - userId: string | null, - details: Record, - req?: Request - ) { - await this.prisma.auditLog.create({ - data: { - event, - userId, - type: 'SECURITY', - details: this.sanitizeDetails(details), - ipAddress: req?.ip || details.ipAddress, - userAgent: req?.get('user-agent'), - timestamp: new Date() - } - }); - } - - // Sanitize PII from logs - private sanitizeDetails(details: Record): Record { - const sensitive = ['password', 'token', 'secret', 'ssn', 'creditCard']; - const sanitized = { ...details }; - - for (const key of sensitive) { - if (sanitized[key]) { - sanitized[key] = '[REDACTED]'; - } - } - - return sanitized; - } -} - -// Usage -await auditService.logSecurityEvent('LOGIN_SUCCESS', user.id, { - email: user.email, - ipAddress: req.ip -}, req); - -await auditService.logSecurityEvent('PERMISSION_DENIED', user.id, { - resource: 'users', - action: 'delete', - targetId: targetUserId -}, req); -``` - -## Security Headers - -```typescript -// Add security headers middleware -import helmet from 'helmet'; - -app.use(helmet({ - contentSecurityPolicy: { - directives: { - defaultSrc: ["'self'"], - styleSrc: ["'self'", "'unsafe-inline'"], - scriptSrc: ["'self'"], - imgSrc: ["'self'", "data:", "https:"] - } - }, - hsts: { - maxAge: 31536000, - includeSubDomains: true, - preload: true - } -})); - -// Additional headers -app.use((req, res, next) => { - res.setHeader('X-Content-Type-Options', 'nosniff'); - res.setHeader('X-Frame-Options', 'DENY'); - res.setHeader('X-XSS-Protection', '1; mode=block'); - res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); - next(); -}); -``` - -## CORS Configuration - -```typescript -// Configure CORS securely -import cors from 'cors'; - -const allowedOrigins = process.env.CORS_ORIGIN?.split(',') || []; - -app.use(cors({ - origin: (origin, callback) => { - if (!origin || allowedOrigins.includes(origin)) { - callback(null, true); - } else { - callback(new Error('Not allowed by CORS')); - } - }, - credentials: true, - methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'], - exposedHeaders: ['X-Request-ID'], - maxAge: 86400 // 24 hours -})); -``` - -## Security Testing - -```typescript -// Security test patterns -describe('Security Tests', () => { - it('should prevent SQL injection', async () => { - const maliciousInput = "'; DROP TABLE users; --"; - const response = await request(app) - .get(`/api/v1/users?search=${encodeURIComponent(maliciousInput)}`) - .set('Authorization', `Bearer ${token}`); - - expect(response.status).not.toBe(500); - // Should return 400 or empty results, not crash - }); - - it('should prevent XSS attacks', async () => { - const xssPayload = ''; - const response = await request(app) - .post('/api/v1/users') - .send({ email: xssPayload, password: 'test123' }); - - // Response should sanitize or reject - expect(response.body.data?.email).not.toContain('