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.
This commit is contained in:
Ho Ngoc Hai
2026-01-14 11:49:58 +07:00
parent 6ca9027782
commit 83081e8f29
52 changed files with 0 additions and 23412 deletions

View File

@@ -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<br/>Rate Limiting<br/>Validation
Middleware->>Controller: Validated Request
Controller->>Controller: Parse DTO<br/>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<br/>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<br/>https://api.goodgo.com] --> B[Version<br/>/v1]
B --> C[Resource Collection<br/>/users]
B --> D[Resource Collection<br/>/orders]
B --> E[Resource Collection<br/>/products]
C --> F[Resource Instance<br/>/users/:id]
C --> G[Sub-Resource<br/>/users/:id/orders]
F --> H[GET /users/:id<br/>Retrieve user]
F --> I[PUT /users/:id<br/>Update user]
F --> J[DELETE /users/:id<br/>Delete user]
C --> K[GET /users<br/>List users]
C --> L[POST /users<br/>Create user]
G --> M[GET /users/:id/orders<br/>List user orders]
G --> N[POST /users/:id/orders<br/>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<T> {
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<T> {
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<ResponseDto> {
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<ResponseDto> {
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<ResponseDto> {
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<ResponseDto> {
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<ResponseDto> {
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<T>(
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<br/>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<br/>Middleware]
F --> L
G --> L
H --> L
I --> L
K --> L
L --> M{Error Type?}
M -->|ValidationError| N[400 Bad Request<br/>VALIDATION_ERROR]
M -->|UnauthorizedError| O[401 Unauthorized<br/>UNAUTHORIZED]
M -->|ForbiddenError| P[403 Forbidden<br/>FORBIDDEN]
M -->|NotFoundError| Q[404 Not Found<br/>NOT_FOUND]
M -->|ConflictError| R[409 Conflict<br/>CONFLICT]
M -->|Unknown Error| S{Development<br/>Mode?}
S -->|Yes| T[500 Internal Error<br/>INTERNAL_ERROR<br/>+ Stack Trace]
S -->|No| U[500 Internal Error<br/>INTERNAL_ERROR<br/>Generic Message]
N --> V[Format Error Response<br/>success: false<br/>error: code, message, details]
O --> V
P --> V
Q --> V
R --> V
T --> V
U --> V
V --> W[Log Error<br/>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

View File

@@ -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`

View File

@@ -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<br/>Middleware
participant Controller as Version-Aware<br/>Controller
participant Service
Client->>Middleware: Request with Accept header<br/>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<br/>Add Deprecation Headers
Deprecated --> Sunset: Sunset Date Reached<br/>Stop Accepting New Requests
Sunset --> Removed: Grace Period Ended<br/>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<br/>v2 adoption?}
Phase2 -->|No| Wait1
Phase2 -->|Yes| Phase2Start[Phase 2: Deprecate v1]
Phase2Start --> AddHeaders[Add deprecation headers to v1]
AddHeaders --> NotifyClients[Notify clients via<br/>deprecation warnings]
NotifyClients --> ProvideGuide[Provide migration guide]
ProvideGuide --> Monitor2[Monitor migration progress]
Monitor2 --> Wait2[Wait until sunset date]
Wait2 --> Phase3{Sunset date<br/>reached?}
Phase3 -->|No| Monitor2
Phase3 -->|Yes| Phase3Start[Phase 3: Remove v1]
Phase3Start --> StopAccepting[Stop accepting new v1 requests]
StopAccepting --> GracePeriod[Grace period for<br/>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: </api/v2/users>; 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`

View File

@@ -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<br/>• Capacity: 10k keys<br/>• TTL: 60s-5min<br/>• Scope: Per-instance"]
end
subgraph L2Layer["L2 Cache - Redis (Distributed)"]
L2[Redis Cache]
L2Props["• Speed: < 5ms<br/>• Capacity: Large<br/>• TTL: Configurable<br/>• 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<br/>Memory}
CheckL1 -->|Hit| ReturnL1[Return Data<br/>from L1]
CheckL1 -->|Miss| CheckL2{Check L2 Cache<br/>Redis}
CheckL2 -->|Hit| StoreL1[Store in L1<br/>Warm Cache]
StoreL1 --> ReturnL2[Return Data<br/>from L2]
CheckL2 -->|Miss| FetchSource[Fetch from<br/>Data Source]
FetchSource --> StoreBoth[Store in L1 & L2]
StoreBoth --> ReturnSource[Return Data<br/>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>('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<string[]> {
const cacheKey = cacheService.keys.userPermissions(userId);
// Try cache first
const cached = await cacheService.get<string[]>(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<br/>Invalidation<br/>Strategy}
ChooseStrategy -->|Single Key| SingleKey[Single Key<br/>Invalidation]
SingleKey --> DelL1[Delete from L1]
DelL1 --> DelL2[Delete from L2]
DelL2 --> Done1([Complete])
ChooseStrategy -->|Pattern Match| PatternMatch[Pattern-Based<br/>Invalidation]
PatternMatch --> FindKeys[Find Matching Keys<br/>user:123:*]
FindKeys --> DelManyL1[Delete from L1<br/>All Matching]
DelManyL1 --> DelManyL2[Delete from L2<br/>All Matching]
DelManyL2 --> Done2([Complete])
ChooseStrategy -->|Multiple Keys| MultipleKeys[Multiple Keys<br/>Invalidation]
MultipleKeys --> ListKeys[List Keys to Delete<br/>user:123<br/>user:123:permissions<br/>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<Data | null> {
try {
// Try cache first
const cached = await cacheService.get<Data>(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

View File

@@ -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<br/>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<boolean> {
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`

View File

@@ -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<br/>// EN: ...<br/>// VI: ..."]
MultiLine["Multi-line Comments<br/>/* EN: ...<br/>VI: ... */"]
JSDoc["JSDoc Comments<br/>/** EN: ...<br/>VI: ... */"]
Prisma["Prisma Comments<br/>/// EN: ...<br/>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)<br/>First"]
VI["Vietnamese (VI)<br/>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<br/>code element?}
CheckType -->|Public API| HighPriority[High Priority:<br/>Always Document]
CheckType -->|Complex Logic| HighPriority
CheckType -->|Security Code| HighPriority
CheckType -->|Config/Setup| HighPriority
CheckType -->|Error Handling| HighPriority
CheckType -->|Helper Function| MediumPriority[Medium Priority:<br/>Document if Helpful]
CheckType -->|Data Transform| MediumPriority
CheckType -->|External Integration| MediumPriority
CheckType -->|Simple Getter/Setter| LowPriority[Low Priority:<br/>Optional]
CheckType -->|Self-explanatory| LowPriority
CheckType -->|Standard CRUD| LowPriority
HighPriority --> ChooseFormat{Choose Comment<br/>Format}
MediumPriority --> ChooseFormat
LowPriority --> ChooseFormat
ChooseFormat -->|Function/Class| UseJSDoc[Use JSDoc Format<br/>/** EN: ...<br/>VI: ... */]
ChooseFormat -->|Brief Explanation| UseSingleLine[Use Single-line<br/>// EN: ...<br/>// VI: ...]
ChooseFormat -->|Multi-step Process| UseMultiLine[Use Multi-line<br/>/* EN: ...<br/>VI: ... */]
ChooseFormat -->|Prisma Schema| UsePrisma[Use Prisma Format<br/>/// EN: ...<br/>VI: ...]
UseJSDoc --> AddParams[Add @param tags<br/>Add @returns tag<br/>Add @throws if needed]
UseSingleLine --> WriteBilingual[Write Bilingual:<br/>EN first, VI second]
UseMultiLine --> WriteBilingual
UsePrisma --> WriteBilingual
AddParams --> WriteBilingual
WriteBilingual --> CheckSpecial{Special<br/>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<string> {
// 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<UserPayload> {
// 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 (
<div className="user-card">
{/* EN: Display user avatar / VI: Hiển thị avatar người dùng */}
<img src={user.avatar} alt={user.name} />
{/* EN: User information section / VI: Phần thông tin người dùng */}
<div className="user-info">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
</div>
);
}
```
### 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ì]
```

View File

@@ -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`

View File

@@ -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<br/>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<br/>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,<br/>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<br/>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<br/>Normalized]
WriteModel --> Event[Domain Event]
Event --> EventStore[(Event Store)]
end
subgraph ReadPath[Read Path]
Query[Query] --> ReadModel[Read Model<br/>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<br/>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<br/>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`

View File

@@ -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<T> {
constructor(protected prisma: PrismaClient) {}
abstract findById(id: string): Promise<T | null>;
abstract findAll(options?: any): Promise<T[]>;
abstract create(data: any): Promise<T>;
abstract update(id: string, data: any): Promise<T>;
abstract delete(id: string): Promise<void>;
}
// src/repositories/user.repository.ts
export class UserRepository extends BaseRepository<User> {
async findById(id: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { id },
include: { profile: true }
});
}
async findByEmail(email: string): Promise<User | null> {
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<User> {
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<User> {
return this.prisma.user.update({
where: { id },
data,
include: { profile: true }
});
}
async delete(id: string): Promise<void> {
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<br/>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<PrismaClient>()
}));
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

View File

@@ -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<br/>api.goodgo.com]
end
subgraph ServiceLayer["Service Layer"]
Service[Service<br/>ClusterIP]
end
subgraph DeploymentLayer["Deployment Layer"]
Deployment[Deployment<br/>iam-service]
HPA[HorizontalPodAutoscaler<br/>HPA]
end
subgraph PodLayer["Pod Layer"]
Pod1[Pod 1<br/>Container]
Pod2[Pod 2<br/>Container]
Pod3[Pod 3<br/>Container]
end
subgraph ConfigLayer["Configuration Layer"]
ConfigMap[ConfigMap<br/>app-config]
Secret[Secret<br/>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<br/>api.goodgo.com/auth/login
Ingress->>Ingress: TLS Termination
Ingress->>Ingress: Path Routing<br/>/auth → iam-service
Ingress->>Service: HTTP Request<br/>iam-service:80
Service->>Service: DNS Resolution<br/>iam-service.goodgo.svc.cluster.local
Service->>Service: Endpoint Selection<br/>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

View File

@@ -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/<br/>docs/vi/"]
ProjectLoc --> ProjectSub{Content Type?}
ProjectSub -->|Guides| GuidesLoc["guides/<br/>(getting-started.md,<br/>deployment.md)"]
ProjectSub -->|Architecture| ArchLoc["architecture/<br/>(system-design.md)"]
ProjectSub -->|API Specs| APILoc["api/openapi/<br/>(*.yaml)"]
ProjectSub -->|Runbooks| RunbookLoc["runbooks/<br/>(incident-response.md)"]
ServiceDocs --> ServiceLoc["services/[name]/README.md<br/>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<br/>< 200 lines| CheckLocation{Document Location?}
CheckLength -->|Long<br/>> 200 lines| SeparateFiles[Use Separate Files Format]
CheckLocation -->|README files<br/>Service/Package docs<br/>Infrastructure docs| SideBySide[Use Side-by-side Format]
CheckLocation -->|docs/guides/<br/>Short configuration docs| SideBySide
CheckLength -->|Medium| CheckType{Content Type?}
CheckType -->|API Documentation<br/>Technical Specifications| Sections[Use Sections Format]
CheckType -->|Mixed Content| Sections
SeparateFiles --> SeparateAction["Create docs/en/[path]/file.md<br/>Create docs/vi/[path]/file.md<br/>(Mirror structure)"]
SideBySide --> SideBySideAction["Single file with<br/>EN / VI inline<br/>Example: 'Title / Tiêu Đề'"]
Sections --> SectionsAction["Single file with<br/>--- separator<br/>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
```

View File

@@ -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 {
<<built-in>>
+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<br/>404]
B -->|Invalid Input| D{Validation?}
B -->|Authentication| E{Type?}
B -->|Resource Conflict| F[ConflictError<br/>409]
B -->|Rate Limit| G[RateLimitError<br/>429]
B -->|Database| H[DatabaseError<br/>500]
B -->|External Service| I[ExternalServiceError<br/>502]
B -->|Service Unavailable| J[ServiceUnavailableError<br/>503]
B -->|Unknown/Programming| K[InternalServerError<br/>500]
D -->|Schema Validation| L[ValidationError<br/>422]
D -->|Bad Request Format| M[BadRequestError<br/>400]
E -->|No Token/Invalid| N[UnauthorizedError<br/>401]
E -->|No Permission| O[ForbiddenError<br/>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

View File

@@ -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<T extends BaseEvent>(
topic: string,
event: Omit<T, 'eventId' | 'timestamp' | 'source'>,
options?: { partitionKey?: string }
): Promise<void> {
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<string, EventHandler[]> = new Map();
on<T extends BaseEvent>(eventType: string, handler: EventHandler<T>): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
async start(topics: string[]): Promise<void> {
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<void> {
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`

View File

@@ -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<br/>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<br/>Input parameters]
A --> C[main.tf<br/>Resource definitions]
A --> D[outputs.tf<br/>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<br/>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<br/>staging config]
F --> H[terraform.tfvars<br/>production config]
G --> I[Remote State Backend<br/>staging/terraform.tfstate]
H --> J[Remote State Backend<br/>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`

View File

@@ -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<br/>Required?}
CheckPerformance -->|Yes| CheckStreaming{Need Streaming?}
CheckPerformance -->|No| CheckFlexible{Need Flexible<br/>Queries?}
CheckStreaming -->|Yes| UseGRPC[Use gRPC]
CheckStreaming -->|No| CheckLowLatency{Ultra Low<br/>Latency?}
CheckLowLatency -->|Yes| UseGRPC
CheckLowLatency -->|No| UseREST
CheckFlexible -->|Yes| UseGraphQL[Use GraphQL]
CheckFlexible -->|No| UseREST
UseREST --> RESTDesc["REST: External APIs<br/>Browser clients<br/>Simple CRUD"]
UseGRPC --> GRPCDesc["gRPC: Internal services<br/>High performance<br/>Streaming support"]
UseGraphQL --> GraphQLDesc["GraphQL: Complex queries<br/>Mobile apps<br/>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<br/>(HTTP/gRPC/GraphQL)
participant Interceptor as Request<br/>Interceptor
participant Auth as Auth<br/>Middleware
participant TargetService as Target Service
participant Logger as Logger
participant Metrics as Metrics
ClientService->>ClientLib: Make request
ClientLib->>Interceptor: Add correlation ID<br/>Add service auth header<br/>Add request ID
Interceptor->>Logger: Log request start
Interceptor->>Metrics: Track request start
Interceptor->>TargetService: HTTP/gRPC/GraphQL Request<br/>(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<br/>(with correlation ID)
ClientLib->>Metrics: Track success<br/>(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<br/>Variables
participant TargetService as Target Service
participant AuthMiddleware as Auth<br/>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<br/>header with API key
ServiceClient->>TargetService: HTTP Request<br/>(x-service-auth: token)
TargetService->>AuthMiddleware: Extract x-service-auth
AuthMiddleware->>AuthMiddleware: Compare with<br/>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<br/>(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`

View File

@@ -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<br/>Complete?}
ImpactCheck -->|No| Phase1
ImpactCheck -->|Yes| Phase2[Phase 2: Foundation Setup]
Phase2 --> FoundationCheck{Service Starts<br/>& Health Check Passes?}
FoundationCheck -->|No| Phase2
FoundationCheck -->|Yes| Phase3[Phase 3: Core Implementation]
Phase3 --> ImplementationCheck{Business Logic<br/>Implemented?}
ImplementationCheck -->|No| Phase3
ImplementationCheck -->|Yes| Phase4[Phase 4: Integration]
Phase4 --> IntegrationCheck{Routes & Middleware<br/>Working?}
IntegrationCheck -->|No| Phase4
IntegrationCheck -->|Yes| Phase5[Phase 5: Testing]
Phase5 --> TestCheck{Tests Pass<br/>& Coverage Met?}
TestCheck -->|No| Phase5
TestCheck -->|Yes| Phase6[Phase 6: Documentation]
Phase6 --> DocCheck{Docs<br/>Complete?}
DocCheck -->|No| Phase6
DocCheck -->|Yes| Phase7[Phase 7: Cleanup & Verification]
Phase7 --> VerificationCheck{All Checks<br/>Pass?}
VerificationCheck -->|No| Phase7
VerificationCheck -->|Yes| Phase8[Phase 8: Deployment]
Phase8 --> DeployCheck{Staging<br/>Deployed?}
DeployCheck -->|No| Phase8
DeployCheck -->|Yes| Production{Deploy to<br/>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<br/>Zod Validation Schemas]
DTOs --> Repo[2. Create Repository<br/>Prisma Data Access]
Repo --> Service[3. Create Service<br/>Business Logic]
Service --> Controller[4. Create Controller<br/>HTTP Handlers]
Controller --> Module[5. Create Module<br/>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<br/>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/`

View File

@@ -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<void>
```
### 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<br/>Helmet, CORS"]
Security --> RateLimit["Rate Limiting<br/>Middleware"]
RateLimit --> Correlation["Correlation ID<br/>Middleware"]
Correlation --> BodyParsing["Body Parsing<br/>JSON, URLEncoded, Cookies"]
BodyParsing --> Logging["Request Logging<br/>Middleware"]
Logging --> Metrics["Metrics Collection<br/>Middleware"]
Metrics --> Routes["Route Handlers"]
Routes -->|Success| Response([HTTP Response])
Routes -->|Error| ErrorHandler["Error Handler<br/>Middleware"]
Routes -->|Not Found| NotFound["Not Found<br/>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', {<br/>method, url, statusCode,<br/>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

View File

@@ -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<br/>Winston/Pino]
Metrics[Metrics<br/>Prometheus]
Traces[Traces<br/>OpenTelemetry]
end
subgraph "Aggregation & Storage"
Loki[Loki<br/>Log Aggregation]
Prom[Prometheus<br/>Metrics Storage]
Jaeger[Jaeger<br/>Trace Storage]
end
subgraph "Visualization & Alerting"
Grafana[Grafana<br/>Dashboards]
AlertManager[AlertManager<br/>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<br/>(Loki)
participant Dashboard as Grafana Dashboard
Client->>Service: HTTP Request<br/>(with x-correlation-id)
Service->>Service: Generate/Extract<br/>Correlation ID
Service->>Logger: Log Request Start<br/>{correlationId, method, url}
Service->>Service: Process Request
Service->>Logger: Log Business Event<br/>{correlationId, event, data}
Service->>Client: HTTP Response<br/>(with x-correlation-id)
Service->>Logger: Log Request End<br/>{correlationId, status, duration}
Logger->>Aggregator: Send Structured Logs<br/>(JSON format)
Aggregator->>Dashboard: Index & Store Logs
Dashboard->>Dashboard: Query by correlationId<br/>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<br/>http_requests_total]
Gauge[Gauge<br/>active_users]
Histogram[Histogram<br/>request_duration]
end
subgraph "Collection"
Prometheus[Prometheus<br/>Scraper]
end
subgraph "Storage & Query"
PromDB[(Prometheus<br/>Time Series DB)]
end
subgraph "Visualization"
Grafana[Grafana<br/>Dashboards]
Alerts[AlertManager<br/>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<br/>(User Service)
participant ServiceB as Service B<br/>(Order Service)
participant DB as Database
participant Jaeger as Jaeger<br/>Collector
Client->>Gateway: Request<br/>(Trace ID: abc123)
Gateway->>Gateway: Create Root Span<br/>Span: gateway.request
Gateway->>ServiceA: HTTP Call<br/>(Trace ID: abc123,<br/>Span ID: span-1)
ServiceA->>ServiceA: Create Child Span<br/>Span: user.getById
ServiceA->>DB: Query User<br/>(Trace ID: abc123,<br/>Span ID: span-2)
DB-->>ServiceA: User Data
ServiceA->>ServiceA: End Span span-2
ServiceA->>ServiceB: HTTP Call<br/>(Trace ID: abc123,<br/>Span ID: span-3)
ServiceB->>ServiceB: Create Child Span<br/>Span: order.getByUserId
ServiceB->>DB: Query Orders<br/>(Trace ID: abc123,<br/>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<br/>(Trace ID: abc123)
ServiceB->>Jaeger: Export Spans<br/>(Trace ID: abc123)
Gateway->>Jaeger: Export Spans<br/>(Trace ID: abc123)
Note over Jaeger: All spans linked by<br/>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<string, any> = {};
// 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

View File

@@ -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`

View File

@@ -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<br/>Next.js Admin]
webClient[web-client<br/>Next.js Client]
appAdmin[app-admin<br/>Flutter Admin]
appClient[app-client<br/>Flutter Client]
end
subgraph gateway[API Gateway]
traefik[Traefik<br/>Path-based Routing]
end
subgraph services[Services Layer]
iamService[iam-service<br/>IAM Service]
templateService[_template<br/>Service Template]
otherServices[Other Services<br/>Node.js/TypeScript]
end
subgraph packages[Shared Packages]
loggerPackage[@goodgo/logger<br/>Centralized Logging]
typesPackage[@goodgo/types<br/>TypeScript Types]
httpClientPackage[@goodgo/http-client<br/>API Client]
authSdkPackage[@goodgo/auth-sdk<br/>Auth Utilities]
tracingPackage[@goodgo/tracing<br/>OpenTelemetry]
configPackage[@goodgo/config<br/>Shared Configs]
end
subgraph infrastructure[Infrastructure]
postgres[Neon PostgreSQL<br/>Database]
redis[Redis<br/>Cache]
prometheus[Prometheus<br/>Metrics]
grafana[Grafana<br/>Dashboards]
loki[Loki<br/>Log Aggregation]
end
subgraph deployments[Deployments]
dockerCompose[Docker Compose<br/>Local Development]
kubernetes[Kubernetes<br/>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/<br/>Configuration]
serviceModules[modules/<br/>Feature Modules]
serviceMiddlewares[middlewares/<br/>Express Middlewares]
serviceRoutes[routes/<br/>Route Definitions]
serviceMain[main.ts<br/>Entry Point]
servicePrisma[prisma/<br/>Schema & Migrations]
serviceDockerfile[Dockerfile<br/>Container Definition]
servicePackageJson[package.json<br/>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<br/>Main Export]
packagePackageJson[package.json<br/>Package Metadata]
packageTsconfig[tsconfig.json<br/>TypeScript Config]
packageReadme[README.md<br/>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/<br/>Next.js App Router]
appServicesApi[services/api/<br/>API Clients]
appStores[stores/<br/>State Management]
appDockerfile[Dockerfile<br/>Container Definition]
appPackageJson[package.json<br/>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<br/>HTTP Handlers]
moduleService[feature.service.ts<br/>Business Logic]
moduleRepository[feature.repository.ts<br/>Data Access]
moduleDto[feature.dto.ts<br/>Zod Schemas]
moduleTypes[feature.types.ts<br/>TypeScript Types]
moduleTest[feature.controller.test.ts<br/>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<typeof CreateFeatureDto>;
// 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<Feature> {
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)

View File

@@ -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<br/>(HTTP Handler)"] --> Service["Service Layer<br/>(Business Logic)"]
Service --> Repository["Repository<br/>(Data Access)"]
Repository --> Prisma["Prisma Client<br/>(ORM)"]
Prisma --> Database[("Database<br/>(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 {
<<abstract>>
#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 {
<<interface>>
+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<User, CreateUserInput, UpdateUserInput> {
constructor(prisma: PrismaClient) {
super(prisma, 'user');
}
// Add custom methods
async findByEmail(email: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { email },
});
}
async findByUsername(username: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { username },
});
}
}
```
### Custom Query Methods
Add domain-specific query methods:
```typescript
export class UserRepository extends BaseRepository<User, CreateUserInput, UpdateUserInput> {
// Find user with related data
async findWithPermissions(userId: string): Promise<User | null> {
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<User[]> {
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<User, CreateUserInput, UpdateUserInput>
implements IRepository<User, CreateUserInput, UpdateUserInput> {
// Implementation...
}
```
### Error Handling
BaseRepository handles errors automatically:
```typescript
async findById(id: string): Promise<T | null> {
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

View File

@@ -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<br/>(errorThresholdPercentage: 50%)
OPEN --> HALF_OPEN: Reset timeout expires<br/>(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 = <TArgs extends any[], TResult>(
action: (...args: TArgs) => Promise<TResult>,
name: string,
options: Partial<CircuitBreaker.Options> = {}
): CircuitBreaker<TArgs, TResult> => {
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:<br/>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<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
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<T>(
promise: Promise<T>,
timeoutMs: number
): Promise<T> {
const timeout = new Promise<never>((_, 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

View File

@@ -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<br/>Authorization header or cookie
alt Token not found
Middleware->>Client: 401 Unauthorized<br/>(AUTH_REQUIRED)
else Token found
Middleware->>JWTService: verifyAccessToken(token)
alt Token invalid or expired
JWTService->>Middleware: Verification failed
Middleware->>Client: 401 Unauthorized<br/>(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<br/>Authenticated?}
CheckAuth -->|No| Return401[Return 401<br/>AUTH_REQUIRED]
CheckAuth -->|Yes| CheckType{Authorization<br/>Type?}
CheckType -->|Role-Based| CheckRole{User has<br/>Required Role?}
CheckType -->|Permission-Based| CheckPermission{User has<br/>Resource:Action<br/>Permission?}
CheckType -->|Ownership| CheckOwnership{Resource ID<br/>matches User ID?}
CheckRole -->|No| LogDenial[Log Permission Denied<br/>with user roles]
CheckPermission -->|No| LogDenial
CheckOwnership -->|No| LogDenial
LogDenial --> Return403[Return 403<br/>FORBIDDEN]
CheckRole -->|Yes| Allow[Allow Request<br/>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<br/>(16 bytes)
EncryptionService->>Crypto: Create cipher<br/>(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<br/>(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<br/>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<br/>(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<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
async verify(password: string, hash: string): Promise<boolean> {
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<void> {
// 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<string, any>,
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<string, any>): Record<string, any> {
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 = '<script>alert("XSS")</script>';
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('<script>');
});
it('should enforce authentication', async () => {
const response = await request(app)
.get('/api/v1/users');
expect(response.status).toBe(401);
});
it('should enforce authorization', async () => {
const userToken = await createUserToken({ roles: ['user'] });
const response = await request(app)
.delete('/api/v1/users/123')
.set('Authorization', `Bearer ${userToken}`);
expect(response.status).toBe(403);
});
it('should rate limit excessive requests', async () => {
const requests = Array(20).fill(null).map(() =>
request(app).get('/api/v1/users')
);
const responses = await Promise.all(requests);
const rateLimited = responses.filter(r => r.status === 429);
expect(rateLimited.length).toBeGreaterThan(0);
});
});
```
## Security Checklist
Before deploying any service:
- [ ] All endpoints require authentication (except public)
- [ ] Authorization checks implemented (RBAC/ABAC)
- [ ] Input validation with Zod schemas
- [ ] Rate limiting configured
- [ ] Error messages sanitized (no info disclosure)
- [ ] PII encrypted at rest
- [ ] Passwords hashed with bcrypt (cost 12+)
- [ ] Tokens hashed before storing
- [ ] Secrets in environment variables (never hardcoded)
- [ ] HTTPS enforced (TLS 1.2+)
- [ ] CORS configured correctly
- [ ] Security headers set (helmet)
- [ ] Audit logging enabled
- [ ] SQL injection prevented (use Prisma)
- [ ] XSS prevention (input sanitization)
- [ ] File upload validation
- [ ] Security tests passing
- [ ] Dependencies scanned for vulnerabilities
- [ ] Secrets rotation plan in place
## Common Security Anti-Patterns
```typescript
// ❌ BAD: Hardcoded secrets
const SECRET = 'my-secret-key';
// ✅ GOOD: Environment variables
const SECRET = process.env.JWT_SECRET;
// ❌ BAD: Plain text passwords
await prisma.user.create({ data: { password: password } });
// ✅ GOOD: Hashed passwords
await prisma.user.create({
data: { passwordHash: await bcrypt.hash(password, 12) }
});
// ❌ BAD: Exposing user existence
if (!user) {
throw new Error('User not found'); // Reveals user doesn't exist
}
// ✅ GOOD: Generic error
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
throw new Error('Invalid credentials');
}
// ❌ BAD: No input validation
const email = req.body.email;
// ✅ GOOD: Validate with Zod
const { email } = CreateUserDto.parse(req.body);
// ❌ BAD: Stack traces in production
res.status(500).json({ error: error.stack });
// ✅ GOOD: Sanitized errors
res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: 'An error occurred' }
});
```
## Incident Response
```typescript
// Security incident detection and response
export class SecurityIncidentService {
async detectAnomaly(userId: string, event: string, context: any) {
// Check for suspicious patterns
const recentEvents = await this.getRecentEvents(userId, '1h');
if (recentEvents.length > 10) {
await this.triggerAlert('SUSPICIOUS_ACTIVITY', {
userId,
eventCount: recentEvents.length,
timeWindow: '1h'
});
}
// Check for privilege escalation attempts
if (event === 'PERMISSION_DENIED' && context.requiredPermission) {
await this.logSecurityEvent('PRIVILEGE_ESCALATION_ATTEMPT', userId, context);
}
}
async triggerAlert(type: string, details: any) {
// Send to monitoring system
logger.error('Security alert', { type, details });
// TODO: Integrate with PagerDuty, Slack, etc.
}
}
```
## Resources
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)

View File

@@ -1,323 +0,0 @@
---
name: service-discovery-registry
description: Service discovery and registry patterns for GoodGo microservices including service registry, health check orchestration, load balancing strategies, and service mesh integration.
---
# Service Discovery & Registry Patterns
## When to Use This Skill
Use this skill when:
- Implementing service discovery mechanisms
- Managing service registry
- Orchestrating health checks
- Implementing load balancing strategies
- Integrating with service mesh
- Managing dynamic service registration
- Implementing DNS-based discovery
- Building service catalog
## Core Concepts
### Service Discovery Types
1. **Client-Side Discovery**: Client queries service registry
2. **Server-Side Discovery**: Load balancer queries registry (Kubernetes DNS)
3. **Service Mesh**: Automatic service discovery (Istio, Linkerd)
### Service Registry
- **Static Configuration**: Hardcoded service addresses
- **Dynamic Registry**: Services register/unregister dynamically
- **DNS-Based**: Use DNS for service discovery (Kubernetes)
## Service Registration Flow
The service registration lifecycle involves startup registration, periodic heartbeats, and graceful shutdown unregistration.
```mermaid
sequenceDiagram
participant Service as Service Instance
participant Registry as Service Registry
participant Health as Health Check
Note over Service,Registry: Service Startup
Service->>Registry: Register service info<br/>(name, url, version)
Registry->>Registry: Store service metadata
Registry-->>Service: Registration confirmed
Note over Service,Registry: Heartbeat Loop (every 30s)
loop Every 30 seconds
Service->>Health: Check own health status
Health-->>Service: Health status
Service->>Registry: Update registration<br/>(status, lastHeartbeat)
Registry->>Registry: Update service record
end
Note over Service,Registry: Service Shutdown
Service->>Registry: Unregister service
Registry->>Registry: Remove service record
Registry-->>Service: Unregistration confirmed
```
## Health Check Orchestration
Health checks ensure services are available and functioning correctly. The system aggregates health status from multiple services to determine overall system health.
```mermaid
flowchart TD
Start([Health Check Request]) --> GetServices[Get All Services from Registry]
GetServices --> CheckEach{For Each Service}
CheckEach --> CheckHealth[Check Service Health Endpoint]
CheckHealth --> HealthOK{Health OK?}
HealthOK -->|Yes| UpdateHealthy[Update Status: Healthy]
HealthOK -->|No| UpdateUnhealthy[Update Status: Unhealthy]
UpdateHealthy --> CheckTimeout{Last Heartbeat<br/>< 60s?}
UpdateUnhealthy --> CheckTimeout
CheckTimeout -->|Yes| MarkActive[Mark as Active]
CheckTimeout -->|No| MarkStale[Mark as Stale]
MarkActive --> NextService{More Services?}
MarkStale --> NextService
NextService -->|Yes| CheckEach
NextService -->|No| AggregateStatus[Aggregate Overall Status]
AggregateStatus --> CountUnhealthy[Count Unhealthy Services]
CountUnhealthy --> DetermineStatus{Unhealthy Count}
DetermineStatus -->|0| StatusHealthy[Status: Healthy]
DetermineStatus -->|< 50%| StatusDegraded[Status: Degraded]
DetermineStatus -->|>= 50%| StatusUnhealthy[Status: Unhealthy]
StatusHealthy --> ReturnResult[Return Health Status]
StatusDegraded --> ReturnResult
StatusUnhealthy --> ReturnResult
ReturnResult --> End([End])
```
## Load Balancing Strategies
Load balancing distributes requests across multiple service instances. Different strategies are used based on service characteristics and requirements.
```mermaid
flowchart TD
Start([Incoming Request]) --> GetInstances[Get Available Service Instances]
GetInstances --> SelectStrategy{Load Balancing Strategy}
SelectStrategy -->|Round Robin| RoundRobin[Round Robin Algorithm]
SelectStrategy -->|Least Connections| LeastConn[Least Connections Algorithm]
SelectStrategy -->|Weighted| Weighted[Weighted Round Robin]
RoundRobin --> SelectNext[Select Next Instance in Order]
SelectNext --> UseInstance[Use Selected Instance]
LeastConn --> CompareConn[Compare Connection Counts]
CompareConn --> SelectMin[Select Instance with<br/>Minimum Connections]
SelectMin --> UseInstance
Weighted --> CalculateWeight[Calculate Total Weight]
CalculateWeight --> RandomSelect[Random Selection Based on Weights]
RandomSelect --> UseInstance
UseInstance --> ForwardRequest[Forward Request to Instance]
ForwardRequest --> UpdateStats[Update Statistics]
UpdateStats --> End([Request Completed])
style RoundRobin fill:#e1f5ff
style LeastConn fill:#e1f5ff
style Weighted fill:#e1f5ff
```
## Kubernetes DNS Discovery
Kubernetes provides built-in DNS-based service discovery. Services are automatically discoverable via DNS names.
```typescript
// Use Kubernetes DNS for service discovery
const serviceUrl = `http://user-service.production.svc.cluster.local`;
```
### DNS Patterns
- **Short form** (same namespace): `http://user-service`
- **Full form** (cross-namespace): `http://user-service.production.svc.cluster.local`
- **With port**: `http://user-service.production.svc.cluster.local:5000`
## Service Registry Implementation
### Register Service
```typescript
// Register service
await serviceRegistry.register({
name: 'user-service',
version: '1.0.0',
url: 'http://user-service:5000',
healthCheckUrl: 'http://user-service:5000/health',
status: 'healthy',
lastHeartbeat: new Date(),
});
```
### Discover Service
```typescript
// Discover service
const service = await serviceRegistry.discover('user-service');
if (service?.status === 'healthy') {
// Use service
}
```
### List Healthy Services
```typescript
// List all healthy services
const healthyServices = await serviceRegistry.listHealthyServices();
```
## Health Check Aggregation
Aggregate health status from multiple services to determine overall system health.
```typescript
// Aggregate health from multiple services
const health = await healthAggregator.getAggregatedHealth();
// Returns: { status: 'healthy' | 'degraded' | 'unhealthy', services: [...] }
```
## Load Balancing Implementation
### Round Robin
```typescript
const instance = loadBalancer.roundRobin(instances);
```
### Least Connections
```typescript
const instance = loadBalancer.leastConnections(instances);
```
### Weighted Round Robin
```typescript
const instance = loadBalancer.weightedRoundRobin(instances);
```
## Service Mesh Integration
Service mesh solutions like Istio provide automatic service discovery and advanced routing capabilities.
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-service
spec:
hosts:
- external-api.example.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS
```
## Best Practices
1. **Kubernetes DNS**: Use Kubernetes DNS for service discovery in K8s environments
2. **Health Checks**: Implement comprehensive health checks (`/health`, `/health/live`, `/health/ready`)
3. **Service Registry**: Use registry for dynamic services that need runtime discovery
4. **Load Balancing**: Choose appropriate load balancing strategy based on service characteristics
5. **Monitoring**: Monitor service discovery and health check metrics
6. **Heartbeat**: Implement periodic heartbeat (every 30 seconds) to keep registry updated
7. **Graceful Shutdown**: Always unregister services on shutdown to prevent stale entries
8. **Fallback**: Provide fallback mechanisms when registry is unavailable
## Common Mistakes
1. **Hardcoded Service URLs**: Breaks in different environments
```typescript
// ❌ BAD: Hardcoded
const url = 'http://user-service:5000';
// ✅ GOOD: Use discovery or env vars
const url = discovery.getServiceUrl('user-service');
```
2. **No Heartbeat**: Stale registry entries
```typescript
// ❌ BAD: Register once
await registry.register(service);
// ✅ GOOD: Periodic heartbeat
setInterval(() => registry.register(service), 30000);
```
3. **Missing Graceful Shutdown**: Orphaned registrations
```typescript
// ✅ Always unregister on shutdown
process.on('SIGTERM', async () => {
await registry.unregister(serviceName);
process.exit(0);
});
```
4. **No Fallback**: Fails when registry unavailable
```typescript
// ❌ BAD: No fallback
const url = await registry.discover('service');
// ✅ GOOD: Fallback to default
const url = await registry.discover('service')
?? process.env.SERVICE_FALLBACK_URL;
```
## Quick Reference
### Discovery Types
| Discovery Type | Implementation | Use Case |
|----------------|----------------|----------|
| **K8s DNS** | `service.namespace.svc.cluster.local` | Internal services |
| **Service Registry** | Database-backed | Dynamic services |
| **Service Mesh** | Istio/Linkerd | Complex routing |
| **Environment Vars** | `process.env.SERVICE_URL` | Simple/external |
### Health Check Endpoints
| Endpoint | Purpose |
|----------|---------|
| `/health` | Basic health |
| `/health/live` | K8s liveness probe |
| `/health/ready` | K8s readiness probe |
### Load Balancing Strategies
| Strategy | When to Use |
|----------|-------------|
| **Round Robin** | Equal capacity servers |
| **Least Connections** | Varying request durations |
| **Weighted** | Different server capacities |
### Service Registration Lifecycle
```
Startup → Register → Heartbeat (30s) → ... → Shutdown → Unregister
```
## Resources
- [Deployment Kubernetes](./deployment-kubernetes.md) - Kubernetes deployment patterns
- [Observability & Monitoring](./observability-monitoring.md) - Health checks and monitoring
- [Project Rules](./project-rules.md) - GoodGo standards
- Skill Source: `.cursor/skills/service-discovery-registry/SKILL.md`

View File

@@ -1,338 +0,0 @@
---
name: service-layer-patterns
description: Service layer organization and patterns for GoodGo microservices. Use when implementing business logic, organizing service classes, using dependency injection, composing services, or separating concerns between controllers and repositories.
---
# Service Layer Patterns
## When to Use This Skill
Use this skill when:
- Implementing business logic in services
- Organizing service layer code
- Using dependency injection patterns
- Composing multiple services together
- Separating concerns between controllers, services, and repositories
- Handling business rules and validations
- Implementing service composition patterns
## Core Concepts
### Service Layer Architecture
The service layer follows a three-tier architecture pattern, separating concerns between HTTP handling, business logic, and data access:
```mermaid
graph TB
subgraph "HTTP Layer"
Controller["Controller<br/>- HTTP request/response<br/>- Input validation<br/>- Status codes"]
end
subgraph "Business Layer"
Service["Service<br/>- Business logic<br/>- Business rules<br/>- Orchestration<br/>- Caching<br/>- Logging"]
end
subgraph "Data Layer"
Repository["Repository<br/>- Data access<br/>- CRUD operations<br/>- Database queries"]
Database[("Database<br/>Prisma")]
end
Controller -->|"Calls"| Service
Service -->|"Uses"| Repository
Repository -->|"Queries"| Database
style Controller fill:#e1f5ff
style Service fill:#fff4e1
style Repository fill:#e8f5e9
style Database fill:#f3e5f5
```
### Service Layer Responsibilities
The service layer:
- Contains business logic
- Orchestrates repository calls
- Validates business rules
- Handles cross-cutting concerns (caching, logging)
- Coordinates multiple repositories
- Independent of HTTP transport layer
### Dependency Injection
Services use constructor injection for dependencies. The dependency injection flow shows how components are wired together:
```mermaid
graph LR
subgraph "Module Initialization"
Prisma[("Prisma Client")]
Cache[("Cache Service")]
end
subgraph "Dependency Creation"
Repo["UserRepository<br/>(prisma)"]
Service["UserService<br/>(repository, cache)"]
Controller["UserController<br/>(service)"]
end
subgraph "Router Setup"
Router["Router<br/>(controller methods)"]
end
Prisma -->|"Injected"| Repo
Cache -->|"Injected"| Service
Repo -->|"Injected"| Service
Service -->|"Injected"| Controller
Controller -->|"Bound to"| Router
style Prisma fill:#f3e5f5
style Cache fill:#f3e5f5
style Repo fill:#e8f5e9
style Service fill:#fff4e1
style Controller fill:#e1f5ff
style Router fill:#e1f5ff
```
**Example Implementation:**
```typescript
export class UserService {
constructor(
private userRepository: UserRepository,
private cacheService: CacheService
) {}
}
```
## Patterns
### Request Flow Through Layers
The following sequence diagram illustrates how a request flows through the Controller → Service → Repository layers:
```mermaid
sequenceDiagram
participant Client
participant Controller
participant Service
participant Cache
participant Repository
participant Database
Client->>Controller: HTTP Request (GET /users/:id)
Controller->>Controller: Validate input
Controller->>Service: getUserById(id)
Service->>Cache: Check cache
alt Cache Hit
Cache-->>Service: Return cached user
Service-->>Controller: Return user
else Cache Miss
Service->>Repository: findById(id)
Repository->>Database: Query user
Database-->>Repository: User data
Repository-->>Service: User entity
alt User Not Found
Service-->>Controller: Throw NotFoundError
Controller-->>Client: 404 Not Found
else User Found
Service->>Cache: Set cache (TTL: 5min)
Service-->>Controller: Return user
Controller-->>Client: 200 OK + User data
end
end
```
### Basic Service Pattern
```typescript
import { logger } from '@goodgo/logger';
import { userRepository } from '../repositories/user.repository';
import { NotFoundError } from '../errors/http-error';
export class UserService {
async getUserById(id: string) {
logger.info('Fetching user by ID', { id });
const user = await userRepository.findById(id);
if (!user) {
throw new NotFoundError('User', { id });
}
return user;
}
}
```
### Service with Caching
```typescript
export class UserService {
constructor(
private repository: UserRepository,
private cacheService: CacheService
) {}
async getUserById(id: string) {
const cacheKey = cacheService.keys.user(id);
// Try cache first
const cached = await this.cacheService.get<User>(cacheKey);
if (cached) return cached;
// Cache miss - fetch from repository
const user = await this.repository.findById(id);
if (!user) {
throw new NotFoundError('User');
}
// Cache for 5 minutes
await this.cacheService.set(cacheKey, user, 300);
return user;
}
}
```
### Service Composition
Services can depend on other services to compose complex operations. The following diagram shows service composition:
```mermaid
graph TB
subgraph "AccessRequestService"
ARService["AccessRequestService"]
ARRepo["AccessRequestRepository"]
end
subgraph "Dependencies"
UserService["UserService"]
RBACService["RBACService"]
end
subgraph "Supporting Services"
UserRepo["UserRepository"]
RBACRepo["RBACRepository"]
end
ARService -->|"Uses"| UserService
ARService -->|"Uses"| RBACService
ARService -->|"Uses"| ARRepo
UserService -->|"Uses"| UserRepo
RBACService -->|"Uses"| RBACRepo
style ARService fill:#fff4e1
style UserService fill:#fff4e1
style RBACService fill:#fff4e1
style ARRepo fill:#e8f5e9
style UserRepo fill:#e8f5e9
style RBACRepo fill:#e8f5e9
```
**Implementation Example:**
Services can depend on other services:
```typescript
export class AccessRequestService {
constructor(
private accessRequestRepository: AccessRequestRepository,
private userService: UserService,
private rbacService: RBACService
) {}
async createRequest(userId: string, data: CreateRequestDto) {
// Use other services
const user = await this.userService.getUserById(userId);
const hasPermission = await this.rbacService.checkPermission(userId, 'CREATE_REQUEST');
if (!hasPermission) {
throw new ForbiddenError('Insufficient permissions');
}
return await this.accessRequestRepository.create({ ...data, userId });
}
}
```
### Business Logic Validation
Services validate business rules:
```typescript
export class UserService {
async createUser(data: CreateUserInput) {
// Business rule: Check if email exists
const existing = await this.repository.findByEmail(data.email);
if (existing) {
throw new ConflictError('User with this email already exists');
}
// Business rule: Validate organization
if (data.organizationId) {
const org = await this.orgRepository.findById(data.organizationId);
if (!org) {
throw new NotFoundError('Organization');
}
}
return await this.repository.create(data);
}
}
```
### Service Module Pattern
Organize services into modules:
```typescript
export class FeatureModule {
private controller: FeatureController;
private service: FeatureService;
private router: Router;
constructor() {
const repository = new FeatureRepository(prisma);
this.service = new FeatureService(repository);
this.controller = new FeatureController(this.service);
this.router = this.createRouter();
}
getRouter(): Router {
return this.router;
}
private createRouter(): Router {
const router = Router();
router.get('/', asyncHandler(this.controller.findAll.bind(this.controller)));
router.post('/', asyncHandler(this.controller.create.bind(this.controller)));
return router;
}
}
```
## Best Practices
1. **Single Responsibility**: Each service handles one domain area
2. **Dependency Injection**: Use constructor injection for testability
3. **Business Logic Only**: Keep HTTP concerns in controllers
4. **Use Repositories**: Don't access database directly
5. **Error Handling**: Throw appropriate domain errors
6. **Logging**: Log important operations and errors
7. **Caching**: Implement caching in services, not repositories
8. **Composition**: Compose services for complex operations
## Common Mistakes
1. **HTTP in Services**: Using `req`/`res` in services
2. **Direct Database Access**: Accessing Prisma directly instead of repositories
3. **Too Many Responsibilities**: Service doing too many things
4. **No Error Handling**: Not throwing appropriate errors
5. **Business Logic in Controllers**: Moving business logic to controllers
## Resources
- [Feature Service](../../services/iam-service/src/modules/feature/feature.service.ts) - Example service implementation
- [Repository Pattern](../repository-pattern/SKILL.md) - Repository patterns
- [Caching Patterns](../caching-patterns/SKILL.md) - Caching in services
- [Error Handling](../error-handling-patterns/SKILL.md) - Error handling patterns

View File

@@ -1,589 +0,0 @@
---
name: testing-patterns
description: Testing best practices for GoodGo microservices. Use when writing unit tests, integration tests, E2E tests, setting up Jest, mocking dependencies, or debugging test failures.
---
# Testing Patterns for GoodGo Microservices
## When to Use This Skill
Use this skill when:
- Writing unit tests for services, controllers, or repositories
- Creating integration tests for middleware chains
- Building E2E tests for API endpoints
- Setting up Jest configuration for a new service
- Mocking external dependencies (Prisma, Redis, Auth SDK)
- Debugging test failures
- Improving test coverage
## Core Concepts
### Test Types
1. **Unit Tests**: Test individual functions/classes in isolation
- Location: Next to source files (`*.test.ts`)
- Scope: Single function or class
- Dependencies: Mocked
- Speed: Fast (<1s per test)
2. **Integration Tests**: Test component interactions
- Location: `__tests__/` directory
- Scope: Multiple components working together
- Dependencies: Some real, some mocked
- Speed: Medium (1-5s per test)
3. **E2E Tests**: Test complete request/response cycles
- Location: `__tests__/*.e2e.ts`
- Scope: Full API workflow
- Dependencies: Test database, mocked external services
- Speed: Slow (5-10s per test)
### Testing Pyramid
The testing pyramid illustrates the recommended distribution of test types. Most tests should be fast unit tests, with fewer integration tests, and the fewest E2E tests.
```mermaid
graph TD
subgraph pyramid["Testing Pyramid"]
E2E["E2E Tests<br/>Fewest<br/>5-10s each<br/>Full stack"]
Integration["Integration Tests<br/>Medium<br/>1-5s each<br/>Components"]
Unit["Unit Tests<br/>Most<br/>&lt;1s each<br/>Isolated"]
end
Unit --> Integration
Integration --> E2E
style E2E fill:#e1f5ff
style Integration fill:#b3e5fc
style Unit fill:#81d4fa
```
**Key Principles:**
- **Unit Tests (Base)**: Many fast tests covering individual functions/classes
- **Integration Tests (Middle)**: Fewer tests verifying component interactions
- **E2E Tests (Top)**: Fewest tests validating complete workflows
## Jest Configuration
```typescript
// jest.config.ts
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: [
'**/__tests__/**/*.test.ts',
'**/__tests__/**/*.e2e.ts',
'**/?(*.)+(spec|test).ts'
],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/main.ts',
'!src/config/**/*.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
},
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setupTests.ts'],
testTimeout: 10000,
clearMocks: true
};
export default config;
```
## Setup Files
```typescript
// src/__tests__/setupTests.ts
import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaClient } from '@prisma/client';
// Mock Prisma
jest.mock('../prisma', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}));
// Mock Redis
jest.mock('ioredis', () => {
const Redis = jest.requireActual('ioredis-mock');
return Redis;
});
// Global test utilities
global.testUtils = {
generateId: () => `test-${Date.now()}`,
createMockRequest: () => ({
headers: {},
body: {},
query: {},
params: {}
}),
createMockResponse: () => {
const res: any = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
res.send = jest.fn().mockReturnValue(res);
return res;
}
};
beforeEach(() => {
jest.clearAllMocks();
});
```
### Test Execution Flow
The test execution flow shows the lifecycle hooks and execution order in Jest test suites.
```mermaid
flowchart TD
Start([Test Suite Starts]) --> BeforeAll[beforeAll<br/>Run once before all tests]
BeforeAll --> BeforeEach[beforeEach<br/>Run before each test]
BeforeEach --> Test[Execute Test<br/>Arrange-Act-Assert]
Test --> AfterEach[afterEach<br/>Run after each test]
AfterEach --> MoreTests{More Tests?}
MoreTests -->|Yes| BeforeEach
MoreTests -->|No| AfterAll[afterAll<br/>Run once after all tests]
AfterAll --> End([Test Suite Ends])
style Start fill:#c8e6c9
style End fill:#c8e6c9
style Test fill:#fff9c4
style BeforeAll fill:#e1bee7
style AfterAll fill:#e1bee7
style BeforeEach fill:#bbdefb
style AfterEach fill:#bbdefb
```
**Execution Order:**
1. `beforeAll`: Setup that runs once for the entire suite (e.g., database connection)
2. `beforeEach`: Setup that runs before each test (e.g., reset mocks, create test data)
3. **Test Execution**: The actual test code following AAA pattern
4. `afterEach`: Cleanup after each test (e.g., clear mocks, reset state)
5. `afterAll`: Final cleanup after all tests (e.g., close database connection)
## Testing Patterns
### Unit Test Pattern
```typescript
// feature.service.test.ts
import { FeatureService } from './feature.service';
import { mockDeep } from 'jest-mock-extended';
describe('FeatureService', () => {
let service: FeatureService;
let mockRepository: any;
beforeEach(() => {
mockRepository = {
findById: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn()
};
service = new FeatureService(mockRepository);
});
describe('findById', () => {
it('should return feature when found', async () => {
// Arrange
const mockFeature = { id: '1', name: 'Test Feature' };
mockRepository.findById.mockResolvedValue(mockFeature);
// Act
const result = await service.findById('1');
// Assert
expect(result).toEqual(mockFeature);
expect(mockRepository.findById).toHaveBeenCalledWith('1');
});
it('should throw error when feature not found', async () => {
// Arrange
mockRepository.findById.mockResolvedValue(null);
// Act & Assert
await expect(service.findById('999')).rejects.toThrow('Feature not found');
});
});
});
```
### Integration Test Pattern
```typescript
// auth.middleware.test.ts
import { authMiddleware } from '../auth.middleware';
import { createMockRequest, createMockResponse } from '../../test-utils';
import jwt from 'jsonwebtoken';
describe('Auth Middleware', () => {
const mockNext = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should call next when valid token provided', async () => {
// Arrange
const req = createMockRequest();
const res = createMockResponse();
const token = jwt.sign({ userId: '123' }, 'secret');
req.headers.authorization = `Bearer ${token}`;
// Act
await authMiddleware(req, res, mockNext);
// Assert
expect(mockNext).toHaveBeenCalled();
expect(req.user).toEqual({ userId: '123' });
});
it('should return 401 when no token provided', async () => {
// Arrange
const req = createMockRequest();
const res = createMockResponse();
// Act
await authMiddleware(req, res, mockNext);
// Assert
expect(res.status).toHaveBeenCalledWith(401);
expect(mockNext).not.toHaveBeenCalled();
});
});
```
### E2E Test Pattern
```typescript
// feature.e2e.ts
import supertest from 'supertest';
import { createApp } from '../app';
import { prisma } from '../prisma';
describe('Feature API E2E', () => {
let app: any;
let request: any;
beforeAll(async () => {
app = await createApp();
request = supertest(app);
});
afterAll(async () => {
await prisma.$disconnect();
});
beforeEach(async () => {
await prisma.feature.deleteMany();
});
describe('POST /api/features', () => {
it('should create a new feature', async () => {
// Arrange
const featureData = {
name: 'New Feature',
description: 'Feature description'
};
// Act
const response = await request
.post('/api/features')
.set('Authorization', 'Bearer valid-token')
.send(featureData)
.expect(201);
// Assert
expect(response.body).toMatchObject({
success: true,
data: {
name: 'New Feature',
description: 'Feature description'
}
});
const created = await prisma.feature.findFirst({
where: { name: 'New Feature' }
});
expect(created).toBeDefined();
});
});
});
```
## Mocking Strategies
### Mocking Strategy by Test Type
Different test types require different levels of mocking. This diagram shows what should be mocked at each test level.
```mermaid
graph TB
subgraph UnitTests["Unit Tests"]
UnitCode[Application Code]
UnitMock[All Dependencies Mocked<br/>- Database<br/>- External APIs<br/>- Services<br/>- Repositories]
UnitCode --> UnitMock
end
subgraph IntegrationTests["Integration Tests"]
IntCode[Application Code]
IntReal[Real Internal Components]
IntMock[External Dependencies Mocked<br/>- External APIs<br/>- Third-party Services]
IntCode --> IntReal
IntReal --> IntMock
end
subgraph E2ETests["E2E Tests"]
E2ECode[Application Code]
E2EReal[Real Internal Stack<br/>- Database<br/>- Services<br/>- Middleware]
E2EMock[Only External Services Mocked<br/>- Payment APIs<br/>- Email Services<br/>- Third-party APIs]
E2ECode --> E2EReal
E2EReal --> E2EMock
end
style UnitTests fill:#ffcdd2
style IntegrationTests fill:#fff9c4
style E2ETests fill:#c8e6c9
style UnitMock fill:#f8bbd0
style IntMock fill:#ffe082
style E2EMock fill:#a5d6a7
```
**Mocking Guidelines:**
- **Unit Tests**: Mock all external dependencies to test logic in isolation
- **Integration Tests**: Use real internal components, mock only external services
- **E2E Tests**: Use real database and internal stack, mock only third-party external services
### Mock Prisma
```typescript
// __mocks__/prisma.ts
import { PrismaClient } from '@prisma/client';
import { mockDeep, DeepMockProxy } from 'jest-mock-extended';
export const prismaMock = mockDeep<PrismaClient>();
jest.mock('../src/prisma', () => ({
__esModule: true,
default: prismaMock,
}));
// Usage in tests
import { prismaMock } from '../__mocks__/prisma';
test('should create user', async () => {
const user = { id: '1', email: 'test@example.com' };
prismaMock.user.create.mockResolvedValue(user);
const result = await createUser({ email: 'test@example.com' });
expect(result).toEqual(user);
});
```
### Mock Redis
```typescript
// __mocks__/redis.ts
import Redis from 'ioredis-mock';
export const redisMock = new Redis();
// Usage in tests
test('should cache value', async () => {
const cache = new CacheService(redisMock);
await cache.set('key', 'value');
const result = await cache.get('key');
expect(result).toBe('value');
});
```
### Mock External APIs
```typescript
// Mock axios
jest.mock('axios');
import axios from 'axios';
const mockedAxios = axios as jest.Mocked<typeof axios>;
test('should fetch external data', async () => {
mockedAxios.get.mockResolvedValue({
data: { result: 'success' }
});
const result = await fetchExternalData();
expect(result).toEqual({ result: 'success' });
});
```
## Testing Utilities
```typescript
// test-utils.ts
export class TestFactory {
static createUser(overrides = {}) {
return {
id: 'test-user-1',
email: 'test@example.com',
name: 'Test User',
createdAt: new Date(),
...overrides
};
}
static createAuthToken(userId: string) {
return jwt.sign({ userId }, 'test-secret');
}
static async cleanDatabase() {
await prisma.user.deleteMany();
await prisma.feature.deleteMany();
}
}
// Usage
const user = TestFactory.createUser({ name: 'Custom Name' });
const token = TestFactory.createAuthToken(user.id);
```
## Common Test Scenarios
### Testing Error Handling
```typescript
test('should handle database errors gracefully', async () => {
prismaMock.user.findUnique.mockRejectedValue(
new Error('Database connection failed')
);
const response = await request
.get('/api/users/123')
.expect(500);
expect(response.body).toEqual({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'Internal server error'
}
});
});
```
### Testing Validation
```typescript
describe('Validation', () => {
it('should reject invalid email', async () => {
const response = await request
.post('/api/auth/register')
.send({ email: 'invalid-email', password: '123456' })
.expect(400);
expect(response.body.error.code).toBe('VALIDATION_ERROR');
});
});
```
### Testing Pagination
```typescript
test('should paginate results', async () => {
// Create test data
const items = Array(25).fill(null).map((_, i) => ({
id: `item-${i}`,
name: `Item ${i}`
}));
prismaMock.item.findMany.mockResolvedValue(items.slice(0, 10));
prismaMock.item.count.mockResolvedValue(25);
const response = await request
.get('/api/items?page=1&limit=10')
.expect(200);
expect(response.body).toEqual({
success: true,
data: items.slice(0, 10),
pagination: {
page: 1,
limit: 10,
total: 25,
totalPages: 3
}
});
});
```
## Test Commands
```json
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:unit": "jest --testPathPattern=\\.test\\.ts$",
"test:e2e": "jest --testPathPattern=\\.e2e\\.ts$",
"test:ci": "jest --coverage --silent --maxWorkers=2"
}
}
```
## Debugging Tests
### Debug with VS Code
```json
// .vscode/launch.json
{
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["test", "--", "--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
```
### Debug Tips
1. Use `test.only()` to run single test
2. Use `--detectOpenHandles` for async issues
3. Use `--runInBand` for sequential execution
4. Add `console.log()` statements temporarily
5. Use debugger breakpoints in VS Code
## Best Practices Checklist
- [ ] Each test is independent and isolated
- [ ] Tests follow AAA pattern (Arrange-Act-Assert)
- [ ] Mock external dependencies
- [ ] Test edge cases and error scenarios
- [ ] Keep tests simple and focused
- [ ] Use descriptive test names
- [ ] Maintain >70% code coverage
- [ ] Run tests before committing
- [ ] Keep test data realistic
- [ ] Clean up after tests

View File

@@ -1,578 +0,0 @@
# Thiết Kế API RESTful
RESTful API design standards for GoodGo microservices. Use when creating new API endpoints, designing DTOs, implementing controllers, writing OpenAPI documentation, or standardizing API responses.
> Tiêu chuẩn thiết kế API RESTful cho các microservices của GoodGo. Sử dụng khi tạo endpoint API mới, thiết kế DTOs, triển khai controllers, viết tài liệu OpenAPI, hoặc chuẩn hóa response API.
## Tổng Quan
This skill covers the RESTful API design patterns and standards used across all GoodGo microservices. It ensures consistency, predictability, and maintainability of APIs through standardized URL structures, HTTP methods, response formats, error handling, and DTO validation.
Skill này bao gồm các pattern và tiêu chuẩn thiết kế API RESTful được sử dụng trong tất cả các microservices của GoodGo. Nó đảm bảo tính nhất quán, dự đoán được và dễ bảo trì của APIs thông qua cấu trúc URL chuẩn hóa, HTTP methods, định dạng response, xử lý lỗi và validation DTO.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Tạo endpoint API mới
- Thiết kế DTOs cho request/response
- Triển khai controllers và routes
- Viết tài liệu OpenAPI/Swagger
- Chuẩn hóa error responses
- Triển khai pagination, filtering và sorting
- Thiết lập API versioning
- Thiết kế quan hệ giữa các resources
## Khái Niệm Chính
### Luồng Request/Response
Sơ đồ sau minh họa cách một request đi qua các lớp của API:
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<br/>Rate Limiting<br/>Validation
Middleware->>Controller: Validated Request
Controller->>Controller: Parse DTO<br/>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<br/>Format Response
Controller-->>Middleware: Response Object
Middleware-->>Client: HTTP Response
```
### Cấu Trúc Phân Cấp API
Sơ đồ sau cho thấy cấu trúc phân cấp của các RESTful API endpoints:
The following diagram shows the hierarchical structure of RESTful API endpoints:
```mermaid
graph TD
A[API Base URL<br/>https://api.goodgo.com] --> B[Version<br/>/v1]
B --> C[Resource Collection<br/>/users]
B --> D[Resource Collection<br/>/orders]
B --> E[Resource Collection<br/>/products]
C --> F[Resource Instance<br/>/users/:id]
C --> G[Sub-Resource<br/>/users/:id/orders]
F --> H[GET /users/:id<br/>Retrieve user]
F --> I[PUT /users/:id<br/>Update user]
F --> J[DELETE /users/:id<br/>Delete user]
C --> K[GET /users<br/>List users]
C --> L[POST /users<br/>Create user]
G --> M[GET /users/:id/orders<br/>List user orders]
G --> N[POST /users/:id/orders<br/>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
```
### Cấu Trúc URL
URLs follow a hierarchical resource-based structure with versioning.
URLs tuân theo cấu trúc phân cấp dựa trên resource với versioning.
```
https://api.goodgo.com/v1/{resource}/{id}/{sub-resource}
Examples:
GET /v1/users # List users / Liệt kê users
POST /v1/users # Create user / Tạo user
GET /v1/users/123 # Get user by ID / Lấy user theo ID
PUT /v1/users/123 # Update user / Cập nhật user
DELETE /v1/users/123 # Delete user / Xóa user
GET /v1/users/123/orders # Get user's orders / Lấy orders của user
POST /v1/users/123/orders # Create order for user / Tạo order cho user
```
### Phương Thức HTTP
- **GET**: Retrieve resource(s) - Safe, Idempotent / Lấy resource(s) - An toàn, Idempotent
- **POST**: Create new resource - Not idempotent / Tạo resource mới - Không idempotent
- **PUT**: Full update - Idempotent / Cập nhật toàn bộ - Idempotent
- **PATCH**: Partial update - Idempotent / Cập nhật một phần - Idempotent
- **DELETE**: Remove resource - Idempotent / Xóa resource - Idempotent
### Định Dạng Response Chuẩn
All API responses follow a consistent structure with `success`, `data`, and optional `metadata` fields.
Tất cả API responses tuân theo cấu trúc nhất quán với các trường `success`, `data`, và `metadata` tùy chọn.
## Các Pattern Thường Dùng
### Pattern Response Thành Công
```typescript
interface SuccessResponse<T> {
success: true;
data: T;
message?: string;
timestamp?: string;
pagination?: {
total: number;
skip: number;
take: number;
};
}
```
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/identity/user/user.controller.ts
res.json({
success: true,
data: {
users: result.users,
pagination: {
total: result.total,
skip: filters.skip,
take: filters.take,
},
},
});
```
### Pattern Response Lỗi
```typescript
interface ErrorResponse {
success: false;
error: {
code: string;
message: string;
details?: any;
field?: string;
};
timestamp?: string;
}
```
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/identity/user/user.controller.ts
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid filters',
details: error.errors,
},
});
return;
}
```
### Mã Trạng Thái HTTP
```typescript
// Mã thành công
200 OK // GET, PUT, PATCH success
201 Created // POST success with resource creation
204 No Content // DELETE success
// Lỗi client
// Dữ liệu request không hợp lệ
// Thiếu/sai xác thực
// Xác thực hợp lệ nhưng không có quyền
// Resource không tồn tại
// Xung đột resource (trùng lặp)
// Lỗi validation
// Lỗi server
// Lỗi server không mong đợi
// Lỗi dịch vụ bên ngoài
// Dịch vụ tạm thời không khả dụng
```
### DTOs với Zod Validation
DTOs use Zod for runtime validation with bilingual error messages.
DTOs sử dụng Zod cho validation runtime với thông báo lỗi song ngữ.
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/identity/identity.dto.ts
import { z } from 'zod';
export const CreateOrganizationDto = z.object({
name: z.string().min(1, 'Organization name is required / Tên tổ chức là bắt buộc').max(255),
domain: z.string().email().optional().or(z.string().min(1).max(255).optional()),
parentId: z.string().optional(),
settings: z.record(z.any()).optional(),
});
export type CreateOrganizationDto = z.infer<typeof CreateOrganizationDto>;
export const UserFiltersDto = z.object({
organizationId: z.string().optional(),
isActive: z.boolean().optional(),
emailVerified: z.boolean().optional(),
search: z.string().optional(),
skip: z.number().int().min(0).default(0),
take: z.number().int().min(1).max(100).default(20),
});
```
### Pattern Triển Khai Controller
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/identity/user/user.controller.ts
export class UserManagementController {
/**
List users
* VI: Liệt kê users
*/
async list(req: Request, res: Response): Promise<void> {
try {
const filters = UserFiltersDto.parse({
organizationId: req.query.organizationId as string,
isActive: req.query.isActive === 'true' ? true : req.query.isActive === 'false' ? false : undefined,
emailVerified: req.query.emailVerified === 'true' ? true : req.query.emailVerified === 'false' ? false : undefined,
search: req.query.search as string,
skip: parseInt(req.query.skip as string) || 0,
take: parseInt(req.query.take as string) || 20,
});
const result = await userManagementService.searchUsers(filters);
res.json({
success: true,
data: {
users: result.users,
pagination: {
total: result.total,
skip: filters.skip,
take: filters.take,
},
},
});
} catch (error: any) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid filters',
details: error.errors,
},
});
return;
}
res.status(500).json({
success: false,
error: {
code: 'LIST_USERS_FAILED',
message: error.message || 'Failed to list users',
},
});
}
}
/**
Get user by ID
* VI: Lấy user theo ID
*/
async get(req: Request, res: Response): Promise<void> {
try {
const { id } = req.params;
const user = await userManagementService.getUser(id);
res.json({
success: true,
data: user,
});
} catch (error: any) {
if (error instanceof NotFoundError) {
res.status(404).json(error.toApiResponse());
return;
}
res.status(500).json({
success: false,
error: {
code: 'GET_USER_FAILED',
message: error.message || 'Failed to get user',
},
});
}
}
}
```
### Luồng Xử Lý Lỗi
Sơ đồ sau minh họa luồng xử lý lỗi trong API:
The following diagram illustrates the error handling flow in the API:
```mermaid
flowchart TD
A[Request Received] --> B{Validation<br/>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<br/>Middleware]
F --> L
G --> L
H --> L
I --> L
K --> L
L --> M{Error Type?}
M -->|ValidationError| N[400 Bad Request<br/>VALIDATION_ERROR]
M -->|UnauthorizedError| O[401 Unauthorized<br/>UNAUTHORIZED]
M -->|ForbiddenError| P[403 Forbidden<br/>FORBIDDEN]
M -->|NotFoundError| Q[404 Not Found<br/>NOT_FOUND]
M -->|ConflictError| R[409 Conflict<br/>CONFLICT]
M -->|Unknown Error| S{Development<br/>Mode?}
S -->|Yes| T[500 Internal Error<br/>INTERNAL_ERROR<br/>+ Stack Trace]
S -->|No| U[500 Internal Error<br/>INTERNAL_ERROR<br/>Generic Message]
N --> V[Format Error Response<br/>success: false<br/>error: code, message, details]
O --> V
P --> V
Q --> V
R --> V
T --> V
U --> V
V --> W[Log Error<br/>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
```
### Xử Lý Lỗi với Custom Error Classes
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/errors/http-error.ts
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.name = this.constructor.name;
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(),
};
}
}
export class NotFoundError extends HttpError {
constructor(resource: string = 'Resource / Tài nguyên', details?: any) {
super(`${resource} not found / ${resource} không tìm thấy`, 404, 'NOT_FOUND', true, details);
}
}
export class ValidationError extends HttpError {
constructor(message: string = 'Validation failed / Validation thất bại', details?: any) {
super(message, 422, 'VALIDATION_ERROR', true, details);
}
}
```
## Thực Hành Tốt Nhất
### Đặt Tên Resource
- ✅ Use plural nouns (`/users` not `/user`) / Sử dụng danh từ số nhiều
- ✅ Use kebab-case for multi-word resources (`/user-profiles`) / Sử dụng kebab-case cho resource nhiều từ
- ✅ Keep URLs as short as possible / Giữ URLs ngắn gọn nhất có thể
- ❌ Avoid verbs in URLs (use HTTP methods instead) / Tránh động từ trong URLs (dùng HTTP methods thay thế)
### Phiên Bản Hóa
- ✅ Include version in URL (`/v1/users`) / Bao gồm version trong URL
- ✅ Maintain backward compatibility / Duy trì tương thích ngược
- ✅ Deprecate old versions gracefully with warnings / Ngừng hỗ trợ các version cũ một cách lịch sự với cảnh báo
### Bảo Mật
- ✅ Always use HTTPS / Luôn sử dụng HTTPS
- ✅ Implement rate limiting / Triển khai rate limiting
- ✅ Validate all inputs with DTOs / Validate tất cả inputs với DTOs
- ✅ Use proper authentication/authorization middleware / Sử dụng middleware xác thực/ủy quyền phù hợp
- ✅ Never expose sensitive data in responses / Không bao giờ expose dữ liệu nhạy cảm trong responses
### Hiệu Năng
- ✅ Implement pagination for lists (use `skip` and `take`) / Triển khai pagination cho danh sách
- ✅ Use field filtering when possible (`select` in Prisma) / Sử dụng field filtering khi có thể
- ✅ Cache responses appropriately / Cache responses phù hợp
- ✅ Compress responses (gzip) / Nén responses (gzip)
### Tài Liệu
- ✅ Keep OpenAPI spec up to date / Giữ OpenAPI spec cập nhật
- ✅ Include examples in documentation / Bao gồm ví dụ trong tài liệu
- ✅ Document error responses / Tài liệu hóa error responses
- ✅ Version your documentation / Phiên bản hóa tài liệu
### Xử Lý Lỗi
- ✅ Use consistent error response format / Sử dụng định dạng error response nhất quán
- ✅ Provide actionable error messages / Cung cấp thông báo lỗi có thể hành động
- ✅ Include error codes for programmatic handling / Bao gồm error codes cho xử lý lập trình
- ✅ Log errors server-side, don't expose stack traces in production / Log lỗi phía server, không expose stack traces trong production
## Ví Dụ Từ Dự Án
### Ví Dụ Controller
- **User Management**: [`services/iam-service/src/modules/identity/user/user.controller.ts`](../../../services/iam-service/src/modules/identity/user/user.controller.ts)
- **Feature Controller**: [`services/iam-service/src/modules/feature/feature.controller.ts`](../../../services/iam-service/src/modules/feature/feature.controller.ts)
- **RBAC Controller**: [`services/iam-service/src/modules/rbac/rbac.controller.ts`](../../../services/iam-service/src/modules/rbac/rbac.controller.ts)
### Ví Dụ DTO
- **Identity DTOs**: [`services/iam-service/src/modules/identity/identity.dto.ts`](../../../services/iam-service/src/modules/identity/identity.dto.ts)
- **Auth DTOs**: [`services/iam-service/src/modules/auth/auth.dto.ts`](../../../services/iam-service/src/modules/auth/auth.dto.ts)
- **RBAC DTOs**: [`services/iam-service/src/modules/rbac/rbac.dto.ts`](../../../services/iam-service/src/modules/rbac/rbac.dto.ts)
### Ví Dụ Xử Lý Lỗi
- **HTTP Error Classes**: [`services/iam-service/src/errors/http-error.ts`](../../../services/iam-service/src/errors/http-error.ts)
- **Error Middleware**: [`services/iam-service/src/middlewares/error.middleware.ts`](../../../services/iam-service/src/middlewares/error.middleware.ts)
## Tham Khảo Nhanh
### Bảng Tra Cứu Định Dạng Response
| Scenario / Tình Huống | Status Code | Response Structure / Cấu Trúc Response |
|----------------------|-------------|----------------------------------------|
| Success (GET) | 200 | `{ success: true, data: {...} }` |
| Success (POST) | 201 | `{ success: true, data: {...} }` |
| Success (DELETE) | 200 | `{ success: true, message: "..." }` |
| Validation Error / Lỗi Validation | 400/422 | `{ success: false, error: { code, message, details } }` |
| Not Found | 404 | `{ success: false, error: { code: "NOT_FOUND", message } }` |
| Unauthorized | 401 | `{ success: false, error: { code: "UNAUTHORIZED", message } }` |
| Forbidden | 403 | `{ success: false, error: { code: "FORBIDDEN", message } }` |
| Server Error / Lỗi Server | 500 | `{ success: false, error: { code: "INTERNAL_ERROR", message } }` |
### Pattern DTO Thường Dùng
```typescript
// DTO Tạo
const CreateDto = z.object({
requiredField: z.string().min(1),
optionalField: z.string().optional(),
});
// DTO Cập Nhật (tất cả fields tùy chọn)
const UpdateDto = z.object({
field1: z.string().optional(),
field2: z.number().optional(),
});
// DTO Query/Filter
const QueryDto = z.object({
page: z.number().int().min(1).default(1),
limit: z.number().int().min(1).max(100).default(20),
search: z.string().optional(),
sortBy: z.string().optional(),
order: z.enum(['asc', 'desc']).default('desc'),
});
```
## Skills Liên Quan
- **[Database Prisma](./database-prisma.md)**: For database operations and repository patterns / Cho các thao tác database và repository patterns
- **[Security](./security.md)**: For authentication, authorization, and security best practices / Cho xác thực, ủy quyền và thực hành bảo mật tốt nhất
- **[Testing Patterns](./testing-patterns.md)**: For testing API endpoints / Cho test API endpoints
- **[Documentation](./documentation.md)**: For writing API documentation / Cho viết tài liệu API
## Tài Nguyên
### Tài Liệu Bên Ngoài
- [REST API Design Best Practices](https://restfulapi.net/)
- [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
- [Zod Documentation](https://zod.dev/)
- [OpenAPI Specification](https://swagger.io/specification/)
### Tài Liệu Nội Bộ
- [API Reference](../api/openapi/iam-service.yaml)
- [Service Communication](../architecture/service-communication.md)
- [Project Rules](./project-rules.md)

View File

@@ -1,197 +0,0 @@
# API Gateway Nâng Cao (API Gateway Advanced)
Advanced API Gateway patterns for GoodGo microservices including API composition, request/response transformation, service mesh integration, advanced routing, and gateway-level resilience.
> Các patterns API Gateway nâng cao cho GoodGo microservices bao gồm API composition, request/response transformation, tích hợp service mesh, routing nâng cao, và resilience ở gateway level.
## Tổng Quan
Advanced API Gateway patterns extend basic gateway functionality with composition, transformation, service mesh integration, and gateway-level resilience patterns.
Các patterns API Gateway nâng cao mở rộng chức năng gateway cơ bản với composition, transformation, tích hợp service mesh, và các resilience patterns ở gateway level.
## Khi Nào Sử Dụng
Use this skill when implementing API composition, request/response transformation, or service mesh integration.
Sử dụng skill này khi implement API composition, request/response transformation, hoặc tích hợp service mesh.
## API Gateway Architecture / Kiến Trúc API Gateway
The API Gateway serves as the single entry point for all client requests, handling routing, composition, transformation, and resilience patterns.
API Gateway hoạt động như điểm vào duy nhất cho tất cả client requests, xử lý routing, composition, transformation, và các 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 / Luồng Request Routing
Requests flow through the gateway middleware chain in a specific order, ensuring proper handling at each stage.
Requests đi qua chuỗi middleware của gateway theo thứ tự cụ thể, đảm bảo xử lý đúng ở mỗi giai đoạn.
```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 / Các Patterns API Composition
API composition enables the gateway to aggregate data from multiple services, reducing client round trips and improving performance.
API composition cho phép gateway tổng hợp dữ liệu từ nhiều services, giảm số lần round trip của client và cải thiện hiệu suất.
### Fan-Out / Fan-In Pattern (Parallel Aggregation) / Pattern Fan-Out / Fan-In (Tổng Hợp Song Song)
```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) / Pattern Chaining (Gọi Tuần Tự với 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
```
## Các Patterns Chính
### API Composition / API Composition
```typescript
// EN: Aggregate multiple service responses
// VI: Tổng hợp responses từ nhiều services
const [user, orders, payments] = await Promise.all([
userClient.get(`/users/${userId}`),
orderClient.get(`/orders?userId=${userId}`),
paymentClient.get(`/payments?userId=${userId}`),
]);
```
## Best Practices / Thực Hành Tốt
1. Use API composition for aggregating related data / Sử dụng API composition để tổng hợp dữ liệu liên quan
2. Cache at gateway / Cache ở gateway
3. Implement circuit breaker at gateway / Implement circuit breaker ở gateway
## Skills Liên Quan
- [Middleware Patterns](./middleware-patterns.md) - Middleware patterns / Các patterns middleware
- [Resilience Patterns](./resilience-patterns.md) - Circuit breaker / Circuit breaker
## Tài Nguyên
- Skill Source: `.cursor/skills/api-gateway-advanced/SKILL.md`

View File

@@ -1,456 +0,0 @@
# Chiến Lược Versioning API (API Versioning Strategy)
API versioning strategies for GoodGo microservices including semantic versioning, backward compatibility patterns, API deprecation, version negotiation, and breaking changes handling.
> Các chiến lược versioning API cho GoodGo microservices bao gồm semantic versioning, backward compatibility patterns, API deprecation, version negotiation, và xử lý breaking changes.
## Tổng Quan
API versioning strategies enable managing API evolution while maintaining compatibility and clearly communicating changes to consumers.
Các chiến lược versioning API cho phép quản lý sự phát triển API đồng thời duy trì tương thích và giao tiếp rõ ràng các thay đổi tới consumers.
## Khi Nào Sử Dụng
Use this skill when versioning APIs, handling breaking changes, or implementing deprecation.
Sử dụng skill này khi:
- Versioning APIs
- Xử lý breaking changes
- Implement API deprecation
- Duy trì backward compatibility
- Implement version negotiation
- Quản lý nhiều API versions
- Lập kế hoạch phát triển API
- Giao tiếp các thay đổi API với consumers
## Các Khái Niệm Cốt Lõi
### Các Chiến Lược Versioning
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 (ví dụ: 1.2.3)
### Các Loại Tương Thích
- **Backward Compatible**: Version mới hoạt động với clients cũ
- **Forward Compatible**: Version cũ hoạt động với clients mới
- **Breaking Changes**: Thay đổi không tương thích yêu cầu version mới
## 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.
Version negotiation cho phép clients yêu cầu một API version cụ thể thông qua headers trong khi duy trì URLs sạch. Middleware trích xuất version từ header `Accept` và định tuyến tới handler phù hợp.
```mermaid
sequenceDiagram
participant Client
participant Middleware as Version Negotiation<br/>Middleware
participant Controller as Version-Aware<br/>Controller
participant Service
Client->>Middleware: Request with Accept header<br/>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
// EN: Version negotiation middleware
// VI: Middleware version negotiation
import { Request, Response, NextFunction } from 'express';
import { logger } from '@goodgo/logger';
export function versionNegotiation(
req: Request,
res: Response,
next: NextFunction
): void {
// EN: Extract version from Accept header
// VI: Trích xuất version từ Accept header
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;
// EN: Check if version is supported
// VI: Kiểm tra xem version có được hỗ trợ không
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 {
// EN: Default to latest version
// VI: Mặc định version mới nhất
req.apiVersion = 2;
}
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.
API deprecation tuân theo một timeline có cấu trúc để cung cấp cho consumers đủ thời gian để migrate. Vòng đời tiến triển qua các giai đoạn active, deprecated, sunset, và removed.
```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<br/>Add Deprecation Headers
Deprecated --> Sunset: Sunset Date Reached<br/>Stop Accepting New Requests
Sunset --> Removed: Grace Period Ended<br/>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
// EN: Deprecation warning middleware
// VI: Middleware cảnh báo deprecation
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();
};
}
// Usage
router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
```
## Migration Flow
Breaking changes require a careful 3-phase migration strategy to ensure zero downtime and smooth client transitions.
Breaking changes yêu cầu một chiến lược migration 3 giai đoạn cẩn thận để đảm bảo zero downtime và chuyển đổi client mượt mà.
```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<br/>v2 adoption?}
Phase2 -->|No| Wait1
Phase2 -->|Yes| Phase2Start[Phase 2: Deprecate v1]
Phase2Start --> AddHeaders[Add deprecation headers to v1]
AddHeaders --> NotifyClients[Notify clients via<br/>deprecation warnings]
NotifyClients --> ProvideGuide[Provide migration guide]
ProvideGuide --> Monitor2[Monitor migration progress]
Monitor2 --> Wait2[Wait until sunset date]
Wait2 --> Phase3{Sunset date<br/>reached?}
Phase3 -->|No| Monitor2
Phase3 -->|Yes| Phase3Start[Phase 3: Remove v1]
Phase3Start --> StopAccepting[Stop accepting new v1 requests]
StopAccepting --> GracePeriod[Grace period for<br/>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
// EN: Migration strategy for breaking changes
// VI: Chiến lược migration cho breaking changes
export class MigrationStrategy {
/**
* EN: Phase 1: Support both versions
* VI: Giai đoạn 1: Hỗ trợ cả hai versions
*/
phase1SupportBoth(): void {
// EN: Keep v1, add v2
// VI: Giữ v1, thêm v2
router.use('/v1', v1Router);
router.use('/v2', v2Router);
}
/**
* EN: Phase 2: Deprecate v1
* VI: Giai đoạn 2: Deprecate v1
*/
phase2DeprecateV1(): void {
router.use('/v1', deprecationMiddleware('2', '2024-12-31'), v1Router);
router.use('/v2', v2Router);
}
/**
* EN: Phase 3: Remove v1
* VI: Giai đoạn 3: Xóa v1
*/
phase3RemoveV1(): void {
// EN: After sunset date, remove v1 routes
// VI: Sau sunset date, xóa v1 routes
router.use('/v2', v2Router);
}
}
```
## URL Path Versioning
### Implementation
```typescript
// src/routes/index.ts
// EN: Route versioning
// VI: Route versioning
import { Router } from 'express';
import v1Router from './v1';
import v2Router from './v2';
const router = Router();
// EN: Version 1 routes
// VI: Routes version 1
router.use('/v1', v1Router);
// EN: Version 2 routes
// VI: Routes version 2
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
// EN: Add version to response
// VI: Thêm version vào response
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
// EN: Backward compatibility adapter
// VI: Adapter tương thích ngược
export class CompatibilityAdapter {
/**
* EN: Adapt v1 response to v2 format
* VI: Adapt response v1 sang format v2
*/
adaptV1ToV2(v1Data: any): any {
return {
success: true,
data: {
user: {
...v1Data,
profile: null, // EN: Add default for new field / VI: Thêm mặc định cho field mới
},
},
metadata: {
version: '2.0.0',
adapted: true,
},
};
}
/**
* EN: Adapt v2 request to v1 format
* VI: Adapt request v2 sang format v1
*/
adaptV2RequestToV1(v2Request: any): any {
return {
email: v2Request.email,
name: v2Request.name,
// EN: Ignore new fields / VI: Bỏ qua các field mới
};
}
}
```
## Best Practices
1. **Versioning Strategy**: Choose URL path or header, be consistent / Chọn chiến lược và nhất quán
2. **Semantic Versioning**: Use MAJOR.MINOR.PATCH / Sử dụng semantic versioning
3. **Deprecation**: Always deprecate before removing / Luôn deprecate trước khi xóa
4. **Migration Guide**: Provide clear migration documentation / Cung cấp tài liệu migration rõ ràng
5. **Backward Compatibility**: Maintain compatibility when possible / Duy trì tương thích khi có thể
6. **Communication**: Clearly communicate version changes / Giao tiếp rõ ràng các thay đổi version
## Common Mistakes
1. **No Deprecation Period**: Breaking clients suddenly / Phá vỡ clients đột ngột
```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 / Gây nhầm lẫn cho client
```
# ❌ 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 / Chiến lược không nhất quán
```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: </api/v2/users>; rel="successor-version"
```
## Tài Nguyên
- [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`

View File

@@ -1,420 +0,0 @@
# Các Pattern Caching
Caching strategies and patterns for GoodGo microservices including multi-layer cache, Redis caching, cache key naming, TTL strategies, cache invalidation, and cache-aside patterns.
> Các strategies và patterns về caching cho GoodGo microservices bao gồm multi-layer cache, Redis caching, cache key naming, TTL strategies, cache invalidation, và cache-aside patterns.
## Tổng Quan
Caching is a critical performance optimization technique that stores frequently accessed data in fast-access storage. This guide covers multi-layer caching strategies, cache key naming conventions, TTL (Time To Live) strategies, cache invalidation patterns, and best practices for implementing effective caching in GoodGo microservices.
Caching là kỹ thuật tối ưu hiệu suất quan trọng lưu trữ dữ liệu thường xuyên truy cập trong storage có tốc độ truy cập nhanh. Hướng dẫn này bao gồm các strategies multi-layer caching, conventions đặt tên cache keys, TTL strategies, patterns cache invalidation, và best practices để implement caching hiệu quả trong GoodGo microservices.
## Khi Nào Sử Dụng
Use caching patterns 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
- Improving application performance
Sử dụng caching patterns khi:
- Implement caching cho dữ liệu thường xuyên truy cập
- Optimize database queries với caching
- Thiết kế cache key naming conventions
- Thiết lập TTL strategies
- Implement cache invalidation patterns
- Sử dụng multi-layer cache (L1: Memory, L2: Redis)
- Xử lý cache failures một cách graceful
- Cải thiện hiệu suất ứng dụng
## Khái Niệm Chính
### Chiến Lược Cache Đa Tầng
The platform uses a two-layer cache architecture to balance speed and capacity.
Nền tảng sử dụng kiến trúc cache hai tầng để cân bằng tốc độ và dung lượng.
```mermaid
graph TB
subgraph Application["Application Layer"]
App[Application Code]
end
subgraph L1Layer["L1 Cache - Memory (NodeCache)"]
L1[In-Memory Cache]
L1Props["• Speed: < 1ms<br/>• Capacity: 10k keys<br/>• TTL: 60s-5min<br/>• Scope: Per-instance"]
end
subgraph L2Layer["L2 Cache - Redis (Distributed)"]
L2[Redis Cache]
L2Props["• Speed: < 5ms<br/>• Capacity: Large<br/>• TTL: Configurable<br/>• 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
```
**Đặc Điểm Các Tầng / Layer Characteristics:**
**Cache Bộ Nhớ / L1 Cache (Memory)**:
- **Storage**: NodeCache in-memory cache
- **Tốc Độ / Speed**: Very fast (< 1ms access time)
- **Dung Lượng / Capacity**: Limited (10k keys default)
- **TTL**: Short (60 seconds default, max 5 minutes)
- **Phạm Vi / Scope**: Per-instance (not shared across instances)
- **Trường Hợp Sử Dụng / Use Case**: Ultra-fast access for hot data
**Cache Redis / L2 Cache (Redis)**:
- **Storage**: Distributed Redis cache
- **Tốc Độ / Speed**: Fast (< 5ms access time)
- **Dung Lượng / Capacity**: Large
- **TTL**: Longer (configurable)
- **Phạm Vi / Scope**: Shared across all service instances
- **Trường Hợp Sử Dụng / Use Case**: Distributed caching, larger datasets
### Luồng Cache
The cache lookup follows this flow: Check L1 first, if miss check L2, if miss fetch from source and populate both layers.
Luồng lookup cache theo trình tự: Kiểm tra L1 trước, nếu miss thì kiểm tra L2, nếu miss thì fetch từ nguồn và populate cả hai tầng.
```mermaid
flowchart TD
Start([Request Data]) --> CheckL1{Check L1 Cache<br/>Memory}
CheckL1 -->|Hit| ReturnL1[Return Data<br/>from L1]
CheckL1 -->|Miss| CheckL2{Check L2 Cache<br/>Redis}
CheckL2 -->|Hit| StoreL1[Store in L1<br/>Warm Cache]
StoreL1 --> ReturnL2[Return Data<br/>from L2]
CheckL2 -->|Miss| FetchSource[Fetch from<br/>Data Source]
FetchSource --> StoreBoth[Store in L1 & L2]
StoreBoth --> ReturnSource[Return Data<br/>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
```
**Tham Khảo**: [`services/iam-service/src/core/cache/multi-layer-cache.ts`](../../../services/iam-service/src/core/cache/multi-layer-cache.ts)
## Patterns
### Sử Dụng Cache Service
Use the cache service wrapper for all cache operations.
Sử dụng cache service wrapper cho tất cả các thao tác cache.
```typescript
import { cacheService } from '../core/cache';
// Get/set đơn giản
const cached = await cacheService.get<User>('user:123');
await cacheService.set('user:123', userData, 300); // 5 minutes TTL
// Pattern get or set (cache-aside)
const user = await cacheService.getOrSet(
'user:123',
async () => {
return await userRepository.findById('123');
},
300 // TTL in seconds
);
```
**Tham Khảo**: [`services/iam-service/src/core/cache/cache.service.ts`](../../../services/iam-service/src/core/cache/cache.service.ts)
### Quy Ước Đặt Tên Cache Key
Use consistent naming patterns to avoid collisions and enable pattern-based invalidation.
Sử dụng patterns đặt tên nhất quán để tránh collisions và cho phép invalidation dựa trên pattern.
**Các 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'
```
**Bộ Tạo Key**:
```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}`,
};
```
### Pattern Cache-Aside
The most common caching pattern - check cache first, fetch if miss, store in cache.
Pattern caching phổ biến nhất - kiểm tra cache trước, fetch nếu miss, lưu vào cache.
```typescript
async getUserPermissions(userId: string): Promise<string[]> {
const cacheKey = cacheService.keys.userPermissions(userId);
// Thử cache trước
const cached = await cacheService.get<string[]>(cacheKey);
if (cached) {
return cached;
}
// Cache miss - fetch từ nguồn
const permissions = await calculatePermissions(userId);
// Lưu vào cache
await cacheService.set(cacheKey, permissions, 300); // 5 min TTL
return permissions;
}
```
**Tham Khảo**: [`services/iam-service/src/modules/rbac/rbac.service.ts`](../../../services/iam-service/src/modules/rbac/rbac.service.ts)
### Pattern Get hoặc Set
Simplified cache-aside pattern that automatically handles cache miss scenarios.
Pattern cache-aside đơn giản hóa tự động xử lý các trường hợp cache miss.
```typescript
const permissions = await cacheService.getOrSet(
cacheService.keys.userPermissions(userId),
async () => {
// Function này chỉ chạy khi cache miss
return await calculatePermissions(userId);
},
300 // TTL
);
```
### Chiến Lược TTL
Choose TTL (Time To Live) based on data characteristics and change frequency.
Chọn TTL (Time To Live) dựa trên đặc điểm dữ liệu và tần suất thay đổi.
**Short TTL (60-300s) / TTL Ngắn**: Frequently changing data / Dữ liệu thay đổi thường xuyên
- User permissions (300s)
- Session data (varies)
- Real-time statistics / Thống kê thời gian thực
**Medium TTL (300-1800s) / TTL Trung Bình**: Moderately changing data / Dữ liệu thay đổi vừa phải
- User profiles (600s)
- Organization data (900s)
- Configuration (1800s)
**Long TTL (1800-3600s) / TTL Dài**: Rarely changing data / Dữ liệu hiếm khi thay đổi
- Static configurations (3600s)
- Reference data (7200s)
**No TTL / Không TTL**: Very stable data (use with caution) / Dữ liệu rất ổn định (sử dụng cẩn thận)
- Rarely use - prefer long TTL instead / Hiếm khi sử dụng - nên dùng TTL dài
### Làm Mất Hiệu Lực Cache
Invalidate cache when data changes to prevent serving stale data. The platform supports multiple invalidation strategies.
Làm mất hiệu lực cache khi dữ liệu thay đổi để tránh phục vụ dữ liệu cũ. Nền tảng hỗ trợ nhiều chiến lược invalidation.
```mermaid
flowchart TD
Start([Data Changed]) --> ChooseStrategy{Choose<br/>Invalidation<br/>Strategy}
ChooseStrategy -->|Single Key| SingleKey[Single Key<br/>Invalidation]
SingleKey --> DelL1[Delete from L1]
DelL1 --> DelL2[Delete from L2]
DelL2 --> Done1([Complete])
ChooseStrategy -->|Pattern Match| PatternMatch[Pattern-Based<br/>Invalidation]
PatternMatch --> FindKeys[Find Matching Keys<br/>user:123:*]
FindKeys --> DelManyL1[Delete from L1<br/>All Matching]
DelManyL1 --> DelManyL2[Delete from L2<br/>All Matching]
DelManyL2 --> Done2([Complete])
ChooseStrategy -->|Multiple Keys| MultipleKeys[Multiple Keys<br/>Invalidation]
MultipleKeys --> ListKeys[List Keys to Delete<br/>user:123<br/>user:123:permissions<br/>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
```
**Ví Dụ Triển Khai / Implementation Examples:**
```typescript
// Làm mất hiệu lực key đơn / Single key invalidation
await cacheService.del(cacheService.keys.user(userId));
await cacheService.del(cacheService.keys.userPermissions(userId));
// Làm mất hiệu lực dựa trên pattern / Pattern-based invalidation
await cacheService.invalidatePattern('user:123:*');
// Nhiều keys / Multiple keys
await cacheService.delMany([
cacheService.keys.user(userId),
cacheService.keys.userPermissions(userId),
cacheService.keys.userRoles(userId),
]);
```
### Làm Nóng Cache
Pre-populate cache with frequently accessed data to improve initial performance.
Pre-populate cache với dữ liệu thường xuyên truy cập để cải thiện hiệu suất ban đầu.
```typescript
async warmCache() {
const activeUsers = await userRepository.findActiveUsers();
for (const user of activeUsers) {
// Pre-cache dữ liệu user
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
);
}
}
```
### Xử Lý Lỗi
Cache failures should not break the application - always fallback to data source.
Cache failures không nên làm hỏng ứng dụng - luôn fallback về data source.
```typescript
async getWithCache(key: string): Promise<Data | null> {
try {
// Thử cache trước
const cached = await cacheService.get<Data>(key);
if (cached) return cached;
} catch (error) {
// Log nhưng tiếp tục - fallback về nguồn
logger.warn('Cache get failed, falling back to source', { key, error });
}
// Fallback về data source
return await fetchFromSource();
}
```
## Thực Hành Tốt Nhất
1. **Cache Keys / Cache Keys**: Sử dụng naming conventions nhất quán
2. **TTL Selection / Chọn TTL**: Chọn TTL dựa trên tần suất thay đổi dữ liệu
3. **Invalidation / Làm Mất Hiệu Lực**: Làm mất hiệu lực cache khi dữ liệu thay đổi
4. **Error Handling / Xử Lý Lỗi**: Không để cache failures làm hỏng ứng dụng
5. **Cache-Aside / Cache-Aside**: Sử dụng cache-aside pattern cho hầu hết các trường hợp
6. **Avoid Over-Caching / Tránh Cache Quá Nhiều**: Không cache dữ liệu thay đổi quá thường xuyên
7. **Monitor Hit Rates / Giám Sát Tỷ Lệ Hit**: Theo dõi cache hit rates để optimize TTL
8. **Warm Cache / Làm Nóng Cache**: Pre-populate cache cho critical data
9. **Use Multi-Layer / Sử Dụng Đa Tầng**: Tận dụng cả L1 và L2 cache
10. **Serialize Properly / Serialize Đúng Cách**: Đảm bảo dữ liệu có thể JSON serialize
## Lỗi Thường Gặp
1. **Cache Key Collisions / Va Chạm Cache Key**: Sử dụng keys chung chung dẫn đến va chạm
2. **Stale Data / Dữ Liệu Cũ**: Không invalidate cache khi dữ liệu thay đổi
3. **Too Short TTL / TTL Quá Ngắn**: Đặt TTL quá ngắn, làm mất lợi ích của cache
4. **Too Long TTL / TTL Quá Dài**: Đặt TTL quá dài, phục vụ dữ liệu cũ
5. **No Error Handling / Không Xử Lý Lỗi**: Để cache errors làm hỏng ứng dụng
6. **Caching Everything / Cache Tất Cả**: Cache dữ liệu không có lợi từ caching
7. **Not Warming Cache / Không Làm Nóng Cache**: Không pre-populate critical cache data
8. **Ignoring Hit Rates / Bỏ Qua Tỷ Lệ Hit**: Không monitor cache performance
## Xử Lý Sự Cố
### Tỷ Lệ Hit Thấp
**Problem / Vấn Đề**: Cache hit rate thấp, cache không hiệu quả
**Solution / Giải Pháp**:
- Review TTL values - có thể quá ngắn
- Check cache key patterns - đảm bảo sử dụng nhất quán
- Verify cache invalidation không quá aggressive
- Monitor dữ liệu nào đang được cache
### Vấn Đề Dữ Liệu Cũ
**Problem / Vấn Đề**: Phục vụ dữ liệu cũ từ cache
**Solution / Giải Pháp**:
- Review TTL values - có thể quá dài
- Đảm bảo cache invalidation khi dữ liệu cập nhật
- Sử dụng TTL ngắn hơn cho dữ liệu thay đổi thường xuyên
- Implement cache versioning nếu cần
### Vấn Đề Hiệu Suất Cache
**Problem / Vấn Đề**: Cache operations chậm
**Solution / Giải Pháp**:
- Kiểm tra Redis connection và network latency
- Monitor Redis memory usage
- Review cache key patterns để hiệu quả
- Xem xét L1 cache hit rate (nên cao)
## Tài Nguyên
- [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 example
- [Redis Configuration](../../../services/iam-service/src/config/redis.config.ts) - Redis configuration

View File

@@ -1,578 +0,0 @@
# CI/CD Patterns Nâng Cao (CI/CD Advanced Patterns)
Advanced CI/CD patterns for GoodGo microservices including blue-green deployments, canary releases, automated rollback, deployment verification, and progressive delivery.
> Các patterns CI/CD nâng cao cho GoodGo microservices bao gồm blue-green deployments, canary releases, automated rollback, deployment verification, và progressive delivery.
## Tổng Quan
Advanced CI/CD patterns enable safe, zero-downtime deployments with blue-green, canary releases, automated rollbacks, and deployment verification.
Các patterns CI/CD nâng cao cho phép deployments an toàn, zero-downtime với blue-green, canary releases, automated rollbacks, và deployment verification.
## Khi Nào Sử Dụng
Use this skill when implementing advanced deployment strategies, automated rollbacks, or progressive delivery.
Sử dụng skill này khi:
- Implement blue-green deployments / Triển khai blue-green deployments
- Setting up canary releases / Thiết lập canary releases
- Implementing automated rollback mechanisms / Triển khai cơ chế rollback tự động
- Creating deployment verification pipelines / Tạo pipelines xác minh deployment
- Implementing progressive delivery / Triển khai progressive delivery
- Setting up deployment gates / Thiết lập deployment gates
- Implementing smoke tests / Triển khai smoke tests
- Managing deployment strategies in Kubernetes / Quản lý chiến lược deployment trong Kubernetes
## Khái Niệm Cốt Lõi
### Deployment Strategies / Chiến Lược Deployment
1. **Rolling Update**: Gradual replacement (default K8s) / Thay thế dần dần (mặc định K8s)
2. **Blue-Green**: Two identical environments, switch traffic / Hai môi trường giống hệt, chuyển traffic
3. **Canary**: Gradual rollout to subset of users / Rollout dần dần tới subset users
4. **Recreate**: Stop old, start new (downtime) / Dừng cũ, khởi động mới (có downtime)
### Deployment Verification / Xác Minh Deployment
- Smoke tests / Kiểm tra smoke
- Health checks / Kiểm tra sức khỏe
- Performance tests / Kiểm tra hiệu suất
- Rollback triggers / Kích hoạt rollback
## 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.
Blue-green deployment duy trì hai môi trường production giống hệt nhau (blue và green). Tại bất kỳ thời điểm nào, chỉ một môi trường phục vụ traffic trực tiếp. Phiên bản mới được triển khai vào môi trường không hoạt động, được xác minh, sau đó traffic được chuyển đổi.
```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 / Triển Khai Kubernetes
```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
# EN: Switch service selector between blue/green
# VI: Service selector chuyển đổi giữa blue/green
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
version: blue # EN: Switch to green after verification / VI: Chuyển sang green sau khi xác minh
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.
Canary deployment triển khai thay đổi dần dần tới một subset nhỏ users trước khi làm cho chúng có sẵn cho tất cả mọi người. Điều này cho phép kiểm tra trong môi trường thực tế với rủi ro tối thiểu.
```mermaid
flowchart TD
Start([Canary Deployment Started]) --> DeployCanary[Deploy Canary Version<br/>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 / Canary Kubernetes với 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 # EN: Start with 1 replica (10% traffic) / VI: Bắt đầu với 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
# EN: VirtualService splits traffic
# VI: VirtualService chia 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 # EN: 10% traffic to canary / VI: 10% traffic tới canary
```
## Automated Rollback / Rollback Tự Động
Automated rollback mechanisms detect deployment failures and automatically revert to the previous stable version, minimizing downtime and impact.
Cơ chế rollback tự động phát hiện lỗi deployment và tự động quay lại phiên bản ổn định trước đó, giảm thiểu downtime và tác động.
```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 / Script Rollback
```bash
#!/bin/bash
# scripts/deployment/rollback.sh
# EN: Automated rollback to previous version
# VI: Rollback tự động về version trước
SERVICE_NAME=$1
NAMESPACE=${2:-production}
# EN: Get previous deployment revision
# VI: Lấy revision deployment trước
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"
# EN: Rollback deployment
# VI: Rollback deployment
kubectl rollout undo deployment/$SERVICE_NAME -n $NAMESPACE --to-revision=$PREVIOUS_REVISION
# EN: Wait for rollout
# VI: Đợi rollout
kubectl rollout status deployment/$SERVICE_NAME -n $NAMESPACE
echo "Rollback complete"
```
### Automated Rollback on Failure / Rollback Tự Động Khi Lỗi
```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 / Xác Minh Deployment
### Smoke Tests / Kiểm Tra Smoke
```typescript
// scripts/deployment/smoke-tests.ts
// EN: Smoke tests for deployment verification
// VI: Smoke tests để xác minh deployment
import axios from 'axios';
const SERVICE_URL = process.env.SERVICE_URL || 'http://localhost';
async function runSmokeTests(): Promise<boolean> {
try {
// EN: Health check
// VI: Health check
const healthResponse = await axios.get(`${SERVICE_URL}/health`);
if (healthResponse.status !== 200) {
console.error('Health check failed');
return false;
}
// EN: Basic functionality test
// VI: Test chức năng cơ bản
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 / Script Kiểm Tra Sức Khỏe
```bash
#!/bin/bash
# scripts/deployment/health-checks.sh
# EN: Comprehensive health checks
# VI: Health checks toàn diện
SERVICE_NAME=$1
NAMESPACE=${2:-production}
echo "Running health checks for $SERVICE_NAME"
# EN: Check pods are ready
# VI: Kiểm tra pods đã 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
# EN: Check service endpoints
# VI: Kiểm tra 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
# EN: Check health endpoint
# VI: Kiểm tra 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 / Cổng Deployment
Deployment gates add checkpoints in the CI/CD pipeline that must pass before proceeding to the next stage.
Deployment gates thêm các điểm kiểm tra trong CI/CD pipeline phải vượt qua trước khi tiến tới giai đoạn tiếp theo.
```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 / Thực Hành Tốt
1. **Blue-Green**: Use for zero-downtime deployments / Sử dụng cho zero-downtime deployments
2. **Canary**: Use for gradual rollouts with monitoring / Sử dụng cho rollouts dần dần với monitoring
3. **Automated Rollback**: Always have rollback plan / Luôn có kế hoạch rollback
4. **Smoke Tests**: Run immediately after deployment / Chạy ngay sau deployment
5. **Health Checks**: Monitor health continuously / Giám sát sức khỏe liên tục
6. **Gates**: Use deployment gates for critical deployments / Sử dụng deployment gates cho deployments quan trọng
## Common Mistakes / Lỗi Thường Gặp
1. **No Rollback Plan**: Can't recover from failed deployment / Không thể phục hồi từ deployment thất bại
```yaml
# ✅ Always have rollback command ready
# ✅ Luôn có lệnh rollback sẵn sàng
kubectl rollout undo deployment/service
```
2. **Skipping Smoke Tests**: Catching issues too late / Phát hiện vấn đề quá muộn
```yaml
# ✅ Run smoke tests immediately after deploy
# ✅ Chạy smoke tests ngay sau khi deploy
- name: Smoke Tests
run: ./scripts/smoke-tests.sh
```
3. **100% Traffic Switch**: All-or-nothing failures / Lỗi tất cả hoặc không gì cả
```yaml
# ❌ BAD: Immediate full switch
# ❌ XẤU: Chuyển đổi toàn bộ ngay lập tức
# ✅ GOOD: Gradual rollout (10% → 50% → 100%)
# ✅ TỐT: Rollout dần dần (10% → 50% → 100%)
```
4. **No Health Monitoring**: Missing deployment issues / Bỏ lỡ vấn đề deployment
```yaml
# ✅ Monitor health after deployment
# ✅ Giám sát sức khỏe sau deployment
- name: Monitor Health
run: kubectl rollout status deployment/service --timeout=5m
```
## Quick Reference / Tham Khảo Nhanh
| 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 / Lệnh Deployment:**
```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 / Kích Hoạt GitHub Actions:**
```yaml
on:
push:
branches: [main] # Deploy to prod
tags: ['v*'] # Release
pull_request:
branches: [main] # PR checks
```
**Deployment Gates / Cổng Deployment:**
```
Build → Test → Security Scan → Deploy Staging
→ Smoke Tests → Manual Approval → Deploy Prod
```
## Tài Nguyên
- [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 / Patterns deployment K8s
- [Testing Patterns](./testing-patterns.md) - Testing strategies / Chiến lược testing
- [Project Rules](./project-rules.md) - GoodGo coding standards / Tiêu chuẩn coding GoodGo
- Skill Source: `.cursor/skills/cicd-advanced-patterns/SKILL.md`

View File

@@ -1,682 +0,0 @@
# Viết Comment Code
Guidelines for adding comprehensive bilingual code comments (English and Vietnamese) to improve code readability for international and Vietnamese teams.
> Hướng dẫn thêm comments song ngữ (tiếng Anh và tiếng Việt) để cải thiện khả năng đọc code cho các đội quốc tế và Việt Nam.
## Tổng Quan
Code comments are essential for maintaining and understanding codebase, especially in a bilingual development environment. This guide provides comprehensive patterns for writing clear, consistent, and helpful comments in both English and Vietnamese across the GoodGo microservices platform. It covers JSDoc documentation, inline comments, special comment types, and best practices for effective code documentation.
Comments code là điều cần thiết để bảo trì và hiểu codebase, đặc biệt trong môi trường phát triển song ngữ. Hướng dẫn này cung cấp các patterns toàn diện để viết comments rõ ràng, nhất quán và hữu ích bằng cả tiếng Anh và tiếng Việt trên nền tảng microservices GoodGo. Nó bao gồm tài liệu JSDoc, inline comments, các loại comment đặc biệt, và best practices cho tài liệu code hiệu quả.
## Khi Nào Sử Dụng
Use bilingual commenting patterns when:
- Adding comments to new code
- Documenting existing code
- Creating JSDoc/TSDoc documentation
- Writing function/class descriptions
- Explaining complex logic or algorithms
- Adding inline comments for clarification
- Documenting API endpoints and interfaces
- Explaining configuration and setup code
- Writing security-critical code documentation
- Creating error handling documentation
Sử dụng các patterns comment song ngữ khi:
- Thêm comments vào code mới
- Tài liệu hóa code hiện có
- Tạo tài liệu JSDoc/TSDoc
- Viết mô tả functions/classes
- Giải thích logic hoặc thuật toán phức tạp
- Thêm inline comments để làm rõ
- Tài liệu hóa API endpoints và interfaces
- Giải thích code cấu hình và setup
- Viết tài liệu code bảo mật quan trọng
- Tạo tài liệu xử lý lỗi
## Cấu Trúc Comment
Biểu đồ sau minh họa cấu trúc và phân cấp các loại comment được sử dụng trong codebase GoodGo:
```mermaid
graph TB
subgraph CommentTypes["Comment Types"]
SingleLine["Single-line Comments<br/>// EN: ...<br/>// VI: ..."]
MultiLine["Multi-line Comments<br/>/* EN: ...<br/>VI: ... */"]
JSDoc["JSDoc Comments<br/>/** EN: ...<br/>VI: ... */"]
Prisma["Prisma Comments<br/>/// EN: ...<br/>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)<br/>First"]
VI["Vietnamese (VI)<br/>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
```
## Quy Trình Tài Liệu Hóa
Biểu đồ sau cho thấy quy trình quyết định khi thêm comments vào code:
```mermaid
flowchart TD
Start([Bắt đầu: Viết Code]) --> CheckType{Loại phần tử<br/>code nào?}
CheckType -->|Public API| HighPriority[Ưu tiên cao:<br/>Luôn tài liệu hóa]
CheckType -->|Logic phức tạp| HighPriority
CheckType -->|Code bảo mật| HighPriority
CheckType -->|Config/Setup| HighPriority
CheckType -->|Xử lý lỗi| HighPriority
CheckType -->|Helper Function| MediumPriority[Ưu tiên trung bình:<br/>Tài liệu khi hữu ích]
CheckType -->|Chuyển đổi dữ liệu| MediumPriority
CheckType -->|Tích hợp bên ngoài| MediumPriority
CheckType -->|Getter/Setter đơn giản| LowPriority[Ưu tiên thấp:<br/>Tùy chọn]
CheckType -->|Tự giải thích| LowPriority
CheckType -->|CRUD tiêu chuẩn| LowPriority
HighPriority --> ChooseFormat{Chọn định dạng<br/>Comment}
MediumPriority --> ChooseFormat
LowPriority --> ChooseFormat
ChooseFormat -->|Function/Class| UseJSDoc[Sử dụng JSDoc<br/>/** EN: ...<br/>VI: ... */]
ChooseFormat -->|Giải thích ngắn| UseSingleLine[Sử dụng Single-line<br/>// EN: ...<br/>// VI: ...]
ChooseFormat -->|Quy trình nhiều bước| UseMultiLine[Sử dụng Multi-line<br/>/* EN: ...<br/>VI: ... */]
ChooseFormat -->|Prisma Schema| UsePrisma[Sử dụng Prisma<br/>/// EN: ...<br/>VI: ...]
UseJSDoc --> AddParams[Thêm @param tags<br/>Thêm @returns tag<br/>Thêm @throws nếu cần]
UseSingleLine --> WriteBilingual[Viết song ngữ:<br/>EN trước, VI sau]
UseMultiLine --> WriteBilingual
UsePrisma --> WriteBilingual
AddParams --> WriteBilingual
WriteBilingual --> CheckSpecial{Loại comment<br/>đặc biệt?}
CheckSpecial -->|Công việc tương lai| AddTODO[Thêm prefix TODO]
CheckSpecial -->|Cần sửa| AddFIXME[Thêm prefix FIXME]
CheckSpecial -->|Cảnh báo quan trọng| AddWARNING[Thêm prefix WARNING]
CheckSpecial -->|Ghi chú quan trọng| AddNOTE[Thêm prefix NOTE]
CheckSpecial -->|Không| End([Hoàn thành])
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
```
## Khái Niệm Chính
### Định Dạng Comment
All comments in the GoodGo project should be bilingual, with English (EN) followed by Vietnamese (VI) translations.
**Pattern / Mẫu**:
```
[English explanation]
// VI: [Vietnamese explanation]
```
### Các Loại Comment
1. **Single-line Comments** (`//`) / **Comments Một Dòng**: For brief explanations / Cho giải thích ngắn gọn
2. **Multi-line Comments** (`/* */`) / **Comments Nhiều Dòng**: For longer explanations / Cho giải thích dài hơn
3. **JSDoc Comments** (`/** */`) / **Comments JSDoc**: For function, class, and API documentation / Cho tài liệu function, class và API
4. **Prisma Comments** (`///`) / **Comments Prisma**: For database schema documentation / Cho tài liệu schema database
## Các Pattern Thường Dùng
### Comments Một Dòng
Use single-line comments for brief explanations of code behavior / Sử dụng single-line comments cho giải thích ngắn gọn về hành vi code.
```typescript
Initialize database connection
// VI: Khởi tạo kết nối database
const db = await createConnection();
Enable detailed logging in development, minimal in production
// VI: Bật ghi log chi tiết trong development, tối thiểu trong production
'error';
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/config/database.config.ts`](../../../services/iam-service/src/config/database.config.ts)
### Comments Nhiều Dòng
Use multi-line comments for detailed explanations or step-by-step processes / Sử dụng multi-line comments cho giải thích chi tiết hoặc quy trình từng bước.
```typescript
/**
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<string> {
Implementation here
// VI: Implementation ở đây
}
```
### Tài Liệu Function
Use JSDoc format for all public functions with bilingual descriptions / Sử dụng format JSDoc cho tất cả public functions với mô tả song ngữ.
```typescript
/**
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 {
Apply discount first
// VI: Áp dụng giảm giá trước
const discountedPrice = basePrice - discount;
Then calculate tax
// VI: Sau đó tính thuế
const tax = discountedPrice * taxRate;
return discountedPrice + tax;
}
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
### Tài Liệu Class
Document classes with bilingual descriptions and explain their purpose / Tài liệu hóa classes với mô tả song ngữ và giải thích mục đích của chúng.
```typescript
/**
Service for managing features in the system
* VI: Service để quản lý các features trong hệ thống
*/
export class FeatureService {
/**
Create a new feature
* VI: Tạo một feature mới
*
* @param data - Feature data / Dữ liệu feature
* @returns Created feature / Feature đã tạo
*/
async create(data: { name: string; title?: string }) {
// Implementation
}
}
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
### Tài Liệu Interface/Type
Document interfaces and types to explain their structure and purpose / Tài liệu hóa interfaces và types để giải thích cấu trúc và mục đích của chúng.
```typescript
/**
User data transfer object
* VI: Đối tượng truyền dữ liệu người dùng
*/
interface UserDto {
Unique user identifier / VI: đnh danh duy nhất */
id: string;
User email address / VI: Địa chỉ email người dùng */
email: string;
User display name / VI: Tên hiển thị người dùng */
name: string;
User role for authorization / VI: Vai trò người dùng đ phân quyền */
role: 'admin' | 'user' | 'guest';
}
```
### Comments Cấu Hình
Document configuration files and environment variables with clear explanations / Tài liệu hóa file cấu hình và biến môi trường với giải thích rõ ràng.
```typescript
/**
Prisma client instance configured for the application
* VI: Instance Prisma client được cấu hình cho ứng dụng
*/
export const prisma = new PrismaClient({
Enable detailed logging in development, minimal in production
// VI: Bật ghi log chi tiết trong development, tối thiểu trong production
['error'],
});
/**
Establish database connection on application startup
* VI: Thiết lập kết nối database khi khởi động ứng dụng
*/
export const connectDatabase = async (): Promise<void> => {
try {
Connect to database using Prisma
// VI: Kết nối tới database sử dụng Prisma
await prisma.$connect();
logger.info('Database connected successfully / Kết nối database thành công');
} catch (error) {
Log error and exit if database connection fails
// VI: Ghi log lỗi và thoát nếu kết nối database thất bại
logger.error('Database connection failed / Kết nối database thất bại', { error });
process.exit(1);
}
};
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/config/database.config.ts`](../../../services/iam-service/src/config/database.config.ts)
### Tài Liệu Middleware
Document middleware functions with their purpose and behavior / Tài liệu hóa middleware functions với mục đích và hành vi của chúng.
```typescript
/**
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
) {
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 {
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',
},
});
}
}
```
### Comments Schema Prisma
Use triple-slash comments (`///`) for Prisma schema documentation / Sử dụng triple-slash comments (`///`) cho tài liệu Prisma schema.
```prisma
User model for authentication and profile
/// VI: Model người dùng cho xác thực và hồ sơ
model User {
Unique identifier / VI: Mã định danh duy nhất
id String @id @default(cuid())
User email (unique) / VI: Email người dùng (duy nhất)
email String @unique
Hashed password / VI: Mật khẩu đã mã hóa
password String
Display name / VI: Tên hiển thị
name String
Account creation timestamp / VI: Thời gian tạo tài khoản
createdAt DateTime @default(now())
Last update timestamp / VI: Thời gian cập nhật cuối
updatedAt DateTime @updatedAt
@@map("users")
}
```
### Comments Test
Document test setup and explain test scenarios in both languages / Tài liệu hóa test setup và giải thích test scenarios bằng cả hai ngôn ngữ.
```typescript
Mock environment variables for tests
// VI: Mock biến môi trường cho tests
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test_db';
Mock external services to avoid real network calls
// VI: Mock các service bên ngoài để tránh gọi mạng thật
jest.mock('@goodgo/logger', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
},
}));
describe('FeatureService', () => {
it('should create a feature successfully', async () => {
Arrange
// VI: Chuẩn bị
const testData = { name: 'test-feature' };
Act
// VI: Thực hiện
const result = await service.create(testData);
Assert
// VI: Kiểm tra
expect(result).toBeDefined();
});
});
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/__tests__/setupTests.ts`](../../../services/iam-service/src/__tests__/setupTests.ts)
### Comments Logic Phức Tạp
When explaining complex algorithms or business logic, break it down into steps / Khi giải thích thuật toán phức tạp hoặc logic nghiệp vụ, chia nhỏ thành các bước.
```typescript
Step 1: Validate input parameters
// VI: Bước 1: Xác thực tham số đầu vào
if (!email || !password) {
throw new ValidationError('Email and password required');
}
Step 2: Check if user exists in database
// VI: Bước 2: Kiểm tra xem người dùng có tồn tại trong database
const user = await prisma.user.findUnique({ where: { email } });
if (!user) {
throw new NotFoundError('User not found');
}
Step 3: Verify password hash
// VI: Bước 3: Xác minh hash mật khẩu
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
throw new AuthenticationError('Invalid credentials');
}
Step 4: Generate and return JWT token
// VI: Bước 4: Tạo và trả về JWT token
const token = jwt.sign({ userId: user.id }, JWT_SECRET);
return { token, user };
```
## Các Loại Comment Đặc Biệt
### Comments TODO
Use TODO comments for future improvements with bilingual descriptions / Sử dụng TODO comments cho cải tiến trong tương lai với mô tả song ngữ.
```typescript
Implement caching for better performance
// TODO VI: Triển khai caching để cải thiện hiệu suất
Add rate limiting to prevent abuse
// TODO VI: Thêm rate limiting để ngăn chặn lạm dụng
```
### Comments FIXME
Use FIXME comments for code that needs fixing / Sử dụng FIXME comments cho code cần sửa.
```typescript
This causes memory leak, needs refactoring
// FIXME VI: Đoạn này gây rò rỉ bộ nhớ, cần refactor
Temporary workaround, should implement proper solution
// FIXME VI: Giải pháp tạm thời, nên triển khai giải pháp đúng đắn
```
### Comments CẢNH BÁO
Use WARNING comments for code that requires special attention / Sử dụng WARNING comments cho code cần chú ý đặc biệt.
```typescript
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
This function modifies global state, use with caution
// WARNING VI: Function này thay đổi global state, sử dụng cẩn thận
```
### Comments GHI CHÚ
Use NOTE comments for important information or explanations / Sử dụng NOTE comments cho thông tin hoặc giải thích quan trọng.
```typescript
This is intentionally async to avoid blocking the event loop
// NOTE VI: Đây là async có chủ ý để tránh block event loop
Priority: Docker Compose > .env.local > .env > System environment
// NOTE VI: Ưu tiên: Docker Compose > .env.local > .env > Môi trường hệ thống
```
## Thực Hành Tốt Nhất
### Vị Trí Comment
- ✅ Place bilingual comments together (EN first, then VI) / Đặt comments song ngữ cùng nhau (EN trước, sau đó VI)
- ✅ Keep comments close to the code they describe / Giữ comments gần với code mà chúng mô tả
- ✅ Use JSDoc format for functions and classes / Sử dụng format JSDoc cho functions và classes
- ✅ Update comments when code changes / Cập nhật comments khi code thay đổi
### Nội Dung Comment
-**DO**: Explain WHY, not WHAT (code shows what) / Giải thích TẠI SAO, không phải CÁI GÌ
-**DO**: Document complex logic and business rules / Tài liệu hóa logic phức tạp và quy tắc nghiệp vụ
-**DO**: Include parameter descriptions and return types / Bao gồm mô tả tham số và kiểu trả về
-**DO**: Document error conditions and exceptions / Tài liệu hóa điều kiện lỗi và ngoại lệ
-**DON'T**: State the obvious / Không nói điều hiển nhiên
-**DON'T**: Write redundant comments / Không viết comments thừa
-**DON'T**: Comment out code (use version control instead) / Không comment code (sử dụng version control)
### Hướng Dẫn Ngôn Ngữ
**English / Tiếng Anh**:
- Use clear, concise technical English / Sử dụng tiếng Anh kỹ thuật rõ ràng, ngắn gọn
- Use proper technical terminology / Sử dụng thuật ngữ kỹ thuật đúng
- Be specific and actionable / Cụ thể và có thể thực hiện
**Vietnamese / Tiếng Việt**:
- Use proper Vietnamese technical terms / Sử dụng thuật ngữ kỹ thuật tiếng Việt đúng
- Keep translations accurate and natural / Giữ bản dịch chính xác và tự nhiên
- Use consistent terminology across codebase / Sử dụng thuật ngữ nhất quán trên codebase
- Prefer technical Vietnamese terms over literal translations / Ưu tiên thuật ngữ kỹ thuật Việt hơn dịch theo nghĩa đen
### Ưu Tiên Tài Liệu
**High Priority** (Always document / Luôn tài liệu hóa):
- Public APIs and exported functions / API công khai và functions được export
- Complex algorithms and business logic / Thuật toán phức tạp và logic nghiệp vụ
- Security-critical code / Code bảo mật quan trọng
- Configuration and environment setup / Cấu hình và thiết lập môi trường
- Error handling strategies / Chiến lược xử lý lỗi
**Medium Priority** (Document when helpful / Tài liệu khi hữu ích):
- Helper functions with non-obvious behavior / Helper functions có hành vi không rõ ràng
- Data transformations / Chuyển đổi dữ liệu
- Integration points with external services / Điểm tích hợp với services bên ngoài
**Low Priority** (Optional / Tùy chọn):
- Simple getters/setters / Getters/setters đơn giản
- Self-explanatory code / Code tự giải thích
- Standard CRUD operations / Các thao tác CRUD tiêu chuẩn
## Ví Dụ Từ Dự Án
### Ví Dụ Comment Thực Tế
1. **Service Comments**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
2. **Config Comments**: [`services/iam-service/src/config/database.config.ts`](../../../services/iam-service/src/config/database.config.ts)
3. **Test Comments**: [`services/iam-service/src/__tests__/setupTests.ts`](../../../services/iam-service/src/__tests__/setupTests.ts)
4. **Jest Config Comments**: [`services/iam-service/jest.config.ts`](../../../services/iam-service/jest.config.ts)
### Patterns Comment Trong Các Ngữ Cảnh Khác Nhau
- **Controllers**: Document API endpoints and request/response handling / Tài liệu hóa API endpoints và xử lý request/response
- **Services**: Document business logic and data processing / Tài liệu hóa logic nghiệp vụ và xử lý dữ liệu
- **Middleware**: Document authentication, authorization, and request processing / Tài liệu hóa xác thực, phân quyền và xử lý request
- **Repositories**: Document database operations and query logic / Tài liệu hóa các thao tác database và logic query
- **Config Files**: Document configuration options and environment variables / Tài liệu hóa tùy chọn cấu hình và biến môi trường
- **Tests**: Document test scenarios and setup procedures / Tài liệu hóa test scenarios và quy trình setup
## Tham Khảo Nhanh
### Template Comment Function
```typescript
/**
[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
*/
```
### Template Inline Comment
```typescript
[English explanation]
// VI: [Giải thích tiếng Việt]
```
### Template Block Phức Tạp
```typescript
Step N: [What this block does]
// VI: Bước N: [Block này làm gì]
```
### Template Comment Class
```typescript
/**
[Class purpose in English]
* VI: [Mục đích class bằng tiếng Việt]
*/
export class ClassName {
/**
[Property description]
* VI: [Mô tả property]
*/
private property: string;
}
```
### Template Comment Interface
```typescript
/**
[Interface purpose in English]
* VI: [Mục đích interface bằng tiếng Việt]
*/
interface InterfaceName {
Property description / VI: tả property */
property: string;
}
```
## Skills Liên Quan
- **[Testing Patterns](./testing-patterns.md)**: Writing comments in test files / Viết comments trong file test
- **[API Design](./api-design.md)**: Documenting API endpoints / Tài liệu hóa API endpoints
- **[Documentation](./documentation.md)**: Writing technical documentation / Viết tài liệu kỹ thuật
- **[Project Rules](./project-rules.md)**: Code organization and standards / Tổ chức code và tiêu chuẩn
## Tài Nguyên
### Tiêu Chuẩn Tài Liệu
- [JSDoc Documentation](https://jsdoc.app/)
- [TypeScript JSDoc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
- [TSDoc Specification](https://tsdoc.org/)
### Tài Liệu Nội Bộ
- [Documentation Writing Guidelines](./documentation.md)
- [API Design Standards](./api-design.md)
- [Project Coding Standards](./project-rules.md)
### Công Cụ
- **JSDoc**: Generate API documentation from comments / Tạo tài liệu API từ comments
- **ESLint**: Enforce comment style and completeness / Thực thi style và tính đầy đủ của comments
- **Prettier**: Format comments consistently / Định dạng comments nhất quán
- **VS Code**: IntelliSense uses JSDoc comments for better autocomplete / IntelliSense sử dụng JSDoc comments cho autocomplete tốt hơn

View File

@@ -1,119 +0,0 @@
# Quản Lý Cấu Hình (Configuration Management)
Configuration management patterns for GoodGo microservices including feature flags, dynamic configuration reloading, environment-specific configurations, and secrets management.
> Các patterns quản lý cấu hình cho GoodGo microservices bao gồm feature flags, dynamic configuration reloading, environment-specific configurations, và secrets management.
## Tổng Quan
Configuration management patterns enable flexible, secure, and environment-aware configuration handling with feature flags, dynamic reloading, and secrets management.
Các patterns quản lý cấu hình cho phép xử lý cấu hình linh hoạt, an toàn và nhận biết environment với feature flags, dynamic reloading, và quản lý secrets.
## Khi Nào Sử Dụng
Use this skill when implementing feature flags, managing configurations, or handling secrets.
Sử dụng skill này khi implement feature flags, quản lý configurations, hoặc xử lý secrets.
## Các Patterns Chính
### Configuration Loading Flow / Luồng Tải Cấu Hình
Quá trình tải cấu hình lấy configuration từ nhiều nguồn, validate và hỗ trợ 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 / Luồng Đánh Giá Feature Flag
Feature flags hỗ trợ nhiều chiến lược đánh giá bao gồm global flags, user-specific flags, và 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 / Feature Flags
```typescript
// EN: Check if feature is enabled
// VI: Kiểm tra xem feature có được bật không
const enabled = await featureFlagService.isEnabled('new-feature', userId);
```
### Dynamic Configuration / Configuration Động
```typescript
// EN: Auto-reload configuration
// VI: Tự động tải lại configuration
await configService.load();
configService.startAutoReload(60000);
```
## Best Practices / Thực Hành Tốt
1. Always validate configuration / Luôn validate configuration
2. Never commit secrets / Không bao giờ commit secrets
3. Use feature flags for rollouts / Sử dụng feature flags cho rollouts
## Tài Nguyên
- Skill Source: `.cursor/skills/configuration-management/SKILL.md`

View File

@@ -1,378 +0,0 @@
# Patterns Nhất Quán Dữ Liệu (Data Consistency Patterns)
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.
> Các patterns nhất quán dữ liệu cho distributed microservices bao gồm Saga patterns, distributed transactions, eventual consistency, compensation, và idempotency. Sử dụng khi xử lý distributed transactions, implement eventual consistency, hoặc quản lý đồng bộ dữ liệu giữa các services.
## Tổng Quan
Data consistency in distributed systems requires different approaches than traditional ACID transactions. This skill covers Saga patterns for distributed transactions, idempotency for retries, optimistic locking for conflicts, and eventual consistency strategies.
Nhất quán dữ liệu trong hệ thống phân tán yêu cầu các cách tiếp cận khác với ACID transactions truyền thống. Skill này bao gồm Saga patterns cho distributed transactions, idempotency cho retries, optimistic locking cho conflicts, và các chiến lược eventual consistency.
## Khi Nào Sử Dụng
Use this skill when:
- Implementing distributed transactions across multiple services
- Handling eventual consistency in microservices
- Implementing Saga patterns for distributed workflows
- Designing compensation strategies
Sử dụng skill này khi:
- Implement distributed transactions qua nhiều services
- Xử lý eventual consistency trong microservices
- Implement Saga patterns cho distributed workflows
- Thiết kế chiến lược compensation
## Khái Niệm Chính
### ACID vs BASE
**ACID (Truyền Thống)**: Atomicity, Consistency, Isolation, Durability
**BASE (Phân Tán)**: Basic Availability, Soft state, Eventual consistency
### Các Mô Hình Nhất Quán
- **Strong Consistency**: Tất cả nodes thấy cùng dữ liệu cùng lúc
- **Eventual Consistency**: Hệ thống trở nên nhất quán theo thời gian
- **Weak Consistency**: Không đảm bảo về thời điểm nhất quán
## Các Patterns Chính
### Saga Orchestrator Pattern
```typescript
// EN: Centralized orchestrator coordinates steps
// VI: Orchestrator tập trung điều phối các bước
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 },
],
});
```
#### Luồng Điều Phối Saga (Saga Orchestration Flow)
Biểu đồ sau minh họa cách Saga orchestrator thực thi các bước tuần tự và xử lý compensation khi thất bại:
```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)
```
#### Luồng Compensation (Compensation Flow)
Khi một bước thất bại, orchestrator sẽ compensate tất cả các bước đã hoàn thành trước đó theo thứ tự ngược lại:
```mermaid
flowchart TD
Start([Saga Execution Starts]) --> Step1[Execute Step 1]
Step1 -->|Success| Step2[Execute Step 2]
Step1 -->|Failure| Fail1[Saga Failed<br/>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<br/>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
```
#### Luồng Eventual Consistency (Eventual Consistency Flow)
Biểu đồ này cho thấy cách dữ liệu trở nên nhất quán giữa các services theo thời gian thông qua việc truyền events:
```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
```
### Saga Choreography Pattern / Pattern Saga Choreography
Trong choreography, các services phản ứng với events mà không cần coordinator tập trung:
```typescript
// EN: Services react to events
// VI: Các services phản ứng với events
eventConsumer.on('order.created', async (event) => {
await inventoryService.reserve(event.data.items);
await eventPublisher.publish('inventory.reserved', {...});
});
```
#### Luồng Saga Choreography (Saga Choreography Flow)
Biểu đồ sau cho thấy cách các services phối hợp thông qua events trong pattern choreography:
```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,<br/>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
Idempotency đảm bảo các operations có thể được retry an toàn mà không có side effects:
```typescript
// EN: Execute with idempotency check
// VI: Thực thi với idempotency check
await idempotencyHandler.execute(
idempotencyKey,
async () => await userService.create(data)
);
```
#### Luồng Idempotency (Idempotency Flow)
```mermaid
flowchart TD
Request[Client Request] --> Check{Idempotency Key<br/>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 / Khóa Lạc Quan
Optimistic locking ngăn chặn lost updates bằng cách sử dụng version fields:
```typescript
// EN: Update with version check
// VI: Cập nhật với kiểm tra version
await optimisticLockService.updateWithLock(
repository,
id,
(current) => ({ ...current, name: newName })
);
```
#### Luồng Optimistic Locking (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 / Pattern CQRS
Command Query Responsibility Segregation tách biệt read và write operations để tối ưu hiệu suất và khả năng mở rộng.
#### Luồng Kiến Trúc CQRS (CQRS Architecture Flow)
Biểu đồ sau minh họa cách CQRS tách biệt write path và read path:
```mermaid
flowchart TB
subgraph WritePath[Write Path]
Command[Command] --> WriteModel[Write Model<br/>Normalized]
WriteModel --> Event[Domain Event]
Event --> EventStore[(Event Store)]
end
subgraph ReadPath[Read Path]
Query[Query] --> ReadModel[Read Model<br/>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
// EN: Write path: Command handler
// VI: Write path: Command handler
await commandHandler.handle({
type: 'CREATE_ORDER',
data: { userId, items }
});
// EN: Read path: Optimized query
// VI: Read path: Query tối ưu
const orders = await readModel.findOrdersByUser(userId);
```
### Outbox Pattern / Pattern Outbox
Outbox pattern đảm bảo event publishing đáng tin cậy bằng cách lưu events trong cùng database transaction với business data.
#### Luồng Outbox Pattern (Outbox Pattern Flow)
Biểu đồ này cho thấy cách Outbox pattern đảm bảo việc gửi events:
```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<br/>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<br/>ensures delivery
```
```typescript
// EN: Execute business operation and store event in same transaction
// VI: Thực thi business operation và lưu event trong cùng 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'
}
});
});
// EN: Background processor publishes events from outbox
// VI: Background processor publish events từ outbox
```
## Best Practices / Thực Hành Tốt
1. **Design Compensations**: Mỗi step cần compensation / Every step needs compensation
2. **Idempotent Steps**: Làm cho steps idempotent cho retries / Make steps idempotent for retries
3. **Conflict Resolution**: Định nghĩa chiến lược giải quyết / Define resolution strategies
4. **Monitoring**: Theo dõi saga execution / Track saga execution
## Skills Liên Quan
- [Event-Driven Architecture](./event-driven-architecture.md) - Event patterns / Các patterns event
- [Error Handling Patterns](./error-handling-patterns.md) - Error handling / Xử lý lỗi
- [Database & Prisma](./database-prisma.md) - Database patterns / Các patterns database
## Tài Nguyên
- [Saga Pattern](https://microservices.io/patterns/data/saga.html) - Saga pattern overview
- Skill Source: `.cursor/skills/data-consistency-patterns/SKILL.md` - Source file đầy đủ

View File

@@ -1,683 +0,0 @@
# Cơ Sở Dữ Liệu & Prisma
Prisma ORM and database patterns for GoodGo microservices. Use when working with databases, creating Prisma schemas, writing migrations, implementing repositories, or optimizing queries.
> Pattern Prisma ORM và database cho các microservices của GoodGo. Sử dụng khi làm việc với databases, tạo Prisma schemas, viết migrations, triển khai repositories, hoặc tối ưu queries.
## Tổng Quan
This skill covers Prisma ORM patterns, database schema design, repository patterns, query optimization, and transaction handling used across GoodGo microservices. It ensures type-safe database operations, consistent data access patterns, and optimal performance.
Skill này bao gồm các pattern Prisma ORM, thiết kế database schema, repository patterns, tối ưu queries, và xử lý transactions được sử dụng trong các microservices của GoodGo. Nó đảm bảo các thao tác database type-safe, pattern truy cập dữ liệu nhất quán, và hiệu năng tối ưu.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Thiết lập Prisma cho service mới
- Tạo hoặc sửa đổi database schemas
- Viết database migrations
- Triển khai repository patterns
- Tối ưu database queries
- Thiết lập kết nối database
- Triển khai transactions
- Làm việc với Neon PostgreSQL
## Khái Niệm Chính
### Pattern Repository
All database operations go through repository classes that extend `BaseRepository`, providing consistent CRUD operations and error handling.
Tất cả các thao tác database đi qua các repository classes kế thừa `BaseRepository`, cung cấp các CRUD operations nhất quán và xử lý lỗi.
### Schema Prisma
Database schema is defined in `prisma/schema.prisma` with models, relations, indexes, and constraints.
Database schema được định nghĩa trong `prisma/schema.prisma` với models, relations, indexes, và constraints.
### An Toàn Kiểu
Prisma generates TypeScript types from schema, ensuring type-safe database operations.
Prisma generate TypeScript types từ schema, đảm bảo các thao tác database type-safe.
## Các Pattern Thường Dùng
### Pattern Schema Prisma
**Example from codebase / Ví dụ từ codebase**:
```prisma
// services/iam-service/prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
User model - Core user entity
// VI: Model User - Entity người dùng cốt lõi
model User {
id String @id @default(cuid())
email String @unique
username String? @unique
passwordHash String? // Nullable for social-only users
isActive Boolean @default(true)
emailVerified Boolean @default(false)
mfaEnabled Boolean @default(false)
mfaSecret String?
lastLoginAt DateTime?
loginCount Int @default(0)
organizationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
userRoles UserRole[]
userPermissions UserPermission[]
sessions Session[]
organization Organization? @relation(fields: [organizationId], references: [id])
profile UserProfile?
@@index([email])
@@index([username])
@@index([createdAt])
@@index([organizationId])
@@map("users")
}
```
### Sơ Đồ Quan Hệ Schema / Schema Relationships Diagram
Sơ đồ sau minh họa các quan hệ giữa các models User, Post, và Profile:
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
}
```
### Pattern Kết Nối Database
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/config/database.config.ts
import { PrismaClient } from '@prisma/client';
import { logger } from '@goodgo/logger';
/**
Prisma client instance configured for the application
* VI: Instance Prisma client được cấu hình cho ứng dụng
*/
export const prisma = new PrismaClient({
Enable detailed logging in development, minimal in production
// VI: Bật ghi log chi tiết trong development, tối thiểu trong production
['error'],
});
/**
Establish database connection on application startup
* VI: Thiết lập kết nối database khi khởi động ứng dụng
*/
export const connectDatabase = async (): Promise<void> => {
try {
await prisma.$connect();
logger.info('Database connected successfully / Kết nối database thành công');
} catch (error) {
logger.error('Database connection failed / Kết nối database thất bại', { error });
process.exit(1);
}
};
/**
Close database connection on application shutdown
* VI: Đóng kết nối database khi tắt ứng dụng
*/
export const disconnectDatabase = async (): Promise<void> => {
await prisma.$disconnect();
logger.info('Database disconnected / Đã ngắt kết nối database');
};
```
### Pattern Base Repository
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/common/repository.ts
import { PrismaClient } from '@prisma/client';
import { logger } from '@goodgo/logger';
import { DatabaseError } from '../../errors/http-error';
/**
Base repository class providing common database operations
* VI: Base repository class cung cấp các thao tác database chung
*/
export abstract class BaseRepository<T, CreateInput, UpdateInput> {
protected prisma: PrismaClient;
protected modelName: string;
constructor(prisma: PrismaClient, modelName: string) {
this.prisma = prisma;
this.modelName = modelName;
}
/**
Find entity by ID
* VI: Tìm entity theo ID
*/
async findById(id: string): Promise<T | null> {
try {
logger.debug(`Finding ${this.modelName} by ID / Tìm ${this.modelName} theo ID`, { id });
const entity = await (this.prisma as any)[this.modelName].findUnique({
where: { id },
});
logger.debug(`${this.modelName} ${entity ? 'found' : 'not found'} / ${this.modelName} ${entity ? 'đã tìm thấy' : 'không tìm thấy'}`, { id });
return entity;
} catch (error: any) {
logger.error(`Failed to find ${this.modelName} by ID / Không thể tìm ${this.modelName} theo ID`, { error, id });
throw new DatabaseError(`Failed to find ${this.modelName}`, { id, originalError: error });
}
}
/**
Find all entities with optional filtering
* VI: Tìm tất cả entities với filtering tùy chọn
*/
async findAll(options?: {
where?: any;
orderBy?: any;
skip?: number;
take?: number;
include?: any;
}): Promise<T[]> {
try {
logger.debug(`Finding all ${this.modelName} / Tìm tất cả ${this.modelName}`, options);
const entities = await (this.prisma as any)[this.modelName].findMany(options || {});
logger.debug(`Found ${entities.length} ${this.modelName} entities / Đã tìm thấy ${entities.length} ${this.modelName} entities`);
return entities;
} catch (error: any) {
logger.error(`Failed to find all ${this.modelName} / Không thể tìm tất cả ${this.modelName}`, { error, options });
throw new DatabaseError(`Failed to find ${this.modelName} entities`, { options, originalError: error });
}
}
/**
Create new entity
* VI: Tạo entity mới
*/
async create(data: CreateInput): Promise<T> {
try {
logger.debug(`Creating new ${this.modelName} / Tạo ${this.modelName} mới`, { data });
const entity = await (this.prisma as any)[this.modelName].create({
data,
});
logger.debug(`${this.modelName} created successfully / ${this.modelName} đã được tạo thành công`, { id: (entity as any).id });
return entity;
} catch (error: any) {
logger.error(`Failed to create ${this.modelName} / Không thể tạo ${this.modelName}`, { error, data });
throw new DatabaseError(`Failed to create ${this.modelName}`, { data, originalError: error });
}
}
/**
Update entity by ID
* VI: Cập nhật entity theo ID
*/
async update(id: string, data: UpdateInput): Promise<T> {
try {
logger.debug(`Updating ${this.modelName} / Cập nhật ${this.modelName}`, { id, data });
const entity = await (this.prisma as any)[this.modelName].update({
where: { id },
data,
});
logger.debug(`${this.modelName} updated successfully / ${this.modelName} đã được cập nhật thành công`, { id });
return entity;
} catch (error: any) {
if (error.code === 'P2025') {
logger.warn(`${this.modelName} not found for update / ${this.modelName} không tìm thấy để cập nhật`, { id });
throw new DatabaseError(`${this.modelName} not found`, { id });
}
logger.error(`Failed to update ${this.modelName} / Không thể cập nhật ${this.modelName}`, { error, id, data });
throw new DatabaseError(`Failed to update ${this.modelName}`, { id, data, originalError: error });
}
}
/**
Execute transaction with multiple operations
* VI: Thực thi transaction với nhiều operations
*/
async transaction<R>(callback: (tx: any) => Promise<R>): Promise<R> {
try {
logger.debug(`Starting ${this.modelName} transaction / Bắt đầu transaction ${this.modelName}`);
const result = await this.prisma.$transaction(async (tx) => {
return await callback(tx);
});
logger.debug(`${this.modelName} transaction completed successfully / Transaction ${this.modelName} đã hoàn thành thành công`);
return result;
} catch (error: any) {
logger.error(`${this.modelName} transaction failed / Transaction ${this.modelName} thất bại`, { error });
throw new DatabaseError(`${this.modelName} transaction failed`, { originalError: error });
}
}
}
```
### Triển Khai Repository Cụ Thể
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/repositories/user.repository.ts
import { PrismaClient, User } from '@prisma/client';
import { BaseRepository } from '../modules/common/repository';
/**
User repository for database operations
* VI: Repository người dùng cho các thao tác database
*/
export class UserRepository extends BaseRepository<User, any, any> {
constructor(prisma: PrismaClient) {
super(prisma, 'user');
}
/**
Find user by email
* VI: Tìm người dùng theo email
*/
async findByEmail(email: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { email },
include: {
userRoles: {
include: { role: true },
},
userPermissions: {
include: { permission: true },
},
},
});
}
/**
Find user by username
* VI: Tìm người dùng theo username
*/
async findByUsername(username: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { username },
});
}
/**
Find user with roles and permissions
* VI: Tìm người dùng với roles và permissions
*/
async findWithPermissions(userId: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { id: userId },
include: {
userRoles: {
where: {
OR: [
{ expiresAt: null },
{ expiresAt: { gte: new Date() } },
],
},
include: {
role: {
include: {
permissions: {
include: { permission: true },
},
},
},
},
},
userPermissions: {
where: {
OR: [
{ expiresAt: null },
{ expiresAt: { gte: new Date() } },
],
},
include: { permission: true },
},
},
});
}
}
```
### Pattern Upsert
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/repositories/user-profile.repository.ts
export class UserProfileRepository extends BaseRepository<UserProfile, any, any> {
/**
Create or update profile for user
* VI: Tạo hoặc cập nhật profile cho user
*/
async upsert(userId: string, data: any): Promise<UserProfile> {
return this.prisma.userProfile.upsert({
where: { userId },
update: {
...data,
updatedAt: new Date(),
},
create: {
userId,
...data,
},
include: {
user: {
select: {
id: true,
email: true,
username: true,
},
},
},
});
}
}
```
### Pattern Tối Ưu Query
**Example from codebase / Ví dụ từ codebase**:
```typescript
// services/iam-service/src/modules/feature/feature.repository.ts
export class FeatureRepository extends BaseRepository<Feature, CreateFeatureInput, UpdateFeatureInput> {
/**
Find features by tags
* VI: Tìm features theo tags
*/
async findByTags(tags: string[]): Promise<Feature[]> {
try {
logger.debug('Finding features by tags / Tìm features theo tags', { tags });
const features = await this.prisma.feature.findMany({
where: {
tags: {
hasSome: tags,
},
},
orderBy: { createdAt: 'desc' },
});
logger.debug(`Found ${features.length} features by tags / Đã tìm thấy ${features.length} features theo tags`, { tags });
return features;
} catch (error) {
logger.error('Failed to find features by tags / Không thể tìm features theo tags', { error, tags });
throw this.handleDatabaseError(error, { tags });
}
}
}
```
## Thực Hành Tốt Nhất
### Thiết Kế Schema
- ✅ Use appropriate field types (String, Int, Boolean, DateTime, Json) / Sử dụng field types phù hợp
- ✅ Add indexes for frequently queried fields (`@@index([email])`) / Thêm indexes cho các fields thường query
- ✅ Use relations instead of storing JSON when possible / Sử dụng relations thay vì lưu JSON khi có thể
- ✅ Implement soft deletes with `deletedAt` field when needed / Triển khai soft deletes với field `deletedAt` khi cần
- ✅ Use `@default()` for default values / Sử dụng `@default()` cho giá trị mặc định
- ✅ Use `@updatedAt` for automatic timestamp updates / Sử dụng `@updatedAt` cho cập nhật timestamp tự động
### Pattern Repository
- ✅ Extend `BaseRepository` for common CRUD operations / Kế thừa `BaseRepository` cho CRUD operations chung
- ✅ Add custom methods for domain-specific queries / Thêm methods tùy chỉnh cho queries đặc thù domain
- ✅ Use `include` and `select` to control data fetching / Sử dụng `include``select` để kiểm soát việc lấy dữ liệu
- ✅ Handle Prisma errors and convert to domain errors / Xử lý lỗi Prisma và chuyển đổi sang domain errors
- ✅ Log database operations for debugging / Log các thao tác database để debug
### Luồng Thực Thi Query / Query Execution Flow
Sơ đồ tuần tự sau minh họa cách các Prisma queries chảy từ lớp ứng dụng đến database:
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<br/>throughout the flow
```
### Tối Ưu Query
- ✅ Use `select` to fetch only needed fields / Sử dụng `select` để chỉ lấy các fields cần thiết
- ✅ Implement pagination with `skip` and `take` / Triển khai pagination với `skip``take`
- ✅ Use indexes for frequently queried fields / Sử dụng indexes cho các fields thường query
- ✅ Avoid N+1 queries by using `include` strategically / Tránh N+1 queries bằng cách sử dụng `include` chiến lược
- ✅ Use `findUnique` instead of `findFirst` when possible / Sử dụng `findUnique` thay vì `findFirst` khi có thể
- ✅ Use transactions for multiple related operations / Sử dụng transactions cho nhiều operations liên quan
### Xử Lý Lỗi
- ✅ Catch Prisma errors and convert to domain errors / Bắt lỗi Prisma và chuyển đổi sang domain errors
- ✅ Handle unique constraint violations (P2002) / Xử lý vi phạm ràng buộc duy nhất
- ✅ Handle record not found (P2025) / Xử lý không tìm thấy bản ghi
- ✅ Log errors with context / Log lỗi kèm context
- ✅ Provide meaningful error messages / Cung cấp thông báo lỗi có ý nghĩa
### Quy Trình Migration / Migration Workflow
Sơ đồ sau mô tả quy trình migration điển hình từ development đến production:
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
```
### Migrations
- ✅ Keep migrations small and focused / Giữ migrations nhỏ và tập trung
- ✅ Test migrations before production / Test migrations trước khi production
- ✅ Backup before major changes / Backup trước khi thay đổi lớn
- ✅ Use descriptive migration names / Sử dụng tên migration mô tả
- ✅ Review generated SQL before applying / Xem xét SQL được generate trước khi áp dụng
### Quản Lý Kết Nối
- ✅ Use connection pooling for production / Sử dụng connection pooling cho production
- ✅ Close connections on application shutdown / Đóng kết nối khi tắt ứng dụng
- ✅ Handle connection errors gracefully / Xử lý lỗi kết nối một cách lịch sự
- ✅ Monitor connection pool usage / Giám sát việc sử dụng connection pool
## Ví Dụ Từ Dự Án
### Ví Dụ Schema
- **IAM Service Schema**: [`services/iam-service/prisma/schema.prisma`](../../../services/iam-service/prisma/schema.prisma)
- **Auth Service Schema**: [`services/iam-service/prisma/schema.prisma`](../../../services/iam-service/prisma/schema.prisma)
### Ví Dụ Repository
- **Base Repository**: [`services/iam-service/src/modules/common/repository.ts`](../../../services/iam-service/src/modules/common/repository.ts)
- **User Repository**: [`services/iam-service/src/repositories/user.repository.ts`](../../../services/iam-service/src/repositories/user.repository.ts)
- **User Profile Repository**: [`services/iam-service/src/repositories/user-profile.repository.ts`](../../../services/iam-service/src/repositories/user-profile.repository.ts)
- **Feature Repository**: [`services/iam-service/src/modules/feature/feature.repository.ts`](../../../services/iam-service/src/modules/feature/feature.repository.ts)
### Ví Dụ Cấu Hình Database
- **Database Config**: [`services/iam-service/src/config/database.config.ts`](../../../services/iam-service/src/config/database.config.ts)
## Tham Khảo Nhanh
### Các Thao Tác Prisma Thường Dùng
```typescript
// Tìm theo ID
const user = await prisma.user.findUnique({ where: { id } });
// Tìm với relations
const user = await prisma.user.findUnique({
where: { id },
include: { profile: true, roles: true },
});
// Tìm nhiều với filters
const users = await prisma.user.findMany({
where: { isActive: true },
skip: 0,
take: 10,
orderBy: { createdAt: 'desc' },
});
// Tạo
const user = await prisma.user.create({
data: { email, passwordHash },
});
// Cập nhật
const user = await prisma.user.update({
where: { id },
data: { isActive: false },
});
// Xóa
await prisma.user.delete({ where: { id } });
// Tạo hoặc cập nhật
const profile = await prisma.userProfile.upsert({
where: { userId },
update: { ...data },
create: { userId, ...data },
});
// Transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.userProfile.create({ data: { userId: user.id, ...profileData } });
});
```
### Mã Lỗi Prisma
| Code | Description / Mô Tả |
|------|-------------------|
| P2002 | Unique constraint violation / Vi phạm ràng buộc duy nhất |
| P2025 | Record not found / Không tìm thấy bản ghi |
| P2003 | Foreign key constraint violation / Vi phạm ràng buộc khóa ngoại |
| P2014 | Required relation violation / Vi phạm quan hệ bắt buộc |
## Skills Liên Quan
- **[API Design](./api-design.md)**: For designing API endpoints that use repositories / Cho thiết kế API endpoints sử dụng repositories
- **[Testing Patterns](./testing-patterns.md)**: For testing database operations / Cho test các thao tác database
- **[Security](./security.md)**: For securing database operations and preventing SQL injection / Cho bảo mật các thao tác database và ngăn chặn SQL injection
- **[Observability](./observability-monitoring.md)**: For monitoring database performance / Cho giám sát hiệu năng database
## Tài Nguyên
### Tài Liệu Bên Ngoài
- [Prisma Documentation](https://www.prisma.io/docs)
- [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference)
- [Prisma Client API](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference)
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
- [Neon Documentation](https://neon.tech/docs)
### Tài Liệu Nội Bộ
- [Neon Database Guide](../guides/neon-database.md)
- [Development Guide](../guides/development.md)

View File

@@ -1,585 +0,0 @@
# Triển Khai Kubernetes
Kubernetes deployment patterns for GoodGo microservices. Use when deploying to staging/production, creating K8s manifests, configuring HPA, setting up ingress, or troubleshooting K8s deployments.
> Các pattern triển khai Kubernetes cho microservices GoodGo. Sử dụng khi triển khai lên staging/production, tạo K8s manifests, cấu hình HPA, thiết lập ingress, hoặc xử lý sự cố triển khai K8s.
## Tổng Quan
This skill covers Kubernetes deployment patterns and best practices for GoodGo microservices. It includes creating deployment manifests, configuring autoscaling, managing secrets and configmaps, setting up ingress, and implementing health checks.
Skill này bao gồm các pattern triển khai Kubernetes và best practices cho microservices GoodGo. Nó bao gồm tạo deployment manifests, cấu hình autoscaling, quản lý secrets và configmaps, thiết lập ingress, và triển khai health checks.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Triển khai services lên môi trường staging/production
- Tạo hoặc cập nhật Kubernetes manifests
- Cấu hình autoscaling (HPA/VPA)
- Thiết lập ingress và load balancing
- Quản lý secrets và configmaps
- Xử lý sự cố triển khai
- Triển khai health checks và probes
- Thiết lập monitoring và logging
## Khái Niệm Chính
### Kiến Trúc Kubernetes
Sơ đồ sau minh họa các thành phần Kubernetes chính và mối quan hệ của chúng trong một triển khai service GoodGo điển hình:
```mermaid
graph TB
subgraph External["External Traffic"]
Client[Client Request]
end
subgraph IngressLayer["Ingress Layer"]
Ingress[Ingress<br/>api.goodgo.com]
end
subgraph ServiceLayer["Service Layer"]
Service[Service<br/>ClusterIP]
end
subgraph DeploymentLayer["Deployment Layer"]
Deployment[Deployment<br/>auth-service]
HPA[HorizontalPodAutoscaler<br/>HPA]
end
subgraph PodLayer["Pod Layer"]
Pod1[Pod 1<br/>Container]
Pod2[Pod 2<br/>Container]
Pod3[Pod 3<br/>Container]
end
subgraph ConfigLayer["Configuration Layer"]
ConfigMap[ConfigMap<br/>app-config]
Secret[Secret<br/>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
```
### Chiến Lược Triển Khai
- Rolling updates để triển khai không downtime
- Resource limits và requests để đảm bảo ổn định
- Health checks (liveness/readiness probes)
- Horizontal Pod Autoscaler (HPA) để tự động scale
- ConfigMaps cho cấu hình
- Secrets cho dữ liệu nhạy cảm
### Vòng Đời Pod
Pods trải qua các trạng thái khác nhau trong vòng đời của chúng. Health checks (liveness và readiness probes) xác định khả năng sẵn sàng của pod:
```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
```
### Luồng Service Discovery
Kubernetes cung cấp service discovery tích hợp thông qua DNS. Sơ đồ sau cho thấy cách requests chảy từ client bên ngoài đến pods:
```mermaid
sequenceDiagram
participant Client
participant Ingress
participant Service
participant Pod1
participant Pod2
participant Pod3
Client->>Ingress: HTTPS Request<br/>api.goodgo.com/auth/login
Ingress->>Ingress: TLS Termination
Ingress->>Ingress: Path Routing<br/>/auth → auth-service
Ingress->>Service: HTTP Request<br/>auth-service:80
Service->>Service: DNS Resolution<br/>auth-service.goodgo.svc.cluster.local
Service->>Service: Endpoint Selection<br/>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
```
## Các Pattern Thường Dùng
### Manifest Triển Khai Service
Standard deployment manifest structure for GoodGo services.
Cấu trúc deployment manifest chuẩn cho các services GoodGo.
**Ví dụ từ codebase**: [`deployments/production/kubernetes/iam-service.yaml`](../../../deployments/production/kubernetes/iam-service.yaml)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: iam-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: iam-service
template:
metadata:
labels:
app: iam-service
spec:
containers:
- name: iam-service
image: goodgo/iam-service:latest
imagePullPolicy: Always
ports:
- containerPort: 5001
envFrom:
- configMapRef:
name: iam-service-config
- secretRef:
name: iam-service-secrets
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 5001
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 5001
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: iam-service
namespace: production
spec:
selector:
app: iam-service
ports:
- protocol: TCP
port: 5001
targetPort: 5001
type: ClusterIP
```
### Tự Động Scale Pod
Configure HPA to automatically scale pods based on CPU and memory utilization.
Cấu hình HPA để tự động scale pods dựa trên CPU và memory utilization.
**Ví dụ từ codebase**: [`deployments/production/kubernetes/iam-service.yaml`](../../../deployments/production/kubernetes/iam-service.yaml)
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: iam-service-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: iam-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
```
### ConfigMap và Secrets
Use ConfigMaps for non-sensitive configuration and Secrets for sensitive data.
Sử dụng ConfigMaps cho cấu hình không nhạy cảm và Secrets cho dữ liệu nhạy cảm.
**Ví dụ từ codebase**:
- ConfigMap: [`deployments/production/kubernetes/iam-service-configmap.yaml`](../../../deployments/production/kubernetes/iam-service-configmap.yaml)
- Secrets: [`deployments/production/kubernetes/secrets.yaml.example`](../../../deployments/production/kubernetes/secrets.yaml.example)
```yaml
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: iam-service-config
namespace: production
data:
"production"
PORT: "5001"
API_VERSION: "v1"
CORS_ORIGIN: "https://goodgo.vn"
LOG_LEVEL: "warn"
SERVICE_NAME: "iam-service"
"true"
---
# Secret (example - use sealed-secrets in production)
apiVersion: v1
kind: Secret
metadata:
name: iam-service-secrets
namespace: production
type: Opaque
stringData:
database-url: "postgresql://user:password@ep-xxx.region.neon.tech/dbname?sslmode=require&pgbouncer=true"
jwt-secret: "your-production-jwt-secret-min-32-chars"
jwt-refresh-secret: "your-production-refresh-secret-min-32-chars"
redis-password: ""
```
### Cấu Hình Ingress
Configure ingress for external access with TLS and path-based routing.
Cấu hình ingress để truy cập từ bên ngoài với TLS và path-based routing.
**Ví dụ từ codebase**: [`deployments/production/kubernetes/ingress.yaml`](../../../deployments/production/kubernetes/ingress.yaml)
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
traefik.ingress.kubernetes.io/rule-type: PathPrefix
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: traefik
tls:
- hosts:
- api.goodgo.vn
secretName: api-tls-cert
rules:
- host: api.goodgo.vn
http:
paths:
- path: /api/v1/auth
pathType: Prefix
backend:
service:
name: iam-service
port:
number: 5001
```
## Thực Hành Tốt Nhất
### Quản Lý Tài Nguyên
- Always set resource requests and limits
- Monitor actual usage and adjust accordingly
- Use HPA for automatic scaling
- Set appropriate CPU and memory based on service requirements
**VI**:
- Luôn đặt resource requests và limits
- Theo dõi sử dụng thực tế và điều chỉnh phù hợp
- Sử dụng HPA để tự động scale
- Đặt CPU và memory phù hợp dựa trên yêu cầu service
### Cấu Hình
- Use ConfigMaps for non-sensitive config
- Use Secrets for sensitive data
- Never hardcode configuration in images
- Use `envFrom` to load entire ConfigMap/Secret
**VI**:
- Sử dụng ConfigMaps cho cấu hình không nhạy cảm
- Sử dụng Secrets cho dữ liệu nhạy cảm
- Không bao giờ hardcode cấu hình trong images
- Sử dụng `envFrom` để load toàn bộ ConfigMap/Secret
### Kiểm Tra Sức Khỏe
- Implement both liveness and readiness probes
- Set appropriate timeouts and thresholds
- Include dependency checks in readiness probe
- Use HTTP probes for web services
**VI**:
- Triển khai cả liveness và readiness probes
- Đặt timeouts và thresholds phù hợp
- Bao gồm kiểm tra dependencies trong readiness probe
- Sử dụng HTTP probes cho web services
**Ví dụ từ codebase**: [`services/iam-service/src/modules/health/health.controller.ts`](../../../services/iam-service/src/modules/health/health.controller.ts)
```typescript
// Liveness probe - is the service alive?
health = async (_req: Request, res: Response): Promise<void> => {
res.json({
success: true,
data: { status: 'ok', timestamp: new Date().toISOString() },
});
};
// Readiness probe - is the service ready to accept traffic?
ready = async (_req: Request, res: Response): Promise<void> => {
try {
// Check database connection
await prisma.$queryRaw`SELECT 1`;
res.json({
success: true,
data: { status: 'ready' },
});
} catch (error) {
res.status(503).json({
success: false,
error: { code: 'HEALTH_001', message: 'Service not ready' },
});
}
};
```
### Triển Khai
- Use rolling updates for zero-downtime
- Set maxSurge and maxUnavailable appropriately
- Test deployments in staging first
- Use image tags instead of `latest` in production
**VI**:
- Sử dụng rolling updates để không downtime
- Đặt maxSurge và maxUnavailable phù hợp
- Test triển khai trong staging trước
- Sử dụng image tags thay vì `latest` trong production
### Bảo Mật
- Run containers as non-root user
- Use network policies to restrict traffic
- Regularly update base images
- Use sealed-secrets or external secret manager
- Never commit secrets to Git
**VI**:
- Chạy containers với user không phải root
- Sử dụng network policies để hạn chế traffic
- Cập nhật base images thường xuyên
- Sử dụng sealed-secrets hoặc external secret manager
- Không bao giờ commit secrets vào Git
### Giám Sát
- Expose metrics endpoint (`/metrics`)
- Set up alerts for critical issues
- Monitor resource usage and performance
- Use ServiceMonitor for Prometheus integration
**VI**:
- Expose metrics endpoint (`/metrics`)
- Thiết lập alerts cho các vấn đề quan trọng
- Theo dõi sử dụng tài nguyên và hiệu suất
- Sử dụng ServiceMonitor cho tích hợp Prometheus
## Ví Dụ Từ Dự Án
### Triển Khai Production
- **IAM Service**: [`deployments/production/kubernetes/iam-service.yaml`](../../../deployments/production/kubernetes/iam-service.yaml)
- **ConfigMap**: [`deployments/production/kubernetes/iam-service-configmap.yaml`](../../../deployments/production/kubernetes/iam-service-configmap.yaml)
- **Ingress**: [`deployments/production/kubernetes/ingress.yaml`](../../../deployments/production/kubernetes/ingress.yaml)
### Triển Khai Staging
- **IAM Service**: [`deployments/staging/kubernetes/iam-service.yaml`](../../../deployments/staging/kubernetes/iam-service.yaml)
- **ConfigMap**: [`deployments/staging/kubernetes/iam-service-configmap.yaml`](../../../deployments/staging/kubernetes/iam-service-configmap.yaml)
### Triển Khai Health Check
- **Health Controller**: [`services/iam-service/src/modules/health/health.controller.ts`](../../../services/iam-service/src/modules/health/health.controller.ts)
## Tham Khảo Nhanh
### Lệnh Thường Dùng
```bash
# Deploy to production
kubectl apply -f deployments/production/kubernetes/ -n production
# Check deployment status
kubectl get deployments -n production
kubectl get pods -n production
kubectl get svc -n production
# View logs
kubectl logs -f deployment/iam-service -n production
kubectl logs -f pod-name -n production --tail=100
# Scale manually
kubectl scale deployment iam-service --replicas=5 -n production
# Update image
kubectl set image deployment/iam-service iam-service=goodgo/iam-service:v1.2.3 -n production
# Rollback
kubectl rollout undo deployment/iam-service -n production
# Port forward for debugging
kubectl port-forward service/iam-service 5001:5001 -n production
# Execute command in pod
kubectl exec -it pod-name -n production -- /bin/sh
# View HPA status
kubectl get hpa -n production
kubectl describe hpa iam-service-hpa -n production
# View resource usage
kubectl top nodes
kubectl top pods -n production
```
### Xử Lý Sự Cố
**Pod Not Starting / Pod Không Khởi Động**:
```bash
# Check pod status
kubectl describe pod pod-name -n production
# Check events
kubectl get events -n production --sort-by='.lastTimestamp'
# Check logs
kubectl logs pod-name -n production --previous
```
**ImagePullBackOff**:
```bash
# Check image name and tag
kubectl describe pod pod-name -n production | grep -i image
# Check image pull secrets
kubectl get secrets -n production
```
**CrashLoopBackOff**:
```bash
# Check logs of crashed container
kubectl logs pod-name -n production --previous
# Check resource limits
kubectl describe pod pod-name -n production | grep -A 5 Limits
```
## Skills Liên Quan
- [Observability & Monitoring](./observability-monitoring.md) - Để giám sát các services đã triển khai
- [Security](./security.md) - Để bảo mật các triển khai Kubernetes
- [Project Rules](./project-rules.md) - Cho cấu trúc và tiêu chuẩn service
## Tài Nguyên
### Tài Liệu Chính Thức
- [Kubernetes Documentation](https://kubernetes.io/docs/)
- [Kubernetes API Reference](https://kubernetes.io/docs/reference/kubernetes-api/)
- [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)
### Tài Nguyên GoodGo
- [Deployment Guide](../guides/deployment.md)
- [Local Deployment Guide](../guides/local-deployment.md)
- [Troubleshooting Guide](../guides/troubleshooting.md)

View File

@@ -1,602 +0,0 @@
# Tài Liệu
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.
> Hướng dẫn viết tài liệu kỹ thuật trong dự án GoodGo. Sử dụng khi tạo hoặc cập nhật file README, hướng dẫn, tài liệu kiến trúc hoặc tài liệu API. Đảm bảo tính nhất quán song ngữ (EN/VI) và cấu trúc phù hợp.
## Tổng Quan
The Documentation skill provides comprehensive guidelines for writing, structuring, and maintaining technical documentation in the GoodGo Microservices Platform. It covers documentation structure, bilingual formatting, templates, writing style, and best practices for maintaining documentation quality.
Skill Documentation cung cấp hướng dẫn toàn diện để viết, cấu trúc và duy trì tài liệu kỹ thuật trong GoodGo Microservices Platform. Nó bao gồm cấu trúc tài liệu, định dạng song ngữ, template, phong cách viết và thực hành tốt nhất để duy trì chất lượng tài liệu.
## Khi Nào Sử Dụng
Use this skill when:
- Creating new documentation files
- Updating existing documentation
- Writing README files for services or packages
- Creating guides or tutorials
- Documenting API endpoints
- Writing architecture documentation
- Creating deployment documentation
- Writing runbooks or operational guides
- Ensuring bilingual consistency
Sử dụng skill này khi:
- Tạo file tài liệu mới
- Cập nhật tài liệu hiện có
- Viết file README cho services hoặc packages
- Tạo hướng dẫn hoặc tutorial
- Tài liệu hóa API endpoints
- Viết tài liệu kiến trúc
- Tạo tài liệu triển khai
- Viết runbook hoặc hướng dẫn vận hành
- Đảm bảo tính nhất quán song ngữ
## Khái Niệm Chính
### Cấu Trúc Tài Liệu
The project follows a structured documentation hierarchy:
```
docs/
├── en/ # English documentation
│ ├── guides/ # How-to guides
│ ├── architecture/ # System design docs
│ ├── api/ # API documentation
│ ├── onboarding/ # New developer guides
│ ├── runbooks/ # Operational guides
│ └── skills/ # Skill documentation
├── vi/ # Vietnamese documentation (mirror structure)
└── README.md # Documentation index
```
Dự án tuân theo hệ thống phân cấp tài liệu có cấu trúc:
```
docs/
├── en/ # Tài liệu tiếng Anh
│ ├── guides/ # Hướng dẫn cách làm
│ ├── architecture/ # Tài liệu thiết kế hệ thống
│ ├── api/ # Tài liệu API
│ ├── onboarding/ # Hướng dẫn cho developer mới
│ ├── runbooks/ # Hướng dẫn vận hành
│ └── skills/ # Tài liệu skills
├── vi/ # Tài liệu tiếng Việt (cấu trúc tương tự)
└── README.md # Mục lục tài liệu
```
#### Sơ Đồ Cấu Trúc Tài Liệu
Sơ đồ sau minh họa nơi đặt các loại tài liệu khác nhau:
```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/<br/>docs/vi/"]
ProjectLoc --> ProjectSub{Content Type?}
ProjectSub -->|Guides| GuidesLoc["guides/<br/>(getting-started.md,<br/>deployment.md)"]
ProjectSub -->|Architecture| ArchLoc["architecture/<br/>(system-design.md)"]
ProjectSub -->|API Specs| APILoc["api/openapi/<br/>(*.yaml)"]
ProjectSub -->|Runbooks| RunbookLoc["runbooks/<br/>(incident-response.md)"]
ServiceDocs --> ServiceLoc["services/[name]/README.md<br/>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
```
### Quy Tắc Tài Liệu Song Ngữ
The project maintains bilingual documentation (English and Vietnamese). Three formats are used:
1. **Side-by-side**: Short content with EN and VI on the same line
2. **Separate files**: Long content in separate EN and VI files
3. **Sections**: Mixed content with separate EN and VI sections
Dự án duy trì tài liệu song ngữ (Tiếng Anh và Tiếng Việt). Ba định dạng được sử dụng:
1. **Side-by-side**: Nội dung ngắn với EN và VI trên cùng một dòng
2. **Separate files**: Nội dung dài trong các file EN và VI riêng biệt
3. **Sections**: Nội dung hỗn hợp với các phần EN và VI riêng biệt
#### Quy Trình Quyết Định Định Dạng Song Ngữ
Sử dụng cây quyết định sau để chọn định dạng song ngữ phù hợp:
```mermaid
flowchart TD
Start[Creating Documentation] --> CheckLength{Content Length?}
CheckLength -->|Short<br/>< 200 lines| CheckLocation{Document Location?}
CheckLength -->|Long<br/>> 200 lines| SeparateFiles[Use Separate Files Format]
CheckLocation -->|README files<br/>Service/Package docs<br/>Infrastructure docs| SideBySide[Use Side-by-side Format]
CheckLocation -->|docs/guides/<br/>Short configuration docs| SideBySide
CheckLength -->|Medium| CheckType{Content Type?}
CheckType -->|API Documentation<br/>Technical Specifications| Sections[Use Sections Format]
CheckType -->|Mixed Content| Sections
SeparateFiles --> SeparateAction["Create docs/en/[path]/file.md<br/>Create docs/vi/[path]/file.md<br/>(Mirror structure)"]
SideBySide --> SideBySideAction["Single file with<br/>EN / VI inline<br/>Example: 'Title / Tiêu Đề'"]
Sections --> SectionsAction["Single file with<br/>--- separator<br/>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
```
#### Khi Nào Sử Dụng Từng Định Dạng
- **Side-by-side**: README files, hướng dẫn ngắn, tài liệu cấu hình
- **Separate files**: Hướng dẫn dài (>200 dòng), tài liệu kiến trúc, runbooks
- **Sections**: Tài liệu API, đặc tả kỹ thuật
## Các Pattern Thường Dùng
### Template README Service
Example structure from `services/iam-service/README.md`:
```markdown
# Tên Dịch Vụ
Brief description in English
> Mô tả ngắn gọn bằng tiếng Việt
## Tính Năng
- Feature 1 / Tính năng 1
- Feature 2 / Tính năng 2
## Yêu Cầu
- Node.js 20+
- PostgreSQL (Neon)
- Redis
## Bắt Đầu Nhanh
```bash
# Cài đặt dependencies
pnpm install
# Thiết lập môi trường
cp .env.example .env
# Khởi động service
pnpm dev
```
## Cấu Hình
| Variable | Description / Mô Tả | Default | Required |
|----------|---------------------|---------|----------|
| PORT | Server port / Cổng server | 5000 | No |
## API Endpoints
See [API Documentation](../api/openapi/iam-service.yaml)
```
Ví dụ cấu trúc từ `services/iam-service/README.md`:
```markdown
# Tên Dịch Vụ
Brief description in English
> Mô tả ngắn gọn bằng tiếng Việt
## Tính Năng
- Feature 1 / Tính năng 1
- Feature 2 / Tính năng 2
## Yêu Cầu
- Node.js 20+
- PostgreSQL (Neon)
- Redis
## Bắt Đầu Nhanh
```bash
# Cài đặt dependencies
pnpm install
# Thiết lập môi trường
cp .env.example .env
# Khởi động service
pnpm dev
```
## Cấu Hình
| Variable | Description / Mô Tả | Default | Required |
|----------|---------------------|---------|----------|
| PORT | Server port / Cổng server | 5000 | No |
## API Endpoints
See [API Documentation](../api/openapi/iam-service.yaml)
```
### Template Hướng Dẫn
Example from `docs/en/guides/getting-started.md`:
```markdown
# Getting Started
## Prerequisites
- Node.js >= 20.0.0
- PNPM >= 8.0.0
- Docker & Docker Compose
- Git
- Neon account (https://neon.tech) - for database
## Initial Setup
1. **Clone the repository**
```bash
git clone <repository-url>
cd Base
```
2. **Setup Neon Database**
```bash
# Run setup script
./scripts/db/setup-neon.sh
```
See [Neon Setup Guide](../../../infra/databases/neon/README.md) for details.
3. **Initialize the project**
```bash
./scripts/setup/init-project.sh
```
## Next Steps
- Read [Development Guide](../guides/development.md)
- Check [API Documentation](../api/openapi/)
- Review [Architecture Overview](../architecture/system-design.md)
```
Ví dụ từ `docs/vi/guides/getting-started.md`:
```markdown
# Bắt Đầu
## Yêu Cầu
- Node.js >= 20.0.0
- PNPM >= 8.0.0
- Docker & Docker Compose
- Git
- Tài khoản Neon (https://neon.tech) - cho database
## Thiết Lập Ban Đầu
1. **Clone repository**
```bash
git clone <repository-url>
cd Base
```
2. **Thiết lập Neon Database**
```bash
# Chạy script setup
./scripts/db/setup-neon.sh
```
Xem [Hướng Dẫn Thiết Lập Neon](../../../infra/databases/neon/README.md) để biết chi tiết.
3. **Khởi tạo dự án**
```bash
./scripts/setup/init-project.sh
```
## Bước Tiếp Theo
- Đọc [Hướng Dẫn Phát Triển](../guides/development.md)
- Xem [Tài Liệu API](../api/openapi/)
- Xem lại [Tổng Quan Kiến Trúc](../architecture/system-design.md)
```
### Template Tài Liệu Kiến Trúc
Example from `docs/en/architecture/system-design.md`:
```markdown
# System Design
## Overview
GoodGo Microservices Platform is built using a microservices architecture pattern with the following principles:
- **Service Independence**: Each service has its own database and can be deployed independently
- **API Gateway**: Traefik handles routing, load balancing, and cross-cutting concerns
- **Shared Libraries**: Common functionality is extracted into shared packages
- **Infrastructure as Code**: All infrastructure configurations are versioned
- **Observability**: Full monitoring, logging, and tracing capabilities
## Architecture Diagram
```
┌─────────────┐ ┌─────────────┐
│ Web App │ │ Mobile App │
│ (Next.js) │ │ (React Native)
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬────────┘
┌────────▼────────┐
│ Traefik │
│ (API Gateway) │
└────────┬─────────┘
```
## Components
### Frontend Layer
- **Web App**: Next.js application with App Router
- **Mobile App**: React Native application
### API Gateway
- **Traefik**: Reverse proxy, load balancer, SSL termination
```
Ví dụ từ `docs/vi/architecture/system-design.md`:
```markdown
# Thiết Kế Hệ Thống
## Tổng Quan
GoodGo Microservices Platform được xây dựng bằng mẫu kiến trúc microservices với các nguyên tắc sau:
- **Độc Lập Dịch Vụ**: Mỗi service có database riêng và có thể triển khai độc lập
- **API Gateway**: Traefik xử lý routing, load balancing và các mối quan tâm chéo
- **Thư Viện Dùng Chung**: Chức năng chung được trích xuất thành packages dùng chung
- **Infrastructure as Code**: Tất cả cấu hình infrastructure được version
- **Observability**: Khả năng giám sát, logging và tracing đầy đủ
## Sơ Đồ Kiến Trúc
```
┌─────────────┐ ┌─────────────┐
│ Web App │ │ Mobile App │
│ (Next.js) │ │ (React Native)
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬────────┘
┌────────▼────────┐
│ Traefik │
│ (API Gateway) │
└────────┬─────────┘
```
## Thành Phần
### Lớp Frontend
- **Web App**: Ứng dụng Next.js với App Router
- **Mobile App**: Ứng dụng React Native
### API Gateway
- **Traefik**: Reverse proxy, load balancer, SSL termination
```
## Thực Hành Tốt Nhất
### Phong Cách Viết
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
**VI**:
1. **Rõ Ràng và Súc Tích**: Sử dụng ngôn ngữ đơn giản, tránh thuật ngữ
2. **Hướng Hành Động**: Bắt đầu bằng động từ (Cài đặt, Cấu hình, Triển khai)
3. **Có Cấu Trúc**: Sử dụng tiêu đề, danh sách và bảng
4. **Ví Dụ**: Cung cấp ví dụ code và lệnh
5. **Trực Quan**: Sử dụng sơ đồ khi hữu ích
### Ví Dụ Code
Always provide context and explanation:
```markdown
# Good: With context and explanation
Install dependencies using pnpm:
```bash
pnpm install
```
# Bad: No context
```bash
pnpm install
```
```
Luôn cung cấp ngữ cảnh và giải thích:
```markdown
# Tốt: Có ngữ cảnh và giải thích
Cài đặt dependencies sử dụng pnpm:
```bash
pnpm install
```
# Không tốt: Không có ngữ cảnh
```bash
pnpm install
```
```
### Liên Kết
- Use relative links for internal docs
- Use descriptive link text (not "click here")
- Example: `See the [Deployment Guide](../guides/deployment.md) for details.`
**VI**:
- Sử dụng liên kết tương đối cho tài liệu nội bộ
- Sử dụng văn bản liên kết mô tả (không phải "click here")
- Ví dụ: `Xem [Hướng Dẫn Triển Khai](../guides/deployment.md) để biết chi tiết.`
## Ví Dụ Từ Dự Án
### Ví Dụ Tài Liệu Tốt
- `docs/en/guides/getting-started.md` - Clear step-by-step guide
- `services/iam-service/README.md` - Comprehensive service README
- `docs/en/architecture/system-design.md` - Architecture documentation
- `docs/README.md` - Documentation index
**VI**:
- `docs/vi/guides/getting-started.md` - Hướng dẫn từng bước rõ ràng
- `services/iam-service/README.md` - README service toàn diện
- `docs/vi/architecture/system-design.md` - Tài liệu kiến trúc
- `docs/README.md` - Mục lục tài liệu
### Tham Chiếu Vị Trí Tài Liệu
| 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 |
**VI**:
| Loại Nội Dung | Vị Trí | Định Dạng |
|--------------|--------|----------|
| Bắt Đầu | `docs/vi/guides/getting-started.md` | File riêng biệt |
| Thiết Lập Service | `services/[name]/README.md` | Side-by-side |
| Triển Khai | `docs/vi/guides/deployment.md` | File riêng biệt |
| Kiến Trúc | `docs/vi/architecture/` | File riêng biệt |
| Spec API | `docs/vi/api/openapi/` | OpenAPI YAML |
| Runbooks | `docs/vi/runbooks/` | File riêng biệt |
| Infrastructure | `infra/[component]/README.md` | Side-by-side |
## Tham Khảo Nhanh
### Đặt Tên File
- Use kebab-case: `getting-started.md`
- Be descriptive: `local-development.md` not `dev.md`
- Match EN and VI filenames
**VI**:
- Sử dụng kebab-case: `getting-started.md`
- Mô tả rõ ràng: `local-development.md` không phải `dev.md`
- Khớp tên file EN và VI
### Cấp Độ Tiêu Đề
```markdown
# H1: Document Title (only one per file)
## H2: Major Sections
### H3: Subsections
#### H4: Details (use sparingly)
```
### Mẫu Song Ngữ
```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
Install dependencies
# VI: Cài đặt dependencies
pnpm install
```
### Danh Sách Kiểm Tra Tài Liệu
Before publishing documentation:
- [ ] Determine correct location (docs/ vs service README)
- [ ] Choose bilingual format (side-by-side vs separate)
- [ ] Review existing docs for consistency
- [ ] Use clear, concise language
- [ ] Include code examples
- [ ] Add diagrams where helpful
- [ ] Provide troubleshooting section
- [ ] Link to related documentation
- [ ] Test all commands and code examples
- [ ] Check all links work
- [ ] Ensure bilingual consistency
- [ ] Update documentation index
Trước khi xuất bản tài liệu:
- [ ] Xác định vị trí đúng (docs/ vs service README)
- [ ] Chọn định dạng song ngữ (side-by-side vs separate)
- [ ] Xem lại tài liệu hiện có để đảm bảo tính nhất quán
- [ ] Sử dụng ngôn ngữ rõ ràng, súc tích
- [ ] Bao gồm ví dụ code
- [ ] Thêm sơ đồ khi hữu ích
- [ ] Cung cấp phần xử lý sự cố
- [ ] Liên kết đến tài liệu liên quan
- [ ] Kiểm tra tất cả lệnh và ví dụ code
- [ ] Kiểm tra tất cả liên kết hoạt động
- [ ] Đảm bảo tính nhất quán song ngữ
- [ ] Cập nhật mục lục tài liệu
## Skills Liên Quan
- **[Project Rules](./project-rules.md)** - Project structure and standards
- **[API Design](./api-design.md)** - API documentation patterns
- **[Comment Code](./comment-code.md)** - Code commenting guidelines
## Tài Nguyên
- [Markdown Guide](https://www.markdownguide.org/)
- [Mermaid Diagrams](https://mermaid.js.org/)
- [Documentation Best Practices](https://www.writethedocs.org/guide/)
**VI**:
- [Hướng Dẫn Markdown](https://www.markdownguide.org/)
- [Sơ Đồ Mermaid](https://mermaid.js.org/)
- [Thực Hành Tốt Nhất Tài Liệu](https://www.writethedocs.org/guide/)

View File

@@ -1,488 +0,0 @@
# Các Pattern Xử Lý Lỗi
Comprehensive error handling patterns and conventions for GoodGo microservices including error classes, error codes, error middleware, and standardized error responses.
> Các patterns và conventions toàn diện về error handling cho GoodGo microservices bao gồm error classes, error codes, error middleware, và standardized error responses.
## Tổng Quan
Error handling is a critical aspect of building robust microservices. This guide provides comprehensive patterns and best practices for implementing consistent, maintainable error handling across all services. It covers error types, error code systems, custom error classes, error middleware, and standardized error response formats.
Error handling là khía cạnh quan trọng của việc xây dựng microservices vững chắc. Hướng dẫn này cung cấp các patterns và best practices toàn diện để implement error handling nhất quán và dễ bảo trì trên tất cả các services. Nó bao gồm các loại errors, hệ thống error codes, custom error classes, error middleware, và các định dạng error response đã được chuẩn hóa.
## Khi Nào Sử Dụng
Use these error handling patterns 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
Sử dụng các error handling patterns này khi:
- Implement error handling trong services, controllers, hoặc repositories
- Tạo custom error classes cho các error scenarios cụ thể
- Chuẩn hóa error responses trên các APIs
- Xử lý exceptions từ external services hoặc database operations
- Implement error middleware và global error handlers
- Debug error scenarios và cải thiện error messages
- Phân biệt giữa operational errors và programming errors
## Khái Niệm Chính
### Các Loại Lỗi
#### Lỗi Vận Hành
Expected errors that occur during normal operation. These are errors that the application is designed to handle.
Các lỗi dự kiến xảy ra trong quá trình vận hành bình thường. Đây là các lỗi mà ứng dụng được thiết kế để xử lý.
**Characteristics / Đặc Điểm**:
- **Examples / Ví dụ**: Validation errors, authentication failures, resource not found, rate limiting
- **Handling / Xử Lý**: Should be handled gracefully with appropriate HTTP status codes
- **Logging / Ghi Log**: Logged as warnings (logger.warn)
- **Exposure / Tiết Lộ**: Can expose error details to clients (with caution)
- **Recovery / Phục Hồi**: Application can recover and continue operation
**Example / Ví dụ**: [`services/iam-service/src/errors/http-error.ts`](../../../services/iam-service/src/errors/http-error.ts)
#### Lỗi Lập Trình
Unexpected errors due to bugs in code. These indicate problems that need to be fixed.
Các lỗi không mong đợi do bugs trong code. Đây là các vấn đề cần được sửa.
**Characteristics / Đặc Điểm**:
- **Examples / Ví dụ**: Null pointer exceptions, type errors, logic bugs, undefined behavior
- **Handling / Xử Lý**: Should be logged with full details for debugging
- **Logging / Ghi Log**: Logged as errors (logger.error)
- **Exposure / Tiết Lộ**: Should return generic error messages to clients (hide implementation details)
- **Recovery / Phục Hồi**: May require application restart or code fixes
### Luồng Lan Truyền Lỗi / Error Propagation Flow
Sơ đồ sau minh họa cách lỗi lan truyền qua các lớp của ứng dụng:
```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
```
### Cấu Trúc Phân Cấp Lỗi / Error Hierarchy Structure
Phân cấp lớp lỗi cho thấy mối quan hệ giữa các loại lỗi khác nhau:
```mermaid
classDiagram
class Error {
<<built-in>>
+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
```
### Cây Quyết Định Xử Lý Lỗi / Error Handling Decision Tree
Sử dụng cây quyết định này để xác định lớp lỗi nào cần sử dụng:
```mermaid
flowchart TD
A[Error Occurs] --> B{Error Type?}
B -->|Resource Not Found| C[NotFoundError<br/>404]
B -->|Invalid Input| D{Validation?}
B -->|Authentication| E{Type?}
B -->|Resource Conflict| F[ConflictError<br/>409]
B -->|Rate Limit| G[RateLimitError<br/>429]
B -->|Database| H[DatabaseError<br/>500]
B -->|External Service| I[ExternalServiceError<br/>502]
B -->|Service Unavailable| J[ServiceUnavailableError<br/>503]
B -->|Unknown/Programming| K[InternalServerError<br/>500]
D -->|Schema Validation| L[ValidationError<br/>422]
D -->|Bad Request Format| M[BadRequestError<br/>400]
E -->|No Token/Invalid| N[UnauthorizedError<br/>401]
E -->|No Permission| O[ForbiddenError<br/>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]
```
### Hệ Thống Error Codes
The platform uses a centralized error code system that provides unique identifiers for each error type, enables error tracking and analytics, and supports consistent error handling.
Nền tảng sử dụng hệ thống error codes tập trung cung cấp unique identifiers cho mỗi loại lỗi, cho phép error tracking và analytics, và hỗ trợ error handling nhất quán.
**Error Code Format / Định Dạng Error Code**: `{CATEGORY}_{NUMBER}`
- `AUTH_001` - Authentication errors / Lỗi xác thực
- `VALIDATION_001` - Validation errors / Lỗi validation
- `RESOURCE_001` - Resource errors / Lỗi tài nguyên
- `DB_001` - Database errors / Lỗi database
- `SYS_001` - System errors / Lỗi hệ thống
**Tham Khảo**: [`services/iam-service/src/errors/error-codes.ts`](../../../services/iam-service/src/errors/error-codes.ts)
## Patterns
### Class Lỗi Cơ Sở
**HttpError** là base class cho tất cả custom errors:
```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(),
};
}
}
```
### Các Class Lỗi Chuẩn
**Resource Errors / Lỗi Tài Nguyên**:
- `NotFoundError` - 404: Resource not found / Tài nguyên không tìm thấy
- `ConflictError` - 409: Resource conflict / Xung đột tài nguyên (ví dụ: trùng lặp)
- `BadRequestError` - 400: Invalid request / Yêu cầu không hợp lệ
**Validation Errors / Lỗi Validation**:
- `ValidationError` - 422: Input validation failed / Validation đầu vào thất bại
**Authentication/Authorization / Xác Thực/Phân Quyền**:
- `UnauthorizedError` - 401: Authentication required / Yêu cầu xác thực
- `ForbiddenError` - 403: Access denied / Truy cập bị từ chối
**System Errors / Lỗi Hệ Thống**:
- `InternalServerError` - 500: Internal server error / Lỗi máy chủ nội bộ (programming error)
- `ServiceUnavailableError` - 503: Service temporarily unavailable / Dịch vụ tạm thời không khả dụng
- `DatabaseError` - 500: Database operation failed / Thao tác database thất bại
- `ExternalServiceError` - 502: External service error / Lỗi dịch vụ bên ngoài
**Rate Limiting / Giới Hạn Tốc Độ**:
- `RateLimitError` - 429: Too many requests / Quá nhiều yêu cầu
### Sử Dụng Errors Trong Services
```typescript
import { NotFoundError, ConflictError } from '../errors/http-error';
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);
}
}
```
**Tham Khảo**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
### Pattern Error Middleware
Global error handler middleware xử lý tất cả 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';
}
// Log and respond...
};
```
**Tham Khảo**: [`services/iam-service/src/middlewares/error.middleware.ts`](../../../services/iam-service/src/middlewares/error.middleware.ts)
### Wrapper Lỗi Async
Wrap async route handlers để 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 });
}));
```
### Định Dạng Error Response
Định dạng error response đã được chuẩn hóa:
```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"
}
```
## Thực Hành Tốt Nhất
1. **Use Specific Error Classes / Sử Dụng Error Classes Cụ Thể**: Sử dụng error class cụ thể nhất cho scenario
2. **Include Context / Bao Gồm Context**: Cung cấp error messages hữu ích với context liên quan (resource IDs, field names)
3. **Mark Operational Errors / Đánh Dấu Operational Errors**: Set `isOperational: true` cho các lỗi dự kiến có thể xử lý gracefully
4. **Don't Expose Internal Details / Không Tiết Lộ Chi Tiết Nội Bộ**: Ẩn implementation details trong production (stack traces, internal paths)
5. **Log Appropriately / Log Phù Hợp**: Sử dụng `logger.error()` cho programming errors, `logger.warn()` cho operational errors
6. **Handle Database Errors / Xử Lý Database Errors**: Map Prisma errors (P2002, P2025, etc.) sang HTTP errors phù hợp
7. **Use Error Codes / Sử Dụng Error Codes**: Luôn sử dụng `ErrorCode` enum để nhất quán trên các services
8. **Validate Early / Validate Sớm**: Validate input sớm trong request pipeline để catch errors trước khi xử lý
9. **Error Middleware Order / Thứ Tự Error Middleware**: Đặt error middleware cuối cùng, sau tất cả routes
10. **Async Error Handling / Xử Lý Lỗi Async**: Luôn sử dụng `asyncHandler` cho async route handlers
## Lỗi Thường Gặp
1. **Not Using Error Classes / Không Sử Dụng Error Classes**: Sử dụng generic `Error` thay vì specific error classes như `NotFoundError`
2. **Exposing Stack Traces / Tiết Lộ Stack Traces**: Bao gồm stack traces trong production API responses
3. **Ignoring Errors / Bỏ Qua Lỗi**: Không xử lý errors trong async operations, để chúng propagate unhandled
4. **Generic Error Messages / Error Messages Chung Chung**: Sử dụng error messages mơ hồ không có context (ví dụ: "Error occurred")
5. **Not Logging / Không Log**: Quên log errors để debug và monitor
6. **Wrong HTTP Status Codes / HTTP Status Codes Sai**: Sử dụng status codes sai cho error types (ví dụ: 500 cho validation errors)
7. **Not Using Error Middleware / Không Sử Dụng Error Middleware**: Xử lý errors manually trong mỗi controller thay vì sử dụng middleware
8. **Not Marking Operational Errors / Không Đánh Dấu Operational Errors**: Không set `isOperational` flag, khiến tất cả errors bị coi như programming errors
9. **Inconsistent Error Codes / Error Codes Không Nhất Quán**: Không sử dụng centralized `ErrorCode` enum, dẫn đến error codes không nhất quán
10. **Error Details in Production / Chi Tiết Lỗi Trong Production**: Tiết lộ error details (như database queries) trong production responses
## Xử Lý Sự Cố
### Lỗi Không Được Catch Bởi Middleware
**Problem / Vấn Đề**: Error không được catch bởi error middleware và gây unhandled promise rejection
**Solution / Giải Pháp**:
- Đảm bảo error middleware được thêm cuối cùng trong middleware chain (sau tất cả routes)
- Sử dụng `asyncHandler` wrapper cho async route handlers
- Verify error đang được throw (không chỉ log)
**Example / Ví Dụ**:
```typescript
// ❌ Bad: Error not caught
app.get('/users/:id', async (req, res) => {
const user = await userService.getUserById(req.params.id);
res.json(user);
});
// ✅ Good: Error caught by middleware
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await userService.getUserById(req.params.id);
res.json(user);
}));
```
### Error Messages Chung Chung Trong Production
**Problem / Vấn Đề**: Generic "Internal server error" được hiển thị ngay cả cho operational errors nên hiển thị messages cụ thể
**Solution / Giải Pháp**:
- Kiểm tra `isOperational` flag được set đúng trên error instances
- Verify error middleware xử lý tất cả error types đúng cách
- Đảm bảo production environment check hoạt động đúng
### Error Code Không Tìm Thấy
**Problem / Vấn Đề**: Error code không tìm thấy trong `ErrorCode` enum, gây vấn đề mapping
**Solution / Giải Pháp**:
- Thêm error code vào `ErrorCode` enum theo naming convention (`{CATEGORY}_{NUMBER}`)
- Update `ERROR_CODE_TO_STATUS` mapping để bao gồm error code mới
- Đảm bảo error code được sử dụng nhất quán trên các services
### Stack Traces Bị Tiết Lộ
**Problem / Vấn Đề**: Stack traces hiển thị trong API responses, tiết lộ internal implementation details
**Solution / Giải Pháp**:
- Đảm bảo production environment checks được đặt trong error middleware
- Verify `isProduction` flag được set đúng
- Filter stack traces và internal error details trong production responses
### Prisma Errors Không Được Xử Lý
**Problem / Vấn Đề**: Prisma errors (như unique constraint violations) không được map sang HTTP errors phù hợp
**Solution / Giải Pháp**:
- Thêm Prisma error code handling trong error middleware
- Map common Prisma error codes (P2002, P2025, etc.) sang HTTP errors phù hợp
- Tham khảo Prisma error code documentation để có mapping đầy đủ
## Tài Nguyên
- [Error Classes](../../../services/iam-service/src/errors/http-error.ts) - Base error classes và implementations
- [Error Codes](../../../services/iam-service/src/errors/error-codes.ts) - Centralized error code definitions
- [Error Middleware](../../../services/iam-service/src/middlewares/error.middleware.ts) - Global error handler implementation
- [API Design](./api-design.md) - API response formats và standards
- [Security](./security.md) - Security-related error handling
- [Testing Patterns](./testing-patterns.md) - Testing error scenarios

View File

@@ -1,505 +0,0 @@
# Kiến Trúc Hướng Sự Kiện (Event-Driven Architecture)
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.
> Các patterns kiến trúc hướng sự kiện với Apache Kafka cho GoodGo microservices. Sử dụng khi implement giao tiếp bất đồng bộ, publish/consume events, event sourcing, CQRS, hoặc tích hợp event streams với HTTP endpoints.
## Tổng Quan
Event-Driven Architecture (EDA) enables asynchronous communication between services using Apache Kafka as the message broker. This pattern decouples services, improves scalability, and enables event sourcing, CQRS, and reactive systems. GoodGo platform uses Kafka for high-throughput event streaming and integrates with Traefik to expose events via SSE/WebSocket endpoints.
Kiến trúc hướng sự kiện (EDA) cho phép giao tiếp bất đồng bộ giữa các services sử dụng Apache Kafka làm message broker. Pattern này tách biệt các services, cải thiện khả năng mở rộng, và cho phép event sourcing, CQRS, và các hệ thống reactive. Nền tảng GoodGo sử dụng Kafka cho event streaming hiệu suất cao và tích hợp với Traefik để expose events qua SSE/WebSocket endpoints.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Implement giao tiếp bất đồng bộ giữa các services
- Tách biệt services để cải thiện khả năng mở rộng
- Publish domain events cho downstream consumers
- Consume events từ các services khác
- Implement event sourcing patterns
- Implement CQRS (Command Query Responsibility Segregation)
- Expose event streams qua HTTP (SSE/WebSocket)
- Xử lý eventual consistency giữa các services
- Xây dựng reactive systems phản hồi với các thay đổi
- Tích hợp với Apache Kafka message broker
## Khái Niệm Chính
### Event-Driven vs Request-Response
**Request-Response (Synchronous / Đồng Bộ):**
- Client waits for response / Client đợi response
- Tight coupling between services / Liên kết chặt chẽ giữa các services
- Blocking operations / Các thao tác blocking
- Immediate consistency / Nhất quán ngay lập tức
- Use Traefik API Gateway for HTTP/REST / Sử dụng Traefik API Gateway cho HTTP/REST
**Event-Driven (Asynchronous / Bất Đồng Bộ):**
- Fire-and-forget publishing / Publish fire-and-forget
- Loose coupling between services / Liên kết lỏng lẻo giữa các services
- Non-blocking operations / Các thao tác non-blocking
- Eventual consistency / Nhất quán cuối cùng
- Use Kafka for message broker / Sử dụng Kafka cho message broker
### Kafka Fundamentals / Các Khái Niệm Cơ Bản về Kafka
- **Topics**: Named streams of events (e.g., `user.created`, `order.placed`) / Các luồng sự kiện được đặt tên
- **Partitions**: Physical division of topics for parallelism and scaling / Chia nhỏ vật lý của topics để song song hóa và mở rộng
- **Consumer Groups**: Groups of consumers that work together to process events / Các nhóm consumers làm việc cùng nhau để xử lý events
- **Producers**: Services that publish events to topics / Services phát hành events tới topics
- **Consumers**: Services that subscribe to topics and process events / Services đăng ký topics và xử lý events
#### Consumer Groups Architecture / Kiến Trúc Consumer Groups
The following diagram illustrates how consumer groups distribute work across partitions:
Biểu đồ sau minh họa cách consumer groups phân phối công việc qua các 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 / Mỗi partition chỉ được consume bởi một consumer trong mỗi consumer group
- Multiple consumer groups can independently consume from the same topic / Nhiều consumer groups có thể độc lập consume từ cùng một topic
- Consumers in a group automatically rebalance when members join or leave / Consumers trong một group tự động rebalance khi có thành viên tham gia hoặc rời khỏi
- More partitions enable better parallelism within a consumer group / Nhiều partitions hơn cho phép song song hóa tốt hơn trong consumer group
### Traefik Integration / Tích Hợp Traefik
Traefik serves dual purpose:
- **API Gateway**: Routes synchronous HTTP/REST requests / Định tuyến các request HTTP/REST đồng bộ
- **Event Streaming Gateway**: Routes SSE/WebSocket connections to event streaming endpoints / Định tuyến các kết nối SSE/WebSocket tới event streaming endpoints
Services publish events to Kafka, then expose SSE/WebSocket endpoints that consume from Kafka for HTTP clients.
Services publish events vào Kafka, sau đó expose SSE/WebSocket endpoints consume từ Kafka cho HTTP clients.
## Các Patterns Chính
### Event Publishing / Phát Hành Events
```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 {
/**
* EN: Publish event to Kafka topic
* VI: Phát hành event tới Kafka topic
*/
async publish<T extends BaseEvent>(
topic: string,
event: Omit<T, 'eventId' | 'timestamp' | 'source'>,
options?: { partitionKey?: string }
): Promise<void> {
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,
},
}],
});
}
}
```
**Tham Khảo**: `.cursor/skills/event-driven-architecture/SKILL.md`
#### Event Publishing Flow / Luồng Phát Hành Events
The following sequence diagram shows how events are published from a service to Kafka:
Biểu đồ sequence sau cho thấy cách events được publish từ service tới 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 / Publishing là bất đồng bộ và không blocking
- Partition key determines which partition receives the event / Partition key xác định partition nào nhận event
- Events are acknowledged by Kafka before completion / Events được Kafka xác nhận trước khi hoàn thành
- Fire-and-forget pattern prevents blocking request handlers / Pattern fire-and-forget ngăn chặn blocking request handlers
### Event Consuming / Tiêu Thụ Events
```typescript
// src/core/events/event-consumer.ts
import { kafka } from '../config/kafka.config';
export class EventConsumer {
private handlers: Map<string, EventHandler[]> = new Map();
/**
* EN: Register event handler
* VI: Đăng ký event handler
*/
on<T extends BaseEvent>(eventType: string, handler: EventHandler<T>): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
/**
* EN: Start consuming from topics
* VI: Bắt đầu consume từ topics
*/
async start(topics: string[]): Promise<void> {
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 / Pattern Outbox
The Outbox pattern ensures transactional event publishing by storing events in the database within the same transaction as the business data.
Pattern Outbox đảm bảo publish events trong transaction bằng cách lưu events trong database trong cùng transaction với business data.
```typescript
// EN: Store event in database within transaction
// VI: Lưu event vào database trong transaction
await prisma.outboxEvent.create({
data: {
eventType: 'user.created',
eventData: userData,
topic: 'user.created',
status: 'PENDING',
},
});
// EN: Separate process publishes from outbox to Kafka
// VI: Process riêng publish từ outbox tới 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' },
});
}
}
```
#### Outbox Pattern Flow / Luồng Outbox Pattern
The following sequence diagram illustrates the outbox pattern workflow:
Biểu đồ sequence sau minh họa luồng làm việc của outbox pattern:
```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 / Events được lưu trong database trong cùng transaction với business data
- A separate background process (Outbox Processor) publishes events to Kafka / Một process nền riêng (Outbox Processor) publish events tới Kafka
- Ensures at-least-once delivery guarantee / Đảm bảo giao hàng ít nhất một lần
- Prevents lost events if Kafka is temporarily unavailable / Ngăn chặn mất events nếu Kafka tạm thời không khả dụng
### SSE Endpoint / Endpoint SSE
Server-Sent Events (SSE) allows clients to receive event streams via HTTP.
Server-Sent Events (SSE) cho phép clients nhận event streams qua HTTP.
```typescript
// src/modules/events/events.controller.ts
async streamEvents(req: Request, res: Response): Promise<void> {
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();
});
}
```
## Cấu Trúc Event / Event Structure
```typescript
interface BaseEvent {
eventId: string; // EN: Unique event identifier / VI: Định danh event duy nhất
eventType: string; // EN: Event type (e.g., "user.created") / VI: Loại event
eventVersion: string; // EN: Schema version / VI: Phiên bản schema
timestamp: string; // EN: ISO 8601 timestamp / VI: Timestamp ISO 8601
source: string; // EN: Service that published the event / VI: Service phát hành event
correlationId?: string; // EN: Request correlation ID / VI: Correlation ID của request
traceId?: string; // EN: Distributed tracing ID / VI: ID phân tán tracing
data: unknown; // EN: Event payload / VI: Payload của event
}
```
## Best Practices / Thực Hành Tốt
### Event Naming Conventions / Quy Ước Đặt Tên Events
- **Event Type**: `{domain}.{action}.v{version}` (e.g., `user.created.v1`) / Loại Event: `{domain}.{action}.v{version}`
- **Topic**: `{domain}.{entity}.{action}` (e.g., `user.created`) / Topic: `{domain}.{entity}.{action}`
- Use lowercase with dots as separators / Sử dụng chữ thường với dấu chấm làm phân cách
- Keep names descriptive and consistent / Giữ tên mô tả và nhất quán
### Partition Key Selection / Lựa Chọn Partition Key
- Use entity ID for ordering guarantees (same entity → same partition) / Sử dụng entity ID để đảm bảo thứ tự
- Use correlation ID for request tracing / Sử dụng correlation ID để trace request
- Use user ID for user-scoped events / Sử dụng user ID cho events phạm vi user
- Avoid high-cardinality keys (distributes evenly) / Tránh keys có độ phân tán cao
### Event Ordering Guarantees / Đảm Bảo Thứ Tự Events
- Kafka guarantees ordering **per partition** / Kafka đảm bảo thứ tự **theo partition**
- Use partition key to ensure related events go to same partition / Sử dụng partition key để đảm bảo events liên quan cùng partition
- Events in different partitions have no ordering guarantee / Events ở các partitions khác nhau không có đảm bảo thứ tự
- Don't rely on global ordering across all events / Không phụ thuộc vào thứ tự toàn cục
### Error Handling / Xử Lý Lỗi
- Implement Dead Letter Queue (DLQ) for failed events / Implement DLQ cho events failed
- Use retry with exponential backoff / Sử dụng retry với exponential backoff
- Log all event processing failures / Ghi log tất cả lỗi xử lý events
- Monitor consumer lag and DLQ size / Giám sát consumer lag và kích thước DLQ
### Observability / Khả Năng Quan Sát
- Log all published and consumed events / Ghi log tất cả events đã publish và consume
- Track metrics: events published/consumed, processing duration, consumer lag / Theo dõi metrics: events published/consumed, thời gian xử lý, consumer lag
- Add distributed tracing to event flows / Thêm distributed tracing vào event flows
- Include correlation IDs for request tracking / Bao gồm correlation IDs để theo dõi request
## Infrastructure Setup / Thiết Lập Hạ Tầng
### Docker Compose (Local / Cục Bộ)
```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
```
**Tham Khảo**: `deployments/local/docker-compose.yml`
## Testing / Kiểm Thử
### Unit Testing / Kiểm Thử Đơn Vị
```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 / Kiểm Thử Tích Hợp
Use Kafka test containers for integration testing:
Sử dụng Kafka test containers cho integration testing:
```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 () => {
// EN: Test implementation
// VI: Implementation test
});
});
```
## Use Cases / Các Trường Hợp Sử Dụng
### User Created Event Flow / Luồng User Created Event
1. Auth Service creates user in database / Auth Service tạo user trong database
2. Publishes `user.created` event to Kafka / Publish event `user.created` tới Kafka
3. Notification Service consumes event and sends welcome email / Notification Service consume event và gửi email chào mừng
4. Analytics Service consumes event and updates metrics / Analytics Service consume event và cập nhật metrics
### Order Processing with Multiple Consumers / Xử Lý Order với Nhiều Consumers
1. Order Service publishes `order.placed` event / Order Service publish event `order.placed`
2. Payment Service processes payment / Payment Service xử lý thanh toán
3. Inventory Service reserves items / Inventory Service dự trữ items
4. Notification Service sends confirmation / Notification Service gửi xác nhận
## Skills Liên Quan
- [Resilience Patterns](./resilience-patterns.md) - Circuit breaker, retry patterns / Circuit breaker, các patterns retry
- [Error Handling Patterns](./error-handling-patterns.md) - Error handling best practices / Best practices về error handling
- [Observability & Monitoring](./observability-monitoring.md) - Logging, metrics, tracing / Logging, metrics, tracing
- [Middleware Patterns](./middleware-patterns.md) - SSE endpoint middleware / Middleware SSE endpoint
- [Project Rules](./project-rules.md) - GoodGo coding standards / Tiêu chuẩn coding GoodGo
## Tài Nguyên
- [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 / Tài liệu chính thức Kafka
- Skill Source: `.cursor/skills/event-driven-architecture/SKILL.md` - Source file đầy đủ / Full source file

View File

@@ -1,244 +0,0 @@
# Infrastructure as Code
Infrastructure as Code patterns for GoodGo platform including Terraform modules, Kubernetes operators, infrastructure testing, GitOps workflows, and multi-environment management.
> Các patterns Infrastructure as Code cho nền tảng GoodGo bao gồm Terraform modules, Kubernetes operators, infrastructure testing, GitOps workflows, và multi-environment management.
## Tổng Quan
Infrastructure as Code enables managing infrastructure through code, providing version control, reproducibility, and automation.
Infrastructure as Code cho phép quản lý infrastructure qua code, cung cấp version control, reproducibility, và automation.
## Khi Nào Sử Dụng
Use this skill when managing infrastructure, implementing GitOps, or creating reusable modules.
Sử dụng skill này khi quản lý infrastructure, implement GitOps, hoặc tạo các modules tái sử dụng.
## Infrastructure as Code Workflow / Quy Trình Infrastructure as Code
The following diagram illustrates the complete IaC workflow from code changes to infrastructure deployment.
Sơ đồ sau minh họa quy trình IaC đầy đủ từ thay đổi code đến triển khai infrastructure.
```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<br/>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 / Quy Trình GitOps
GitOps enables automated synchronization of Kubernetes manifests from Git to clusters.
GitOps cho phép đồng bộ tự động Kubernetes manifests từ Git đến 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 / Quy Trình Thực Thi Terraform
The Terraform workflow ensures safe and predictable infrastructure changes.
Quy trình Terraform đảm bảo các thay đổi infrastructure an toàn và có thể dự đoán được.
```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]
```
## Các Patterns Chính
### Terraform Modules / Terraform Modules
Terraform modules enable reusable infrastructure components across environments.
Terraform modules cho phép tái sử dụng các component infrastructure giữa các môi trường.
```hcl
# EN: Reusable module
# VI: Module tái sử dụng
module "postgresql" {
source = "../../modules/postgresql"
database_name = "goodgo"
environment = "staging"
}
```
### Module Structure / Cấu Trúc Module
The following diagram shows the typical Terraform module structure and how modules are composed.
Sơ đồ sau cho thấy cấu trúc Terraform module điển hình và cách các modules được kết hợp.
```mermaid
graph TD
A[Module: postgresql] --> B[variables.tf<br/>Input parameters]
A --> C[main.tf<br/>Resource definitions]
A --> D[outputs.tf<br/>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<br/>Environment config]
```
### GitOps with ArgoCD / GitOps với ArgoCD
GitOps tools like ArgoCD and Flux automatically sync Kubernetes manifests from Git repositories.
Các công cụ GitOps như ArgoCD và Flux tự động đồng bộ Kubernetes manifests từ Git repositories.
```yaml
# EN: Automated sync from Git
# VI: Đồng bộ tự động từ Git
spec:
source:
repoURL: https://github.com/goodgo/platform
path: deployments/production/kubernetes
syncPolicy:
automated:
prune: true
selfHeal: true
```
### Multi-Environment Management / Quản Lý Đa Môi Trường
Managing infrastructure across multiple environments requires clear separation and consistent patterns.
Quản lý infrastructure trên nhiều môi trường yêu cầu tách biệt rõ ràng và các patterns nhất quán.
```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<br/>staging config]
F --> H[terraform.tfvars<br/>production config]
G --> I[Remote State Backend<br/>staging/terraform.tfstate]
H --> J[Remote State Backend<br/>production/terraform.tfstate]
```
## Infrastructure Testing / Kiểm Thử Infrastructure
Always validate infrastructure changes before applying them.
Luôn validate các thay đổi infrastructure trước khi áp dụng.
```bash
# EN: Validate Terraform syntax
# VI: Validate cú pháp Terraform
terraform init
terraform validate
# EN: Preview changes
# VI: Xem trước các thay đổi
terraform plan -out=tfplan
# EN: Review plan before applying
# VI: Xem xét plan trước khi apply
terraform show tfplan
```
## Best Practices / Thực Hành Tốt
1. **Version Control / Kiểm Soát Phiên Bản**: Keep all infrastructure in version control / Giữ tất cả infrastructure trong version control
2. **Modules / Modules**: Create reusable Terraform modules for common components / Tạo các Terraform modules tái sử dụng cho các component phổ biến
3. **Testing / Kiểm Thử**: Test infrastructure changes before applying to production / Kiểm thử các thay đổi infrastructure trước khi áp dụng lên production
4. **GitOps / GitOps**: Use GitOps (ArgoCD/Flux) for Kubernetes deployments / Sử dụng GitOps (ArgoCD/Flux) cho Kubernetes deployments
5. **Environment Isolation / Cô Lập Môi Trường**: Separate environments completely with different state backends / Tách biệt hoàn toàn các môi trường với các state backends khác nhau
6. **State Management / Quản Lý State**: Use remote state backends (S3, GCS) with state locking / Sử dụng remote state backends (S3, GCS) với state locking
7. **Secrets / Bí Mật**: Never commit secrets - use environment variables or secrets managers / Không bao giờ commit secrets - sử dụng environment variables hoặc secrets managers
### Common Mistakes to Avoid / Các Lỗi Thường Gặp Cần Tránh
1. **Committing Secrets / Commit Secrets**: Never hardcode passwords or API keys / Không bao giờ hardcode passwords hoặc API keys
2. **Local State Only / Chỉ Dùng Local State**: Always use remote state backends for team collaboration / Luôn sử dụng remote state backends cho collaboration trong team
3. **No State Locking / Không Lock State**: Enable state locking to prevent concurrent modifications / Bật state locking để ngăn chặn các thay đổi đồng thời
4. **Direct Apply / Apply Trực Tiếp**: Always review `terraform plan` output before applying / Luôn xem xét output của `terraform plan` trước khi apply
## Tài Nguyên
- Skill Source: `.cursor/skills/infrastructure-as-code/SKILL.md`

View File

@@ -1,303 +0,0 @@
# Giao Tiếp Giữa Các Services (Inter-Service Communication)
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.
> Các patterns giao tiếp giữa services cho GoodGo microservices bao gồm gRPC, GraphQL, service-to-service authentication, lựa chọn protocol, và client patterns. Sử dụng khi implement service-to-service calls, chọn communication protocols, hoặc xây dựng service clients.
## Tổng Quan
Inter-service communication enables services to work together in a microservices architecture. GoodGo supports multiple protocols: HTTP/REST (current standard), gRPC (for high performance), and GraphQL (for flexible queries). This skill covers patterns for implementing resilient, secure, and performant inter-service communication.
Giao tiếp giữa các services cho phép các services làm việc cùng nhau trong kiến trúc microservices. GoodGo hỗ trợ nhiều protocols: HTTP/REST (chuẩn hiện tại), gRPC (cho hiệu suất cao), và GraphQL (cho queries linh hoạt). Skill này bao gồm các patterns để implement giao tiếp giữa services có khả năng phục hồi, bảo mật và hiệu suất cao.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Implement giao tiếp giữa các services
- Chọn giữa REST, gRPC, hoặc GraphQL protocols
- Setup gRPC services và clients
- Implement GraphQL services và resolvers
- Implement service-to-service authentication
- Xây dựng service clients có khả năng phục hồi với circuit breakers
## Khái Niệm Chính
### Các Lựa Chọn Protocol
**HTTP/REST:**
- Dễ đọc, dễ debug / Human-readable, easy to debug
- Tương thích browser / Browser-compatible
- Semantics HTTP chuẩn / Standard HTTP semantics
- JSON payloads
- Tốt cho external APIs / Good for external APIs
**gRPC:**
- Binary protocol (Protocol Buffers)
- Hiệu suất cao, độ trễ thấp / High performance, low latency
- Hỗ trợ streaming / Streaming support
- Strong typing với .proto files
- Dựa trên HTTP/2 / HTTP/2 based
**GraphQL:**
- Ngôn ngữ query linh hoạt / Flexible query language
- Single endpoint
- Client kiểm soát data fetching / Client-controlled data fetching
- Strong typing với schema
### Hướng Dẫn Lựa Chọn Protocol
Chọn protocol dựa trên use case, yêu cầu hiệu suất, chuyên môn team, và nhu cầu ecosystem.
Choose protocol based on use case, performance requirements, team expertise, and ecosystem needs.
#### Sơ Đồ Quyết Định Lựa Chọn Protocol / 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<br/>Required?}
CheckPerformance -->|Yes| CheckStreaming{Need Streaming?}
CheckPerformance -->|No| CheckFlexible{Need Flexible<br/>Queries?}
CheckStreaming -->|Yes| UseGRPC[Use gRPC]
CheckStreaming -->|No| CheckLowLatency{Ultra Low<br/>Latency?}
CheckLowLatency -->|Yes| UseGRPC
CheckLowLatency -->|No| UseREST
CheckFlexible -->|Yes| UseGraphQL[Use GraphQL]
CheckFlexible -->|No| UseREST
UseREST --> RESTDesc["REST: External APIs<br/>Browser clients<br/>Simple CRUD"]
UseGRPC --> GRPCDesc["gRPC: Internal services<br/>High performance<br/>Streaming support"]
UseGraphQL --> GraphQLDesc["GraphQL: Complex queries<br/>Mobile apps<br/>Flexible data"]
style UseREST fill:#e1f5ff
style UseGRPC fill:#fff4e1
style UseGraphQL fill:#e8f5e9
```
## Các Patterns Chính
### Luồng Gọi Service-to-Service / Service-to-Service Call Flow
Sơ đồ sau minh họa luồng hoàn chỉnh của service-to-service call, bao gồm authentication, interceptors, và error handling:
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<br/>(HTTP/gRPC/GraphQL)
participant Interceptor as Request<br/>Interceptor
participant Auth as Auth<br/>Middleware
participant TargetService as Target Service
participant Logger as Logger
participant Metrics as Metrics
ClientService->>ClientLib: Make request
ClientLib->>Interceptor: Add correlation ID<br/>Add service auth header<br/>Add request ID
Interceptor->>Logger: Log request start
Interceptor->>Metrics: Track request start
Interceptor->>TargetService: HTTP/gRPC/GraphQL Request<br/>(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<br/>(with correlation ID)
ClientLib->>Metrics: Track success<br/>(duration, status)
ClientLib-->>ClientService: Response Data
end
```
### HTTP/REST Service Client
```typescript
// EN: Service client với circuit breaker
// VI: Service client với circuit breaker
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,
});
// EN: Usage
// VI: Sử dụng
await notificationClient.post('/api/v1/notifications', {
userId,
message,
});
```
### gRPC Service
```typescript
// EN: gRPC server
// VI: gRPC server
import { UserGrpcServer } from './user.grpc.service';
const grpcServer = new UserGrpcServer(userService);
grpcServer.start(50051);
// EN: gRPC client
// VI: 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
// EN: GraphQL client
// VI: 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
Luồng authentication đảm bảo giao tiếp an toàn giữa các services:
The authentication flow ensures secure communication between services:
```mermaid
sequenceDiagram
participant ClientService as Client Service
participant ServiceClient as Service Client
participant Env as Environment<br/>Variables
participant TargetService as Target Service
participant AuthMiddleware as Auth<br/>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<br/>header with API key
ServiceClient->>TargetService: HTTP Request<br/>(x-service-auth: token)
TargetService->>AuthMiddleware: Extract x-service-auth
AuthMiddleware->>AuthMiddleware: Compare with<br/>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<br/>(INVALID_SERVICE_AUTH)
ServiceClient->>ServiceClient: Throw ServiceError
ServiceClient-->>ClientService: Error Response
end
```
#### Implementation / Triển Khai
```typescript
// EN: Internal auth middleware
// VI: Internal auth middleware
import { internalAuthMiddleware } from '../../middlewares/internal-auth.middleware';
router.use('/internal', internalAuthMiddleware);
// EN: Client tự động thêm auth header
// VI: Client automatically adds auth header
const client = new ServiceClient({
baseURL: 'http://service:5000',
serviceName: 'service',
});
// X-Service-Auth header được thêm tự động
```
## Best Practices / Thực Hành Tốt
### Protocol Selection / Lựa Chọn Protocol
- **REST**: External APIs, browser clients, simple CRUD / APIs ngoài, browser clients, CRUD đơn giản
- **gRPC**: Internal services, high performance, streaming / Services nội bộ, hiệu suất cao, streaming
- **GraphQL**: Complex queries, mobile apps, flexible data / Queries phức tạp, mobile apps, data linh hoạt
### Performance / Hiệu Suất
- Use connection pooling / Sử dụng connection pooling
- Enable HTTP keep-alive / Bật HTTP keep-alive
- Set appropriate timeouts / Thiết lập timeout phù hợp
- Implement circuit breakers / Implement circuit breakers
### Security / Bảo Mật
- Always authenticate internal calls / Luôn xác thực internal calls
- Use TLS/mTLS / Sử dụng TLS/mTLS
- Store secrets securely / Lưu secrets an toàn
- Implement rate limiting / Implement rate limiting
### Observability / Khả Năng Quan Sát
- Log with correlation IDs / Ghi log với correlation IDs
- Track metrics (duration, success rate) / Theo dõi metrics
- Add distributed tracing / Thêm distributed tracing
- Monitor service health / Giám sát sức khỏe service
## Testing / Kiểm Thử
```typescript
// EN: Mock service client
// VI: Mock service client
const mockClient = createMockServiceClient();
mockClient.get.mockResolvedValue({ id: '123' });
```
## Skills Liên Quan
- [Resilience Patterns](./resilience-patterns.md) - Circuit breaker, retry patterns / Circuit breaker, các patterns retry
- [Security](./security.md) - Authentication, authorization / Authentication, authorization
- [API Design](./api-design.md) - RESTful API patterns / RESTful API patterns
- [Project Rules](./project-rules.md) - GoodGo coding standards / Tiêu chuẩn coding GoodGo
## Tài Nguyên
- [gRPC Documentation](https://grpc.io/docs/) - Tài liệu gRPC chính thức
- [GraphQL Documentation](https://graphql.org/learn/) - Tài liệu GraphQL
- [Protocol Buffers](https://developers.google.com/protocol-buffers) - Hướng dẫn Protocol Buffers
- Skill Source: `.cursor/skills/inter-service-communication/SKILL.md` - Source file đầy đủ

View File

@@ -1,344 +0,0 @@
# Quy Trình Phát Triển Microservices (Microservices Development Process)
Standard development process for creating and maintaining microservices in GoodGo platform. Use when creating new services, migrating services, refactoring services, or planning service implementations.
> Quy trình phát triển chuẩn để tạo và duy trì microservices trong nền tảng GoodGo. Sử dụng khi tạo services mới, migrate services, refactor services, hoặc lập kế hoạch implement services.
## Tổng Quan
The microservices development process provides a structured 8-phase approach to building services, ensuring comprehensive coverage from planning to deployment.
Quy trình phát triển microservices cung cấp cách tiếp cận có cấu trúc 8 giai đoạn để xây dựng services, đảm bảo bao phủ toàn diện từ lập kế hoạch đến triển khai.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Tạo microservice mới từ đầu
- Migrate hoặc refactor service hiện có
- Lập kế hoạch implement service với nhiều giai đoạn
- Đảm bảo bao phủ toàn diện tất cả các khía cạnh phát triển
## Quy Trình 8 Giai Đoạn
The microservices development process follows these phases:
Quy trình phát triển microservices theo các giai đoạn sau:
1. **Planning & Impact Analysis** - Define scope, impact, dependencies / Định nghĩa scope, impact, dependencies
2. **Foundation Setup** - Service structure, configs, infrastructure / Cấu trúc service, configs, infrastructure
3. **Core Implementation** - Business logic, APIs, data layer / Business logic, APIs, data layer
4. **Integration** - Routes, middleware, external services / Routes, middleware, external services
5. **Testing** - Unit, integration, E2E tests / Unit, integration, E2E tests
6. **Documentation** - API docs, README, guides / API docs, README, guides
7. **Cleanup & Verification** - Remove temporary files, verify completeness / Xóa file tạm, xác minh hoàn chỉnh
8. **Deployment** - Staging deployment, production deployment / Staging deployment, production deployment
### Biểu Đồ Luồng Quy Trình / Process Flow Diagram
This diagram shows the complete 8-phase development process with decision points and feedback loops.
Biểu đồ này hiển thị quy trình phát triển 8 giai đoạn đầy đủ với các điểm quyết định và vòng lặp phản hồi.
```mermaid
graph TD
Start([Start: New Service Requirements]) --> Phase1[Phase 1: Planning & Impact Analysis]
Phase1 --> ImpactCheck{Impact Analysis<br/>Complete?}
ImpactCheck -->|No| Phase1
ImpactCheck -->|Yes| Phase2[Phase 2: Foundation Setup]
Phase2 --> FoundationCheck{Service Starts<br/>& Health Check Passes?}
FoundationCheck -->|No| Phase2
FoundationCheck -->|Yes| Phase3[Phase 3: Core Implementation]
Phase3 --> ImplementationCheck{Business Logic<br/>Implemented?}
ImplementationCheck -->|No| Phase3
ImplementationCheck -->|Yes| Phase4[Phase 4: Integration]
Phase4 --> IntegrationCheck{Routes & Middleware<br/>Working?}
IntegrationCheck -->|No| Phase4
IntegrationCheck -->|Yes| Phase5[Phase 5: Testing]
Phase5 --> TestCheck{Tests Pass<br/>& Coverage Met?}
TestCheck -->|No| Phase5
TestCheck -->|Yes| Phase6[Phase 6: Documentation]
Phase6 --> DocCheck{Docs<br/>Complete?}
DocCheck -->|No| Phase6
DocCheck -->|Yes| Phase7[Phase 7: Cleanup & Verification]
Phase7 --> VerificationCheck{All Checks<br/>Pass?}
VerificationCheck -->|No| Phase7
VerificationCheck -->|Yes| Phase8[Phase 8: Deployment]
Phase8 --> DeployCheck{Staging<br/>Deployed?}
DeployCheck -->|No| Phase8
DeployCheck -->|Yes| Production{Deploy to<br/>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
```
### Luồng Chi Tiết Các Giai Đoạn / Detailed Phase Flow
This diagram breaks down the tasks within each phase and shows the sequential flow between phases.
Biểu đồ này phân tích các nhiệm vụ trong từng giai đoạn và hiển thị luồng tuần tự giữa các giai đoạn.
```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
```
## Các Giai Đoạn Chính
### Phase 1: Planning & Impact Analysis / Giai Đoạn 1: Lập Kế Hoạch & Phân Tích Tác Động
**Scope Definition / Định Nghĩa Scope:**
- Service Purpose: What business capability does it provide? / Mục đích service: Cung cấp khả năng kinh doanh gì?
- API Surface: What endpoints are needed? / API Surface: Cần endpoints nào?
- Data Models: What data structures are required? / Data Models: Cần cấu trúc dữ liệu nào?
- Dependencies: What services/packages does it depend on? / Dependencies: Phụ thuộc vào services/packages nào?
**Impact Analysis Checklist / Checklist Phân Tích Tác Động:**
- [ ] Service directory: `services/service-name/`
- [ ] Prisma schema: `services/service-name/prisma/schema.prisma`
- [ ] Dockerfile: `services/service-name/Dockerfile`
- [ ] Update `deployments/local/docker-compose.yml`
- [ ] Update `infra/traefik/dynamic/routes.yml`
### Phase 2: Foundation Setup / Giai Đoạn 2: Thiết Lập Nền Tảng
**Service Structure / Cấu Trúc Service:**
```bash
# EN: Copy template
# VI: Copy template
cp -r services/_template services/new-service-name
cd services/new-service-name
```
**Acceptance Criteria / Tiêu Chí Chấp Nhận:**
- [ ] Service starts: `pnpm dev` (health check passes) / Service khởi động: `pnpm dev` (health check pass)
- [ ] Docker build succeeds / Docker build thành công
- [ ] Service accessible via Traefik / Service có thể truy cập qua Traefik
### Phase 3: Core Implementation / Giai Đoạn 3: Implementation Cốt Lõi
**Implementation Order / Thứ Tự Implementation:**
1. **DTOs** - Zod schemas for validation / Zod schemas cho validation
2. **Repository** - Prisma-based data access / Data access dựa trên Prisma
3. **Service** - Business logic / Business logic
4. **Controller** - HTTP request handling / Xử lý HTTP requests
5. **Module** - Wire up components / Kết nối các components
**Luồng Implementation / Implementation Flow:**
This diagram shows the step-by-step implementation order for each feature module within Phase 3.
Biểu đồ này hiển thị thứ tự implementation từng bước cho mỗi feature module trong Giai đoạn 3.
```mermaid
graph TD
Start[Start Implementation] --> DTOs[1. Create DTOs<br/>Zod Validation Schemas]
DTOs --> Repo[2. Create Repository<br/>Prisma Data Access]
Repo --> Service[3. Create Service<br/>Business Logic]
Service --> Controller[4. Create Controller<br/>HTTP Handlers]
Controller --> Module[5. Create Module<br/>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
```
### Phase 4: Integration / Giai Đoạn 4: Tích Hợp
**Required Middlewares / Middlewares Bắt Buộc:**
1. Correlation middleware
2. Logging middleware
3. Metrics middleware
4. CORS middleware
5. Rate limiting middleware
6. Authentication middleware (if needed) / (nếu cần)
7. Error middleware (always last) / (luôn cuối cùng)
### Phase 5: Testing / Giai Đoạn 5: Kiểm Thử
**Test Coverage Targets / Mục Tiêu Test Coverage:**
- Minimum: 70% coverage / Tối thiểu: 70% coverage
- Critical paths: 90%+ coverage / Đường dẫn quan trọng: 90%+ coverage
- Repositories: 80%+ coverage
- Services: 80%+ coverage
**Testing Checklist / Checklist Testing:**
- [ ] Unit tests pass / Unit tests pass
- [ ] Integration tests pass / Integration tests pass
- [ ] E2E tests pass / E2E tests pass
- [ ] Coverage meets thresholds / Coverage đạt ngưỡng
### Phase 6: Documentation / Giai Đoạn 6: Tài Liệu
**Required Documentation / Tài Liệu Bắt Buộc:**
- Service README (bilingual EN/VI) / README service (song ngữ EN/VI)
- Swagger/OpenAPI spec / Swagger/OpenAPI spec
- Architecture docs (if complex) / Tài liệu kiến trúc (nếu phức tạp)
### Phase 7: Cleanup & Verification / Giai Đoạn 7: Dọn Dẹp & Xác Minh
**Luồng Quy Trình Xác Minh / 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.
Biểu đồ này minh họa quy trình dọn dẹp và xác minh cho Giai đoạn 7, bao gồm điểm quyết định cho migrations và các bước xác minh toàn diện.
```mermaid
graph TD
Start[Start Cleanup] --> Remove[Remove Temporary Files]
Remove --> Update{Is Migration?}
Update -->|Yes| RefUpdate[Update References<br/>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
```
**Verification Steps / Các Bước Xác Minh:**
- [ ] TypeScript check: `pnpm typecheck` / Kiểm tra TypeScript
- [ ] Lint check: `pnpm lint` / Kiểm tra lint
- [ ] Test check: `pnpm test` / Kiểm tra test
- [ ] Build check: `pnpm build` / Kiểm tra build
- [ ] Docker build succeeds / Docker build thành công
### Phase 8: Deployment / Giai Đoạn 8: Triển Khai
**Staging Deployment / Triển Khai Staging:**
```bash
# EN: Build and push Docker image
# VI: Build và push Docker image
docker build -t goodgo/service-name:latest .
docker push goodgo/service-name:latest
# EN: Apply Kubernetes configs
# VI: Áp dụng configs Kubernetes
kubectl apply -f deployments/staging/kubernetes/service-name.yaml
```
## Best Practices / Thực Hành Tốt
1. **Always Plan First**: Complete impact analysis before coding / Luôn lập kế hoạch trước: Hoàn thành phân tích tác động trước khi code
2. **Follow Phases**: Don't skip verification steps / Tuân theo các giai đoạn: Không bỏ qua các bước xác minh
3. **Test Early**: Write tests alongside implementation / Test sớm: Viết tests cùng với implementation
4. **Document as You Go**: Don't leave documentation for the end / Tài liệu khi làm: Không để tài liệu đến cuối
5. **Verify Comprehensively**: Use checklists to ensure nothing is missed / Xác minh toàn diện: Sử dụng checklists để đảm bảo không bỏ sót
## Common Pitfalls to Avoid / Các Lỗi Thường Gặp Cần Tránh
1. **Skipping Impact Analysis**: Leads to missing updates / Bỏ qua phân tích tác động: Dẫn đến thiếu cập nhật
2. **No Verification Steps**: Misses broken references / Không có bước xác minh: Bỏ sót references bị hỏng
3. **Incomplete Testing**: Missing edge cases / Testing không đầy đủ: Thiếu edge cases
4. **Poor Documentation**: Makes maintenance difficult / Tài liệu kém: Làm khó bảo trì
## Skills Liên Quan
- [Project Rules](./project-rules.md) - Architecture and conventions / Kiến trúc và conventions
- [API Design](./api-design.md) - API design patterns / Các patterns thiết kế API
- [Testing Patterns](./testing-patterns.md) - Testing best practices / Best practices về testing
- [Documentation](./documentation.md) - Documentation guidelines / Hướng dẫn tài liệu
- [Database Prisma](./database-prisma.md) - Prisma patterns / Các patterns Prisma
## Tài Nguyên
- Service Template: `services/_template/` - Template service
- Skill Source: `.cursor/skills/microservices-development-process/SKILL.md` - Source file đầy đủ

View File

@@ -1,462 +0,0 @@
# Các Pattern Middleware
Express middleware patterns and best practices for GoodGo microservices including middleware chain ordering, custom middleware creation, authentication, validation, logging, and error handling middleware.
> Các patterns và best practices về Express middleware cho GoodGo microservices bao gồm thứ tự middleware chain, tạo custom middleware, authentication, validation, logging, và error handling middleware.
## Tổng Quan
Middleware functions are the fundamental building blocks of Express.js applications. They provide a way to execute code, make changes to requests and responses, end the request-response cycle, and call the next middleware. This guide covers middleware patterns, ordering, common middleware implementations, and best practices for GoodGo microservices.
Middleware functions là các building blocks cơ bản của ứng dụng Express.js. Chúng cung cấp cách để thực thi code, thay đổi requests và responses, kết thúc request-response cycle, và gọi middleware tiếp theo. Hướng dẫn này bao gồm các middleware patterns, thứ tự, common middleware implementations, và best practices cho GoodGo microservices.
## Khi Nào Sử Dụng
Use middleware patterns 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
Sử dụng middleware patterns khi:
- Tạo custom Express middleware
- Tổ chức middleware chains và thứ tự
- Implement authentication/authorization middleware
- Tạo request/response transformation middleware
- Xử lý cross-cutting concerns (logging, metrics, validation)
- Implement async middleware patterns
- Testing middleware implementations
## Khái Niệm Chính
### Chữ Ký Function Middleware
Express middleware functions have a specific signature that receives request, response, and next function.
Express middleware functions có chữ ký cụ thể nhận request, response, và next function.
```typescript
(req: Request, res: Response, next: NextFunction) => void | Promise<void>
```
### Các Loại Middleware
1. **Application-level / Cấp Ứng Dụng**: Applied to all routes (`app.use()`)
2. **Router-level / Cấp Router**: Applied to specific routes (`router.use()`)
3. **Route-level / Cấp Route**: Applied to specific route handlers
### Thứ Tự Thực Thi Middleware
Middleware order is critical! Execution flows top-to-bottom in the order they are registered.
Thứ tự middleware rất quan trọng! Thực thi theo trình tự từ trên xuống dưới theo thứ tự đăng ký.
```
Request → Middleware 1 → Middleware 2 → ... → Route Handler → Response
```
Biểu đồ sau minh họa luồng middleware chain hoàn chỉnh trong GoodGo services:
The following diagram illustrates the complete middleware chain flow in GoodGo services:
```mermaid
flowchart TD
Start([HTTP Request]) --> Security["Security Middleware<br/>Helmet, CORS"]
Security --> RateLimit["Rate Limiting<br/>Middleware"]
RateLimit --> Correlation["Correlation ID<br/>Middleware"]
Correlation --> BodyParsing["Body Parsing<br/>JSON, URLEncoded, Cookies"]
BodyParsing --> Logging["Request Logging<br/>Middleware"]
Logging --> Metrics["Metrics Collection<br/>Middleware"]
Metrics --> Routes["Route Handlers"]
Routes -->|Success| Response([HTTP Response])
Routes -->|Error| ErrorHandler["Error Handler<br/>Middleware"]
Routes -->|Not Found| NotFound["Not Found<br/>Handler"]
ErrorHandler --> Response
NotFound --> Response
```
## Patterns
### Thứ Tự Middleware Chain
Standard middleware order in GoodGo services ensures proper request processing flow.
Thứ tự middleware chuẩn trong GoodGo services đảm bảo luồng xử lý request đúng cách.
```typescript
// Bảo mật
app.use(helmet());
app.use(cors({ ... }));
// Giới hạn tốc độ
app.use('/api', rateLimitMiddleware);
// Correlation ID (sớm cho tracing)
app.use(correlationMiddleware());
// Phân tích body
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
// Ghi log request
app.use(loggerMiddleware);
// Metrics
app.use(metricsMiddleware);
// Routes
app.use(createRouter());
// Xử lý lỗi (LUÔN CUỐI CÙNG)
app.use(notFoundHandler);
app.use(errorHandler);
```
**Tham Khảo**: [`services/iam-service/src/main.ts`](../../../services/iam-service/src/main.ts)
### Pattern Correlation Middleware
Adds correlation ID for request tracing across services.
Thêm correlation ID để tracing requests qua các services.
```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();
};
};
```
**Tham Khảo**: [`services/iam-service/src/middlewares/correlation.middleware.ts`](../../../services/iam-service/src/middlewares/correlation.middleware.ts)
### Pattern Authentication Middleware
Verifies JWT tokens and attaches user information to request.
Xác minh JWT tokens và gắn thông tin người dùng vào 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' });
}
};
};
```
Biểu đồ tuần tự sau minh họa luồng authentication middleware:
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
```
**Tham Khảo**: [`services/iam-service/src/middlewares/auth.middleware.ts`](../../../services/iam-service/src/middlewares/auth.middleware.ts)
### Pattern Validation Middleware
Validates request data using Zod schemas.
Validate dữ liệu request sử dụng Zod schemas.
```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);
}
};
};
```
Biểu đồ tuần tự sau minh họa luồng validation middleware:
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
```
**Tham Khảo**: [`services/iam-service/src/middlewares/validation.middleware.ts`](../../../services/iam-service/src/middlewares/validation.middleware.ts)
### Pattern Async Middleware
Handle async operations properly to catch promise rejections.
Xử lý async operations đúng cách để catch promise rejections.
```typescript
export const asyncHandler = (fn: Function) => {
return (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
// Usage
app.get('/users', asyncHandler(async (req, res) => {
const users = await userService.findAll();
res.json({ success: true, data: users });
}));
```
Biểu đồ tuần tự sau minh họa xử lý lỗi async middleware:
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
```
### Pattern Logging Middleware
Log request details with timing and correlation tracking.
Ghi log chi tiết request với timing và correlation tracking.
```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();
};
```
Biểu đồ tuần tự sau minh họa cách logging middleware theo dõi vòng đời request:
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', {<br/>method, url, statusCode,<br/>duration, correlationId})
Logger->>Logger: Write structured log entry
Response->>Client: HTTP Response
```
### Chuyển Đổi Request/Response
Transform request or response data in middleware.
Chuyển đổi dữ liệu request hoặc response trong middleware.
```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();
};
};
```
Biểu đồ tuần tự sau minh họa cách middleware chuyển đổi request và response hoạt động:
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)
```
## Thực Hành Tốt Nhất
1. **Order Matters / Thứ Tự Quan Trọng**: Đặt middleware đúng thứ tự (security → correlation → parsing → logging → routes → errors)
2. **Error Handling / Xử Lý Lỗi**: Luôn xử lý errors và gọi `next(error)` cho error middleware
3. **Async Support / Hỗ Trợ Async**: Wrap async middleware đúng cách để catch promise rejections
4. **Early Returns / Return Sớm**: Sử dụng early returns cho validation failures (không gọi `next()`)
5. **Request Extension / Mở Rộng Request**: Sử dụng TypeScript declaration merging để mở rộng Request type
6. **Conditional Logic / Logic Có Điều Kiện**: Sử dụng middleware factories cho conditional middleware
7. **Reusability / Tái Sử Dụng**: Tạo reusable middleware functions
8. **Performance / Hiệu Suất**: Giữ middleware lightweight, tránh heavy operations
## Lỗi Thường Gặp
1. **Wrong Order / Thứ Tự Sai**: Đặt middleware sai thứ tự (ví dụ: error handler trước routes)
2. **Not Calling Next / Không Gọi Next**: Quên gọi `next()` hoặc `next(error)`
3. **Async Errors / Lỗi Async**: Không xử lý promise rejections trong async middleware
4. **Early Return Issues / Vấn Đề Return Sớm**: Gọi `next()` sau khi đã send response
5. **Type Safety / An Toàn Kiểu**: Không mở rộng Express Request type đúng cách
6. **Performance / Hiệu Suất**: Thực hiện heavy operations trong middleware
## Xử Lý Sự Cố
### Middleware Không Thực Thi
**Problem / Vấn Đề**: Middleware không được gọi
**Solution / Giải Pháp**:
- Kiểm tra thứ tự middleware, đảm bảo nó được thêm trước routes
- Verify `next()` được gọi
- Check middleware registration order
### Lỗi Async Không Được Bắt
**Problem / Vấn Đề**: Unhandled promise rejections trong async middleware
**Solution / Giải Pháp**:
- Sử dụng `asyncHandler` wrapper
- Hoặc wrap async code trong try-catch với `next(error)`
**Example / Ví Dụ**:
```typescript
// ❌ Bad: Unhandled promise rejection
app.get('/users', async (req, res) => {
const users = await userService.findAll(); // Error not caught!
res.json(users);
});
// ✅ Good: Proper error handling
app.get('/users', asyncHandler(async (req, res) => {
const users = await userService.findAll();
res.json(users);
}));
```
## Tài Nguyên
- [Correlation Middleware](../../../services/iam-service/src/middlewares/correlation.middleware.ts) - Correlation ID implementation
- [Auth Middleware](../../../services/iam-service/src/middlewares/auth.middleware.ts) - Authentication middleware
- [Validation Middleware](../../../services/iam-service/src/middlewares/validation.middleware.ts) - Request validation
- [Error Middleware](../../../services/iam-service/src/middlewares/error.middleware.ts) - Error handling
- [Metrics Middleware](../../../services/iam-service/src/middlewares/metrics.middleware.ts) - Metrics collection
- [Error Handling](./error-handling-patterns.md) - Error middleware patterns

View File

@@ -1,723 +0,0 @@
# Khả Năng Quan Sát & Giám Sát
Observability and monitoring patterns for GoodGo microservices. Use when adding metrics, implementing logging, setting up tracing, creating health checks, or debugging production issues.
> Các pattern observability và monitoring cho microservices GoodGo. Sử dụng khi thêm metrics, triển khai logging, thiết lập tracing, tạo health checks, hoặc debug các vấn đề production.
## Tổng Quan
This skill covers the three pillars of observability (logs, metrics, traces) and how to implement them in GoodGo microservices. It includes structured logging, Prometheus metrics, distributed tracing with OpenTelemetry, health checks, and error tracking.
Skill này bao gồm ba trụ cột của observability (logs, metrics, traces) và cách triển khai chúng trong microservices GoodGo. Nó bao gồm structured logging, Prometheus metrics, distributed tracing với OpenTelemetry, health checks, và error tracking.
## Khi Nào Sử Dụng
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
Sử dụng skill này khi:
- Thiết lập hạ tầng logging
- Triển khai thu thập metrics
- Thêm distributed tracing
- Tạo health check endpoints
- Thiết lập monitoring dashboards
- Debug các vấn đề production
- Triển khai alerting rules
- Phân tích performance bottlenecks
## Khái Niệm Chính
### Ba Trụ Cột Của Observability
1. **Logs**: Event records for debugging and auditing
2. **Metrics**: Numerical measurements over time (counters, gauges, histograms)
3. **Traces**: Request flow across services (distributed tracing)
**VI**:
1. **Logs**: Bản ghi sự kiện để debug và audit
2. **Metrics**: Đo lường số học theo thời gian (counters, gauges, histograms)
3. **Traces**: Luồng request qua các services (distributed tracing)
### Công Nghệ
- **Logging**: `@goodgo/logger` (Pino-based structured logging)
- **Metrics**: Prometheus + Grafana
- **Tracing**: OpenTelemetry + Jaeger (`@goodgo/tracing`)
- **Correlation IDs**: Request tracking across services
**VI**:
- **Logging**: `@goodgo/logger` (structured logging dựa trên Pino)
- **Metrics**: Prometheus + Grafana
- **Tracing**: OpenTelemetry + Jaeger (`@goodgo/tracing`)
- **Correlation IDs**: Theo dõi request qua các services
### Kiến Trúc Observability Stack
Observability stack bao gồm ba trụ cột hoạt động cùng nhau để cung cấp khả năng quan sát toàn diện vào hành vi hệ thống:
```mermaid
graph TB
subgraph "Application Layer"
App[Microservice]
end
subgraph "Three Pillars of Observability"
Logs[Logs<br/>Winston/Pino]
Metrics[Metrics<br/>Prometheus]
Traces[Traces<br/>OpenTelemetry]
end
subgraph "Aggregation & Storage"
Loki[Loki<br/>Log Aggregation]
Prom[Prometheus<br/>Metrics Storage]
Jaeger[Jaeger<br/>Trace Storage]
end
subgraph "Visualization & Alerting"
Grafana[Grafana<br/>Dashboards]
AlertManager[AlertManager<br/>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
```
## Các Pattern Thường Dùng
### Logging Có Cấu Trúc
Use structured logging with correlation IDs for request tracking.
Sử dụng structured logging với correlation IDs để theo dõi request.
**Ví dụ từ codebase**: [`services/iam-service/src/middlewares/logger.middleware.ts`](../../../services/iam-service/src/middlewares/logger.middleware.ts)
```typescript
import { Request, Response, NextFunction } from 'express';
import { logger } from '@goodgo/logger';
import { getCorrelationId, getRequestId } from './correlation.middleware';
export const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
// Bỏ qua logging chi tiết cho health checks và metrics
if (req.path.startsWith('/health') || req.path.startsWith('/metrics')) {
return next();
}
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const correlationId = getCorrelationId(req);
const requestId = getRequestId(req);
logger.info('Request processed / Request đã xử lý', {
correlationId,
requestId,
method: req.method,
path: req.path,
query: req.query,
statusCode: res.statusCode,
duration: `${duration}ms`,
contentLength: res.get('Content-Length') || 0,
userAgent: req.get('User-Agent'),
ip: req.ip,
userId: (req as any).user?.userId,
});
});
next();
};
```
### Luồng Logging
Luồng logging cho thấy cách các request được log với correlation IDs và chảy qua hệ thống:
```mermaid
sequenceDiagram
participant Client
participant Service as Microservice
participant Logger as Winston/Pino Logger
participant Aggregator as Log Aggregator<br/>(Loki)
participant Dashboard as Grafana Dashboard
Client->>Service: HTTP Request<br/>(with x-correlation-id)
Service->>Service: Generate/Extract<br/>Correlation ID
Service->>Logger: Log Request Start<br/>{correlationId, method, url}
Service->>Service: Process Request
Service->>Logger: Log Business Event<br/>{correlationId, event, data}
Service->>Client: HTTP Response<br/>(with x-correlation-id)
Service->>Logger: Log Request End<br/>{correlationId, status, duration}
Logger->>Aggregator: Send Structured Logs<br/>(JSON format)
Aggregator->>Dashboard: Index & Store Logs
Dashboard->>Dashboard: Query by correlationId<br/>to trace request flow
```
### Correlation IDs
Use correlation IDs to track requests across services.
Sử dụng correlation IDs để theo dõi request qua các services.
**Ví dụ từ codebase**: [`services/iam-service/src/middlewares/correlation.middleware.ts`](../../../services/iam-service/src/middlewares/correlation.middleware.ts)
```typescript
import { Request, Response, NextFunction } from 'express';
import { randomUUID } from 'crypto';
import { logger } from '@goodgo/logger';
export const CORRELATION_ID_HEADER = 'x-correlation-id';
export const REQUEST_ID_HEADER = 'x-request-id';
export const correlationMiddleware = (
options: {
headerName?: string;
generateId?: () => string;
skipPaths?: string[];
} = {}
) => {
const {
headerName = CORRELATION_ID_HEADER,
generateId = randomUUID,
skipPaths = ['/health', '/metrics', '/favicon.ico'],
} = options;
return (req: Request, res: Response, next: NextFunction) => {
// Lấy correlation ID từ header hoặc tạo mới
const correlationId = req.headers[headerName.toLowerCase()] as string || generateId();
const requestId = generateId();
// Gắn vào request object
req.correlationId = correlationId;
req.requestId = requestId;
// Thêm vào response headers
res.setHeader(headerName, correlationId);
res.setHeader(REQUEST_ID_HEADER, requestId);
// Log request start
logger.info('Request started / Request bắt đầu', {
correlationId,
requestId,
method: req.method,
url: req.url,
userAgent: req.get('User-Agent'),
ip: req.ip,
});
next();
};
};
```
### Thu Thập Metrics
Expose Prometheus metrics for monitoring and alerting.
Expose Prometheus metrics để monitoring và alerting.
**Ví dụ từ codebase**: [`services/iam-service/src/middlewares/metrics.middleware.ts`](../../../services/iam-service/src/middlewares/metrics.middleware.ts)
```typescript
import { Request, Response, NextFunction } from 'express';
import client from 'prom-client';
import { getCorrelationId } from './correlation.middleware';
// Tạo Registry để đăng ký các metrics
const register = client.register;
// Thu thập các metrics mặc định
client.collectDefaultMetrics({ register });
// Tạo histogram cho thời lượng request HTTP
const httpRequestDurationSeconds = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code', 'correlation_id'],
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2, 5, 10],
});
// Tạo counter cho tổng số request HTTP
const httpRequestsTotal = new client.Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'route', 'status_code'],
});
// Tạo gauge cho active requests
const activeRequests = new client.Gauge({
name: 'http_active_requests',
help: 'Number of active HTTP requests',
});
// Tạo counter cho lỗi HTTP request
const httpRequestErrors = new client.Counter({
name: 'http_request_errors_total',
help: 'Total number of HTTP request errors',
labelNames: ['method', 'route', 'error_type'],
});
export const metricsMiddleware = (req: Request, res: Response, next: NextFunction) => {
// Tăng active requests
activeRequests.inc();
// Bắt đầu bấm giờ
const start = process.hrtime.bigint();
res.on('finish', () => {
// Giảm active requests
activeRequests.dec();
// Tính toán thời lượng
const end = process.hrtime.bigint();
const durationInSeconds = Number(end - start) / 1e9;
// Chuẩn hóa path để tránh high cardinality
const route = normalizeRoutePath(req);
const correlationId = getCorrelationId(req) || 'unknown';
// Ghi nhận thời lượng
httpRequestDurationSeconds
.labels(req.method, route, res.statusCode.toString(), correlationId)
.observe(durationInSeconds);
// Tăng bộ đếm request
httpRequestsTotal
.labels(req.method, route, res.statusCode.toString())
.inc();
// Theo dõi lỗi
if (res.statusCode >= 400) {
const errorType = res.statusCode >= 500 ? 'server_error' : 'client_error';
httpRequestErrors
.labels(req.method, route, errorType)
.inc();
}
});
next();
};
// Chuẩn hóa route path để ngăn high cardinality metrics
function normalizeRoutePath(req: Request): string {
if (req.route && req.route.path) {
return req.route.path;
}
let path = req.path;
// Thay thế UUIDs và numeric IDs bằng placeholders
path = path.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, ':uuid');
path = path.replace(/\d+/g, ':id');
return path;
}
```
### Luồng Thu Thập Metrics
Metrics được thu thập từ services và expose cho Prometheus để monitoring và alerting:
```mermaid
graph LR
subgraph "Service Instance"
App[Application]
Middleware[Metrics Middleware]
Registry[Prometheus Registry]
Endpoint[/metrics Endpoint]
end
subgraph "Metrics Types"
Counter[Counter<br/>http_requests_total]
Gauge[Gauge<br/>active_users]
Histogram[Histogram<br/>request_duration]
end
subgraph "Collection"
Prometheus[Prometheus<br/>Scraper]
end
subgraph "Storage & Query"
PromDB[(Prometheus<br/>Time Series DB)]
end
subgraph "Visualization"
Grafana[Grafana<br/>Dashboards]
Alerts[AlertManager<br/>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
Use OpenTelemetry for distributed tracing across services.
Sử dụng OpenTelemetry cho distributed tracing qua các services.
**Ví dụ từ codebase**: [`packages/tracing/src/index.ts`](../../../packages/tracing/src/index.ts)
```typescript
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
export interface TracingConfig {
serviceName: string;
jaegerEndpoint?: string;
enabled?: boolean;
}
export const initTracing = (config: TracingConfig): NodeSDK | null => {
if (config.enabled === false) {
return null;
}
// Tạo Jaeger exporter nếu endpoint được cung cấp
const jaegerExporter = config.jaegerEndpoint
? new JaegerExporter({
endpoint: config.jaegerEndpoint,
})
: undefined;
// Khởi tạo OpenTelemetry NodeSDK với auto-instrumentations
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: config.serviceName,
}),
traceExporter: jaegerExporter,
instrumentations: [getNodeAutoInstrumentations()],
});
// Khởi động tracing SDK
sdk.start();
return sdk;
};
```
**Cách sử dụng trong service**:
```typescript
// services/iam-service/src/main.ts
import { initTracing } from '@goodgo/tracing';
// Khởi tạo tracing
if (process.env.TRACING_ENABLED === 'true') {
initTracing({
serviceName: process.env.SERVICE_NAME || 'iam-service',
jaegerEndpoint: process.env.JAEGER_ENDPOINT,
enabled: true,
});
}
```
### Luồng Distributed Tracing
Distributed tracing theo dõi requests qua nhiều services sử dụng OpenTelemetry:
```mermaid
sequenceDiagram
participant Client
participant Gateway as API Gateway
participant ServiceA as Service A<br/>(User Service)
participant ServiceB as Service B<br/>(Order Service)
participant DB as Database
participant Jaeger as Jaeger<br/>Collector
Client->>Gateway: Request<br/>(Trace ID: abc123)
Gateway->>Gateway: Create Root Span<br/>Span: gateway.request
Gateway->>ServiceA: HTTP Call<br/>(Trace ID: abc123,<br/>Span ID: span-1)
ServiceA->>ServiceA: Create Child Span<br/>Span: user.getById
ServiceA->>DB: Query User<br/>(Trace ID: abc123,<br/>Span ID: span-2)
DB-->>ServiceA: User Data
ServiceA->>ServiceA: End Span span-2
ServiceA->>ServiceB: HTTP Call<br/>(Trace ID: abc123,<br/>Span ID: span-3)
ServiceB->>ServiceB: Create Child Span<br/>Span: order.getByUserId
ServiceB->>DB: Query Orders<br/>(Trace ID: abc123,<br/>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<br/>(Trace ID: abc123)
ServiceB->>Jaeger: Export Spans<br/>(Trace ID: abc123)
Gateway->>Jaeger: Export Spans<br/>(Trace ID: abc123)
Note over Jaeger: All spans linked by<br/>Trace ID: abc123
```
### Kiểm Tra Sức Khỏe
Implement liveness and readiness probes for Kubernetes.
Triển khai liveness và readiness probes cho Kubernetes.
**Ví dụ từ codebase**: [`services/iam-service/src/modules/health/health.controller.ts`](../../../services/iam-service/src/modules/health/health.controller.ts)
```typescript
import { Request, Response } from 'express';
import { prisma } from '../../config/database.config';
import { ApiResponse } from '@goodgo/types';
export class HealthController {
/**
Basic liveness probe
* VI: Kiểm tra liveness cơ bản
*/
health = async (_req: Request, res: Response): Promise<void> => {
const response: ApiResponse<{ status: string; timestamp: string }> = {
success: true,
data: {
status: 'ok',
timestamp: new Date().toISOString(),
},
timestamp: new Date().toISOString(),
};
res.json(response);
};
/**
Readiness probe (checks database connection)
* VI: Kiểm tra readiness (kiểm tra kết nối database)
*/
ready = async (_req: Request, res: Response): Promise<void> => {
try {
// Kiểm tra kết nối database
await prisma.$queryRaw`SELECT 1`;
res.json({
success: true,
data: { status: 'ready' },
timestamp: new Date().toISOString(),
});
} catch (error) {
// Trả về 503 nếu database chưa sẵn sàng
res.status(503).json({
success: false,
error: {
code: 'HEALTH_001',
message: 'Service not ready',
},
timestamp: new Date().toISOString(),
});
}
};
/**
Alias for health check
* VI: Alias cho kiểm tra sức khỏe
*/
live = async (_req: Request, res: Response): Promise<void> => {
res.json({
success: true,
data: { status: 'live' },
timestamp: new Date().toISOString(),
});
};
}
```
## Thực Hành Tốt Nhất
### Logging
- Use structured logging (JSON format)
- Include correlation IDs for request tracing
- Log at appropriate levels (ERROR, WARN, INFO, DEBUG)
- Avoid logging sensitive data (passwords, tokens, PII)
- Use consistent log format across services
**VI**:
- Sử dụng structured logging (định dạng JSON)
- Bao gồm correlation IDs để theo dõi request
- Log ở mức độ phù hợp (ERROR, WARN, INFO, DEBUG)
- Tránh log dữ liệu nhạy cảm (mật khẩu, tokens, PII)
- Sử dụng format log nhất quán giữa các services
### 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
- Normalize route paths to prevent high cardinality
**VI**:
- Sử dụng các loại metric chuẩn (Counter, Gauge, Histogram)
- Giữ cardinality thấp (tránh high-cardinality labels)
- Định nghĩa SLIs và SLOs cho các đường dẫn quan trọng
- Giám sát business metrics, không chỉ technical metrics
- Chuẩn hóa route paths để tránh high cardinality
### Tracing
- Add traces for critical operations
- Include relevant context in spans
- Sample appropriately to control costs
- Use distributed tracing for microservices
- Propagate correlation IDs across service boundaries
**VI**:
- Thêm traces cho các thao tác quan trọng
- Bao gồm context liên quan trong spans
- Sample phù hợp để kiểm soát chi phí
- Sử dụng distributed tracing cho microservices
- Truyền correlation IDs qua ranh giới service
### Cảnh Báo
- Alert on symptoms, not causes
- Include runbook links in alerts
- Avoid alert fatigue with proper thresholds
- Test alerting rules regularly
- Use correlation IDs in alert context
**VI**:
- Cảnh báo về triệu chứng, không phải nguyên nhân
- Bao gồm links runbook trong alerts
- Tránh alert fatigue với thresholds phù hợp
- Test alerting rules thường xuyên
- Sử dụng correlation IDs trong alert context
## Ví Dụ Từ Dự Án
### Triển Khai Logging
- **Request Logger**: [`services/iam-service/src/middlewares/logger.middleware.ts`](../../../services/iam-service/src/middlewares/logger.middleware.ts)
- **Correlation Middleware**: [`services/iam-service/src/middlewares/correlation.middleware.ts`](../../../services/iam-service/src/middlewares/correlation.middleware.ts)
### Triển Khai Metrics
- **Metrics Middleware**: [`services/iam-service/src/middlewares/metrics.middleware.ts`](../../../services/iam-service/src/middlewares/metrics.middleware.ts)
- **Metrics Endpoint**: Exposed at `/metrics` in all services
### Triển Khai Tracing
- **Tracing Package**: [`packages/tracing/src/index.ts`](../../../packages/tracing/src/index.ts)
- **Service Integration**: [`services/iam-service/src/main.ts`](../../../services/iam-service/src/main.ts)
### Health Checks
- **Health Controller**: [`services/iam-service/src/modules/health/health.controller.ts`](../../../services/iam-service/src/modules/health/health.controller.ts)
## Tham Khảo Nhanh
### Mức Độ Log
- `ERROR`: Errors that require immediate attention
- `WARN`: Warnings that may indicate issues
- `INFO`: Informational messages (default)
- `DEBUG`: Detailed debugging information
**VI**:
- `ERROR`: Lỗi cần chú ý ngay lập tức
- `WARN`: Cảnh báo có thể chỉ ra vấn đề
- `INFO`: Thông điệp thông tin (mặc định)
- `DEBUG`: Thông tin debug chi tiết
### Loại Metrics
- **Counter**: Monotonically increasing value (e.g., request count)
- **Gauge**: Value that can go up or down (e.g., active connections)
- **Histogram**: Distribution of values (e.g., request duration)
**VI**:
- **Counter**: Giá trị tăng đơn điệu (ví dụ: số lượng request)
- **Gauge**: Giá trị có thể tăng hoặc giảm (ví dụ: kết nối đang hoạt động)
- **Histogram**: Phân phối giá trị (ví dụ: thời lượng request)
### Endpoints Health Check
- `/health` or `/health/live`: Liveness probe (service is running)
- `/health/ready`: Readiness probe (service is ready to accept traffic)
**VI**:
- `/health` hoặc `/health/live`: Liveness probe (service đang chạy)
- `/health/ready`: Readiness probe (service sẵn sàng nhận traffic)
### Truy Vấn Prometheus
```promql
# Request rate
rate(http_requests_total[5m])
# Error rate
rate(http_requests_total{status_code=~"5.."}[5m])
# 95th percentile latency
histogram_quantile(0.95, http_request_duration_seconds)
# Active requests
http_active_requests
```
## Skills Liên Quan
- [Kubernetes Deployment](./deployment-kubernetes.md) - Để cấu hình health checks trong K8s
- [Security](./security.md) - Để logging và monitoring an toàn
- [Project Rules](./project-rules.md) - Cho cấu trúc và tiêu chuẩn service
## Tài Nguyên
### Tài Liệu Chính Thức
- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
- [Prometheus Documentation](https://prometheus.io/docs/)
- [Jaeger Documentation](https://www.jaegertracing.io/docs/)
### Tài Nguyên GoodGo
- [Observability Guide](../guides/observability.md)
- [Troubleshooting Guide](../guides/troubleshooting.md)
- [Logger Package](../../../packages/logger/README.md)
- [Tracing Package](../../../packages/tracing/README.md)

View File

@@ -1,154 +0,0 @@
# Tối Ưu Hiệu Suất (Performance Optimization)
Performance optimization patterns for GoodGo microservices including database query optimization, memory leak detection, profiling, connection pooling, and caching strategies.
> Các patterns tối ưu hiệu suất cho GoodGo microservices bao gồm tối ưu database queries, phát hiện memory leaks, profiling, connection pooling, và caching strategies.
## Tổng Quan
Performance optimization patterns help identify and fix performance bottlenecks, optimize database queries, detect memory leaks, and improve overall application performance.
Các patterns tối ưu hiệu suất giúp xác định và sửa các nút cổ chai hiệu suất, tối ưu database queries, phát hiện memory leaks, và cải thiện hiệu suất ứng dụng tổng thể.
## Khi Nào Sử Dụng
Use this skill when optimizing performance, profiling applications, or detecting bottlenecks.
Sử dụng skill này khi tối ưu hiệu suất, profiling ứng dụng, hoặc phát hiện bottlenecks.
## Quy Trình Tối Ưu Hiệu Suất
Quy trình tối ưu hiệu suất tuân theo phương pháp có hệ thống để xác định, phân tích và giải quyết các nút cổ chai hiệu suất.
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
```
## Quy Trình Tối Ưu Query
Tối ưu database query là một khía cạnh quan trọng của hiệu suất. Quy trình này cho thấy cách tối ưu query một cách có hệ thống.
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
```
## Quy Trình Profiling
Quy trình profiling giúp xác định các nút cổ chai hiệu suất thông qua việc thu thập và phân tích dữ liệu có hệ thống.
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
```
## Các Patterns Chính
### Database Query Optimization / Tối Ưu Database Queries
```typescript
// EN: Avoid N+1 queries
// VI: Tránh N+1 queries
const users = await userRepository.findAll({
include: { orders: true }, // EN: Single query / VI: Single query
});
```
### Memory Profiling / Memory Profiling
```typescript
// EN: Monitor memory usage
// VI: Giám sát memory usage
const profiler = new MemoryProfiler();
profiler.start();
```
## Best Practices / Thực Hành Tốt
1. Use indexes, avoid N+1 queries / Sử dụng indexes, tránh N+1 queries
2. Monitor memory usage / Giám sát memory usage
3. Cache frequently accessed data / Cache dữ liệu thường truy cập
## Skills Liên Quan
- [Caching Patterns](./caching-patterns.md) - Caching strategies / Chiến lược caching
- [Observability & Monitoring](./observability-monitoring.md) - Monitoring / Giám sát
## Tài Nguyên
- Skill Source: `.cursor/skills/performance-optimization/SKILL.md`

View File

@@ -1,564 +0,0 @@
# Quy Tắc Dự Án
GoodGo Microservices Platform coding standards and architecture patterns. Use when working with services, apps, packages, or infrastructure.
> Tiêu chuẩn mã hóa và mẫu kiến trúc của GoodGo Microservices Platform. Sử dụng khi làm việc với services, apps, packages, hoặc infrastructure.
## Tổng Quan
The Project Rules skill provides comprehensive guidelines for the GoodGo Microservices Platform architecture, coding standards, naming conventions, workflows, and best practices. This skill ensures consistency across all services, packages, and applications in the monorepo.
Skill Project Rules cung cấp hướng dẫn toàn diện về kiến trúc GoodGo Microservices Platform, tiêu chuẩn mã hóa, quy ước đặt tên, quy trình làm việc và thực hành tốt nhất. Skill này đảm bảo tính nhất quán trên tất cả services, packages và applications trong monorepo.
## Khi Nào Sử Dụng
Use this skill when:
- Creating a new service or package
- Setting up project structure
- Following naming conventions
- Understanding deployment patterns
- Working with dependencies
- Configuring Docker containers
- Setting up CI/CD workflows
- Understanding the monorepo structure
Sử dụng skill này khi:
- Tạo service hoặc package mới
- Thiết lập cấu trúc dự án
- Tuân theo quy ước đặt tên
- Hiểu các mẫu triển khai
- Làm việc với dependencies
- Cấu hình Docker containers
- Thiết lập CI/CD workflows
- Hiểu cấu trúc monorepo
## Khái Niệm Chính
### Kiến Trúc
The platform follows a microservices architecture with:
- **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)
Nền tảng tuân theo kiến trúc microservices với:
- **Apps**: Next.js (web) + Flutter (mobile)
- **Services**: Node.js/TypeScript microservices (Express)
- **Packages**: Thư viện dùng chung (logger, types, http-client, auth-sdk, tracing)
- **Infrastructure**: Traefik (API Gateway), Redis, Neon PostgreSQL, Observability
- **Deployments**: Local (Docker Compose), Staging/Production (Kubernetes)
#### Kiến Trúc Monorepo
```mermaid
graph TB
subgraph apps[Apps Layer]
webAdmin[web-admin<br/>Next.js Admin]
webClient[web-client<br/>Next.js Client]
appAdmin[app-admin<br/>Flutter Admin]
appClient[app-client<br/>Flutter Client]
end
subgraph gateway[API Gateway]
traefik[Traefik<br/>Path-based Routing]
end
subgraph services[Services Layer]
iamService[iam-service<br/>IAM Service]
templateService[_template<br/>Service Template]
otherServices[Other Services<br/>Node.js/TypeScript]
end
subgraph packages[Shared Packages]
loggerPackage[@goodgo/logger<br/>Centralized Logging]
typesPackage[@goodgo/types<br/>TypeScript Types]
httpClientPackage[@goodgo/http-client<br/>API Client]
authSdkPackage[@goodgo/auth-sdk<br/>Auth Utilities]
tracingPackage[@goodgo/tracing<br/>OpenTelemetry]
configPackage[@goodgo/config<br/>Shared Configs]
end
subgraph infrastructure[Infrastructure]
postgres[Neon PostgreSQL<br/>Database]
redis[Redis<br/>Cache]
prometheus[Prometheus<br/>Metrics]
grafana[Grafana<br/>Dashboards]
loki[Loki<br/>Log Aggregation]
end
subgraph deployments[Deployments]
dockerCompose[Docker Compose<br/>Local Development]
kubernetes[Kubernetes<br/>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
```
### Công Nghệ
**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
## Các Pattern Thường Dùng
### Cấu Trúc Service
Standard service structure:
```
services/[service-name]/
├── src/
│ ├── config/ # Configuration files
│ ├── modules/ # Feature modules
│ ├── middlewares/ # Express middlewares
│ ├── repositories/ # Data access layer
│ ├── routes/ # Route definitions
│ └── main.ts # Entry point
├── prisma/ # Database schema
├── Dockerfile # Container definition
└── package.json # Dependencies
```
Cấu trúc service chuẩn:
```
services/[service-name]/
├── src/
│ ├── config/ # File cấu hình
│ ├── modules/ # Module tính năng
│ ├── middlewares/ # Express middlewares
│ ├── repositories/ # Lớp truy cập dữ liệu
│ ├── routes/ # Định nghĩa routes
│ └── main.ts # Điểm vào
├── prisma/ # Database schema
├── Dockerfile # Định nghĩa container
└── package.json # Dependencies
```
#### Biểu Đồ Cấu Trúc Chi Tiết
```mermaid
graph TB
subgraph service[Service Structure]
serviceRoot[service-name/]
serviceSrc[src/]
serviceConfig[config/<br/>Configuration]
serviceModules[modules/<br/>Feature Modules]
serviceMiddlewares[middlewares/<br/>Express Middlewares]
serviceRoutes[routes/<br/>Route Definitions]
serviceMain[main.ts<br/>Entry Point]
servicePrisma[prisma/<br/>Schema & Migrations]
serviceDockerfile[Dockerfile<br/>Container Definition]
servicePackageJson[package.json<br/>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<br/>Main Export]
packagePackageJson[package.json<br/>Package Metadata]
packageTsconfig[tsconfig.json<br/>TypeScript Config]
packageReadme[README.md<br/>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/<br/>Next.js App Router]
appServicesApi[services/api/<br/>API Clients]
appStores[stores/<br/>State Management]
appDockerfile[Dockerfile<br/>Container Definition]
appPackageJson[package.json<br/>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<br/>HTTP Handlers]
moduleService[feature.service.ts<br/>Business Logic]
moduleRepository[feature.repository.ts<br/>Data Access]
moduleDto[feature.dto.ts<br/>Zod Schemas]
moduleTypes[feature.types.ts<br/>TypeScript Types]
moduleTest[feature.controller.test.ts<br/>Unit Tests]
moduleRoot --> moduleController
moduleRoot --> moduleService
moduleRoot --> moduleRepository
moduleRoot --> moduleDto
moduleRoot --> moduleTypes
moduleRoot --> moduleTest
moduleController --> moduleService
moduleService --> moduleRepository
end
serviceModules --> moduleRoot
```
### Pattern Module
Controller → Service → Repository pattern:
```typescript
// DTO with Zod
export const CreateFeatureDto = z.object({
name: z.string().min(1),
email: z.string().email()
});
export type CreateFeatureDto = z.infer<typeof CreateFeatureDto>;
// 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<Feature> {
async create(data: CreateFeatureDto) {
return this.prisma.feature.create({ data });
}
}
```
Pattern Controller → Service → Repository:
```typescript
// DTO với Zod
export const CreateFeatureDto = z.object({
name: z.string().min(1),
email: z.string().email()
});
export type CreateFeatureDto = z.infer<typeof CreateFeatureDto>;
// 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<Feature> {
async create(data: CreateFeatureDto) {
return this.prisma.feature.create({ data });
}
}
```
### Pattern Phản Hồi API
Standardized API responses:
```typescript
// Success response
{
success: true,
data: any,
timestamp: string
}
// Error response
{
success: false,
error: {
code: string,
message: string,
details?: any
},
timestamp: string
}
```
Phản hồi API chuẩn hóa:
```typescript
// Phản hồi thành công
{
success: true,
data: any,
timestamp: string
}
// Phản hồi lỗi
{
success: false,
error: {
code: string,
message: string,
details?: any
},
timestamp: string
}
```
## Thực Hành Tốt Nhất
### Quy Ước Đặt Tên
- **Services/Packages**: `kebab-case` (e.g., `iam-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`
**VI**:
- **Services/Packages**: `kebab-case` (ví dụ: `iam-service`, `http-client`)
- **Files**: `kebab-case.type.ts` (ví dụ: `user.controller.ts`)
- **Components**: `PascalCase.tsx` (React), `snake_case.dart` (Flutter)
- **Classes**: `PascalCase`
- **Functions**: `camelCase`
- **Constants**: `UPPER_SNAKE_CASE`
- **Package Names**: `@goodgo/package-name`
### Tiêu Chuẩn TypeScript
- Strict mode enabled
- No `any` type (use `unknown` instead)
- Zod for runtime validation
- Export shared types from `@goodgo/types`
**VI**:
- Bật strict mode
- Không dùng type `any` (dùng `unknown` thay thế)
- Zod cho runtime validation
- Export shared types từ `@goodgo/types`
### Ghi Log
Use `@goodgo/logger`:
```typescript
import { logger } from '@goodgo/logger';
logger.info('Message', { context });
logger.error('Error', { error, context });
```
Sử dụng `@goodgo/logger`:
```typescript
import { logger } from '@goodgo/logger';
logger.info('Message', { context });
logger.error('Error', { error, context });
```
### Biến Môi Trường
- Use `.env.example` template
- Never commit `.env` files
- Validate with Zod at startup
- Document all variables in README
**VI**:
- Sử dụng template `.env.example`
- Không bao giờ commit file `.env`
- Validate với Zod khi khởi động
- Tài liệu hóa tất cả biến trong README
## Ví Dụ Từ Dự Án
### Template Service
See `services/_template/` for a complete service template with:
- Standard structure
- Middleware setup
- Error handling
- Health checks
- Swagger documentation
Xem `services/_template/` để có template service hoàn chỉnh với:
- Cấu trúc chuẩn
- Thiết lập middleware
- Xử lý lỗi
- Health checks
- Tài liệu Swagger
### Cấu Hình Traefik
Services are registered in `deployments/local/docker-compose.yml`:
```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"
```
Services được đăng ký trong `deployments/local/docker-compose.yml`:
```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"
```
### Ví Dụ Service Thực Tế
- `services/iam-service/` - Identity and Access Management service (RBAC, OIDC, MFA, Identity, Access, Governance)
- `packages/logger/` - Shared logging package
- `packages/types/` - Shared TypeScript types
**VI**:
- `services/iam-service/` - Service quản lý danh tính và quyền truy cập (RBAC, OIDC, MFA, Identity, Access, Governance)
- `packages/logger/` - Package logging dùng chung
- `packages/types/` - TypeScript types dùng chung
## Tham Khảo Nhanh
### Tạo Service Mới
```bash
# 1. Copy template
cp -r services/_template services/my-service
# 2. Update package.json name
# Change to: "@goodgo/my-service"
# 3. Add to docker-compose.yml
# Add service configuration with Traefik labels
# 4. Configure Prisma schema
cd services/my-service
pnpm prisma migrate dev
# 5. Add health check endpoint
# Already included in template
```
### Làm Việc Với Dependencies
```bash
# Add external package
pnpm --filter @goodgo/service-name add package-name
# Add workspace package
pnpm --filter @goodgo/service-name add @goodgo/logger
# Add dev dependency
pnpm --filter @goodgo/service-name add -D @types/pkg
```
### Lệnh Database
```bash
# Run migrations
pnpm --filter @goodgo/service-name prisma migrate dev
# Generate Prisma client
pnpm --filter @goodgo/service-name prisma generate
# Seed database
pnpm --filter @goodgo/service-name prisma db seed
```
## Skills Liên Quan
- **[API Design](./api-design.md)** - RESTful API design standards
- **[Database Prisma](./database-prisma.md)** - Prisma ORM patterns
- **[Security](./security.md)** - Security best practices
- **[Testing Patterns](./testing-patterns.md)** - Testing guidelines
- **[Documentation](./documentation.md)** - Documentation writing guidelines
## Tài Nguyên
- [Architecture Docs](../architecture/system-design.md)
- [API Specs](../api/openapi/)
- [Development Guide](../guides/development.md)
- [Deployment Guide](../guides/deployment.md)
- [Contributing Guide](../../../CONTRIBUTING.md)
**VI**:
- [Tài Liệu Kiến Trúc](../architecture/system-design.md)
- [Spec API](../api/openapi/)
- [Hướng Dẫn Phát Triển](../guides/development.md)
- [Hướng Dẫn Triển Khai](../guides/deployment.md)
- [Hướng Dẫn Đóng Góp](../../../CONTRIBUTING.md)

View File

@@ -1,421 +0,0 @@
# Pattern Repository
Repository pattern implementation and best practices for GoodGo microservices including BaseRepository extension, custom queries, transactions, and query optimization.
> Implementation và best practices về repository pattern cho GoodGo microservices bao gồm mở rộng BaseRepository, custom queries, transactions, và query optimization.
## Tổng Quan
The Repository Pattern provides an abstraction layer between business logic and data access, making code more maintainable, testable, and consistent. This guide covers how to implement repositories using the BaseRepository class, write custom queries, handle transactions, and optimize database operations in GoodGo microservices.
Repository Pattern cung cấp abstraction layer giữa business logic và data access, làm cho code dễ bảo trì, testable, và nhất quán hơn. Hướng dẫn này bao gồm cách implement repositories sử dụng BaseRepository class, viết custom queries, xử lý transactions, và optimize database operations trong GoodGo microservices.
## Khi Nào Sử Dụng
Use the repository pattern when:
- Implementing data access layers for new modules
- Extending BaseRepository for specific entity types
- Writing custom database queries beyond standard CRUD
- Handling database transactions for multiple operations
- Optimizing database queries and operations
- Testing repository implementations
- Organizing data access code consistently
Sử dụng repository pattern khi:
- Implement data access layers cho modules mới
- Mở rộng BaseRepository cho các entity types cụ thể
- Viết custom database queries ngoài CRUD chuẩn
- Xử lý database transactions cho nhiều operations
- Optimize database queries và operations
- Testing repository implementations
- Tổ chức data access code một cách nhất quán
## Khái Niệm Chính
### Lợi Ích Của Repository Pattern
1. **Abstraction / Trừu Tượng Hóa**: Tách biệt business logic khỏi data access
2. **Testability / Khả Năng Test**: Dễ dàng mock repositories để test
3. **Maintainability / Khả Năng Bảo Trì**: Centralized database operations
4. **Consistency / Tính Nhất Quán**: Standardized CRUD operations
5. **Type Safety / An Toàn Kiểu**: TypeScript generics cung cấp type safety
### Kiến Trúc Repository
Repository pattern tạo abstraction layer giữa service layer và data access layer, cung cấp sự tách biệt rõ ràng giữa các concerns.
```mermaid
graph TB
Controller["Controller<br/>(HTTP Handler)"] --> Service["Service Layer<br/>(Business Logic)"]
Service --> Repository["Repository<br/>(Data Access)"]
Repository --> Prisma["Prisma Client<br/>(ORM)"]
Prisma --> Database[("Database<br/>(PostgreSQL)")]
style Controller fill:#e1f5ff
style Service fill:#fff4e1
style Repository fill:#e8f5e9
style Prisma fill:#f3e5f5
style Database fill:#ffebee
```
### Class BaseRepository
The `BaseRepository` abstract class provides common database operations that can be extended by specific repositories. It handles error handling, logging, and provides type-safe methods for CRUD operations.
Class abstract `BaseRepository` cung cấp các database operations chung có thể được mở rộng bởi các repositories cụ thể. Nó xử lý error handling, logging, và cung cấp các methods type-safe cho CRUD operations.
**Available Methods / Các Methods Có Sẵn**:
- `findById(id)` - Find entity by ID / Tìm entity theo ID
- `findByUnique(field, value)` - Find by unique field / Tìm theo field duy nhất
- `findAll(options)` - Find all with filtering, pagination, sorting / Tìm tất cả với filtering, pagination, sorting
- `create(data)` - Create new entity / Tạo entity mới
- `update(id, data)` - Update entity / Cập nhật entity
- `delete(id)` - Delete entity / Xóa entity
- `count(where)` - Count entities / Đếm entities
- `exists(id)` - Check if entity exists / Kiểm tra entity có tồn tại
- `transaction(callback)` - Execute transaction / Thực thi transaction
### Cấu Trúc Class Hierarchy
Repositories extend abstract class `BaseRepository`, kế thừa các CRUD operations chung trong khi cho phép thêm custom query methods.
```mermaid
classDiagram
class BaseRepository {
<<abstract>>
#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 {
<<interface>>
+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
```
**Lưu ý**: Các repositories cụ thể như `UserRepository``ProductRepository` extend `BaseRepository` và có thể implement interface `IRepository` để có thêm type safety. Chúng kế thừa tất cả base CRUD methods và thêm các domain-specific query methods.
**Tham Khảo**: [`services/iam-service/src/modules/common/repository.ts`](../../../services/iam-service/src/modules/common/repository.ts)
## Patterns
### Mở Rộng BaseRepository
Create a repository by extending BaseRepository and passing the Prisma model name.
Tạo repository bằng cách mở rộng BaseRepository và truyền tên Prisma model.
```typescript
import { PrismaClient, User } from '@prisma/client';
import { BaseRepository } from '../modules/common/repository';
export class UserRepository extends BaseRepository<User, CreateUserInput, UpdateUserInput> {
constructor(prisma: PrismaClient) {
super(prisma, 'user');
}
// Thêm các methods tùy chỉnh
async findByEmail(email: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { email },
});
}
}
```
**Tham Khảo**: [`services/iam-service/src/repositories/user.repository.ts`](../../../services/iam-service/src/repositories/user.repository.ts)
### Các Methods Query Tùy Chỉnh
Add domain-specific query methods for complex queries that go beyond standard CRUD operations.
Thêm các methods query domain-specific cho các queries phức tạp vượt quá CRUD operations chuẩn.
```typescript
export class UserRepository extends BaseRepository<User, CreateUserInput, UpdateUserInput> {
// Tìm user với dữ liệu liên quan
async findWithPermissions(userId: string): Promise<User | null> {
return this.prisma.user.findUnique({
where: { id: userId },
include: {
userRoles: {
include: { role: true },
},
userPermissions: {
include: { permission: true },
},
},
});
}
// Query phức tạp với filtering
async findActiveUsers(organizationId?: string): Promise<User[]> {
return this.prisma.user.findMany({
where: {
isActive: true,
...(organizationId && { organizationId }),
},
orderBy: { createdAt: 'desc' },
});
}
}
```
### Sử Dụng Repository Interface
Implement the `IRepository` interface for additional type safety and consistency.
Implement interface `IRepository` để có thêm type safety và tính nhất quán.
```typescript
import { IRepository } from '../modules/common/repository';
export class UserRepository
extends BaseRepository<User, CreateUserInput, UpdateUserInput>
implements IRepository<User, CreateUserInput, UpdateUserInput> {
constructor(prisma: PrismaClient) {
super(prisma, 'user');
}
// Implementation...
}
```
### Xử Lý Lỗi
BaseRepository handles errors automatically, wrapping database operations in try-catch blocks and converting errors to DatabaseError instances.
BaseRepository xử lý errors tự động, bọc các database operations trong try-catch blocks và chuyển đổi errors thành DatabaseError instances.
```typescript
// BaseRepository tự động xử lý errors
async findById(id: string): Promise<T | null> {
try {
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 the repository transaction method for operations that need to be atomic (all succeed or all fail).
Sử dụng method transaction của repository cho các operations cần atomic (tất cả thành công hoặc tất cả thất bại).
```typescript
// Thực thi nhiều operations trong transaction
await userRepository.transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.userProfile.create({
data: { userId: user.id, ...profileData }
});
return user;
});
```
**Transaction Flow / Luồng Transaction**:
```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)<br/>Tất cả operations sử dụng 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 / Tất cả operations thành công
Prisma->>DB: COMMIT
DB-->>Prisma: Transaction committed
Prisma-->>Repository: Success result
Repository-->>Service: Success result
else Error occurs / Lỗi xảy ra
Prisma->>DB: ROLLBACK
DB-->>Prisma: Transaction rolled back
Prisma-->>Repository: Error thrown
Repository-->>Service: DatabaseError thrown
end
```
**Important / Quan Trọng**: Tất cả operations trong transaction callback phải sử dụng transaction client (`tx`) parameter, không phải main Prisma client, để đảm bảo tính atomic.
### Các Tùy Chọn Query
Use Prisma query options in findAll for filtering, pagination, sorting, and including relations.
Sử dụng các tùy chọn query của Prisma trong findAll để filtering, pagination, sorting, và include relations.
```typescript
// Phân trang
const users = await userRepository.findAll({
skip: (page - 1) * limit,
take: limit,
});
// Lọc
const activeUsers = await userRepository.findAll({
where: { isActive: true },
});
// Sắp xếp
const recentUsers = await userRepository.findAll({
orderBy: { createdAt: 'desc' },
});
// Bao gồm relations
const usersWithRoles = await userRepository.findAll({
include: { userRoles: true },
});
```
## Thực Hành Tốt Nhất
1. **Extend BaseRepository / Mở Rộng BaseRepository**: Luôn extend BaseRepository thay vì implement từ đầu
2. **Custom Methods / Methods Tùy Chỉnh**: Thêm domain-specific query methods trong repository subclasses
3. **Type Safety / An Toàn Kiểu**: Sử dụng TypeScript generics cho type safety
4. **Error Handling / Xử Lý Lỗi**: Để BaseRepository xử lý common errors, xử lý domain-specific errors trong custom methods
5. **Logging / Ghi Log**: BaseRepository xử lý logging tự động
6. **Transactions / Transactions**: Sử dụng repository transaction method cho multi-step operations
7. **Query Optimization / Tối Ưu Query**: Sử dụng Prisma query options (select, include) để optimize queries
8. **Single Responsibility / Trách Nhiệm Đơn**: Mỗi repository xử lý một entity type
9. **Dependency Injection / Tiêm Phụ Thuộc**: Inject PrismaClient trong constructor để dễ test
10. **Avoid Business Logic / Tránh Business Logic**: Không đặt business logic trong repository, đặt trong service layer
## Lỗi Thường Gặp
1. **Not Extending BaseRepository / Không Mở Rộng BaseRepository**: Implement CRUD từ đầu thay vì extend
2. **Business Logic in Repository / Business Logic Trong Repository**: Đặt business logic trong repository thay vì service layer
3. **Exposing Prisma Client / Tiết Lộ Prisma Client**: Expose raw Prisma client thay vì sử dụng repository methods
4. **Missing Error Handling / Thiếu Xử Lý Lỗi**: Không xử lý errors trong custom query methods
5. **Over-fetching Data / Lấy Quá Nhiều Dữ Liệu**: Sử dụng `include` không cần thiết, fetch quá nhiều data
6. **No Type Safety / Không An Toàn Kiểu**: Không sử dụng TypeScript generics đúng cách
7. **Transaction Mistakes / Lỗi Transaction**: Không sử dụng repository transaction method cho related operations
8. **N+1 Query Problems / Vấn Đề N+1 Query**: Fetch related data trong loops thay vì sử dụng include
9. **Missing Indexes / Thiếu Indexes**: Không thêm database indexes cho các fields thường query
## Xử Lý Sự Cố
### Lỗi Kiểu Với Prisma
**Problem / Vấn Đề**: TypeScript errors khi sử dụng Prisma client methods
**Solution / Giải Pháp**:
- Đảm bảo Prisma client đã được generate: `pnpm prisma generate`
- Sử dụng type assertions nếu cần: `(this.prisma as any)[this.modelName]`
- Verify Prisma schema matches TypeScript types
### Vấn Đề Rollback Transaction
**Problem / Vấn Đề**: Transaction không rollback khi có lỗi
**Solution / Giải Pháp**:
- Đảm bảo tất cả operations trong transaction callback sử dụng transaction client (`tx`) parameter
- Không sử dụng main Prisma client trong transaction callback
- Let errors propagate, transaction will rollback automatically
**Example / Ví Dụ**:
```typescript
// ❌ Bad: Using main client in transaction
await repository.transaction(async (tx) => {
await repository.prisma.user.create(...); // Wrong!
});
// ✅ Good: Using transaction client
await repository.transaction(async (tx) => {
await tx.user.create(...); // Correct!
});
```
### Vấn Đề Hiệu Suất
**Problem / Vấn Đề**: Queries chậm hoặc N+1 query problems
**Solution / Giải Pháp**:
- Sử dụng `include` để fetch related data trong single query
- Sử dụng `select` để limit fields được fetch
- Thêm database indexes cho các fields thường query
- Avoid fetching unnecessary relations
**Example / Ví Dụ**:
```typescript
// ❌ Bad: N+1 query problem
const users = await userRepository.findAll();
for (const user of users) {
const roles = await roleRepository.findByUserId(user.id); // N queries!
}
// ✅ Good: Single query with include
const users = await userRepository.findAll({
include: { userRoles: { include: { role: true } } },
});
```
### Tên Model Không Khớp
**Problem / Vấn Đề**: Model name không khớp với Prisma schema
**Solution / Giải Pháp**:
- Verify model name matches Prisma schema exactly (case-sensitive)
- Use camelCase for model names (e.g., 'user', 'userProfile', 'accessRequest')
- Check Prisma schema for correct model names
## Tài Nguyên
- [BaseRepository](../../../services/iam-service/src/modules/common/repository.ts) - Base repository implementation
- [User Repository](../../../services/iam-service/src/repositories/user.repository.ts) - Example repository implementation
- [Database Prisma](./database-prisma.md) - Prisma ORM patterns và best practices
- [Error Handling](./error-handling-patterns.md) - Error handling patterns trong repositories

View File

@@ -1,217 +0,0 @@
# Các Pattern Resilience
Resilience patterns for GoodGo microservices including circuit breaker, retry strategies, timeout handling, and graceful degradation for improved fault tolerance and system reliability.
> Các resilience patterns cho GoodGo microservices bao gồm circuit breaker, retry strategies, timeout handling, và graceful degradation để cải thiện fault tolerance và độ tin cậy hệ thống.
## Tổng Quan
Resilience patterns help microservices handle failures gracefully, prevent cascading failures, and maintain system availability even when dependencies fail. This guide covers circuit breaker patterns, retry strategies, timeout handling, and graceful degradation techniques.
Resilience patterns giúp microservices xử lý failures một cách graceful, ngăn chặn cascading failures, và duy trì tính khả dụng của hệ thống ngay cả khi dependencies fail. Hướng dẫn này bao gồm circuit breaker patterns, retry strategies, timeout handling, và các kỹ thuật graceful degradation.
## Khi Nào Sử Dụng
Use resilience patterns 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
Sử dụng resilience patterns khi:
- Implement circuit breaker patterns cho external services
- Thêm retry logic cho transient failures
- Thiết lập timeout handling cho long-running operations
- Implement graceful degradation strategies
- Xử lý external service failures
- Cải thiện system fault tolerance
## Khái Niệm Chính
### Các Pattern Resilience
1. **Circuit Breaker / Ngắt Mạch**: Prevents cascading failures by stopping calls to failing services / Ngăn chặn cascading failures bằng cách dừng calls tới services đang fail
2. **Retry / Thử Lại**: Automatically retries failed operations with backoff / Tự động retry các operations thất bại với backoff
3. **Timeout / Hết Thời Gian**: Sets maximum time limits for operations / Thiết lập giới hạn thời gian tối đa cho operations
4. **Bulkhead / Ngăn Cách**: Isolates failures to prevent spread / Cô lập failures để ngăn chặn lây lan
5. **Graceful Degradation / Suy Giảm Nhẹ Nhàng**: Provides fallback behavior when services fail / Cung cấp fallback behavior khi services fail
## Patterns
### Pattern Circuit Breaker
Protects against cascading failures by opening the circuit when error threshold is reached.
Bảo vệ chống lại cascading failures bằng cách mở circuit khi đạt ngưỡng lỗi.
Circuit breaker có ba trạng thái chuyển đổi dựa trên tỷ lệ lỗi và timeout:
```mermaid
stateDiagram-v2
[*] --> CLOSED: Initial State
CLOSED --> OPEN: Errors exceed threshold<br/>(errorThresholdPercentage: 50%)
OPEN --> HALF_OPEN: Reset timeout expires<br/>(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 / Trạng Thái Circuit Breaker:**
- **CLOSED**: Normal operation, requests pass through / Hoạt động bình thường, requests được cho phép
- **OPEN**: Circuit is open, requests are immediately rejected / Circuit mở, requests bị từ chối ngay lập tức
- **HALF-OPEN**: Testing if service has recovered, allows limited requests / Kiểm tra service đã phục hồi, cho phép số lượng requests hạn chế
```typescript
import CircuitBreaker from 'opossum';
import { logger } from '@goodgo/logger';
export const createCircuitBreaker = <TArgs extends any[], TResult>(
action: (...args: TArgs) => Promise<TResult>,
name: string,
options: Partial<CircuitBreaker.Options> = {}
): CircuitBreaker<TArgs, TResult> => {
const breaker = new CircuitBreaker(action, {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000,
...options,
name,
});
breaker.on('open', () => {
${name}`);
});
return breaker;
};
// Usage
const externalApiBreaker = createCircuitBreaker(
async (data) => await externalApi.call(data),
'external-api'
);
```
**Tham Khảo**: [`services/iam-service/src/modules/common/circuit-breaker.ts`](../../../services/iam-service/src/modules/common/circuit-breaker.ts)
### Pattern Retry
Retry transient failures with exponential backoff.
Retry transient failures với exponential backoff.
Pattern retry thử lại operation nhiều lần với delay tăng dần giữa các lần thử:
```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:<br/>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 / Ví Dụ Exponential Backoff:**
- Attempt 1: 1s delay
- Attempt 2: 2s delay
- Attempt 3: 4s delay
- Attempt 4: 8s delay
```typescript
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
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');
}
```
### Pattern Timeout
Set maximum time limits for operations.
Thiết lập giới hạn thời gian tối đa cho operations.
Pattern timeout sử dụng Promise.race để thực thi giới hạn thời gian tối đa:
```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 / Hành Vi Timeout:**
- Uses `Promise.race()` to compete operation vs timeout / Sử dụng `Promise.race()` để so sánh operation với timeout
- First to resolve/reject wins / Cái nào resolve/reject trước sẽ thắng
- Operation may continue after timeout, but result is ignored / Operation có thể tiếp tục sau timeout, nhưng kết quả bị bỏ qua
```typescript
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number
): Promise<T> {
const timeout = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Operation timeout')), timeoutMs);
});
return Promise.race([promise, timeout]);
}
```
## Thực Hành Tốt Nhất
1. **Circuit Breaker / Circuit Breaker**: Sử dụng cho external service calls
2. **Retry / Retry**: Chỉ retry transient failures (network, timeout)
3. **Timeout / Timeout**: Thiết lập timeouts phù hợp cho tất cả external calls
4. **Fallback / Fallback**: Luôn cung cấp fallback behavior
5. **Monitoring / Giám Sát**: Monitor circuit breaker states và retry rates
6. **Logging / Ghi Log**: Log tất cả resilience actions để debug
## Lỗi Thường Gặp
1. **Retrying Non-Retryable Errors / Retry Các Lỗi Không Thể Retry**: Retry 4xx errors (client errors)
2. **No Timeout / Không Có Timeout**: Thiếu timeouts trên external calls
3. **No Fallback / Không Có Fallback**: Không có graceful degradation strategy
4. **Too Many Retries / Retry Quá Nhiều**: Retry quá nhiều gây performance issues
## Tài Nguyên
- [Circuit Breaker](../../../services/iam-service/src/modules/common/circuit-breaker.ts) - Circuit breaker implementation

View File

@@ -1,853 +0,0 @@
# Bảo Mật
> Thực hành và mẫu bảo mật tốt nhất cho nền tảng microservices GoodGo. Sử dụng khi triển khai xác thực, phân quyền, bảo vệ dữ liệu, xác thực đầu vào, giới hạn tốc độ, quản lý bí mật hoặc kiểm tra bảo mật trên tất cả các dịch vụ.
## Tổng Quan
Skill Security cung cấp các mẫu bảo mật toàn diện, thực hành tốt nhất và ví dụ triển khai để bảo vệ các microservices GoodGo. Nó bao gồm xác thực, phân quyền, bảo vệ dữ liệu, xác thực đầu vào, giới hạn tốc độ, quản lý bí mật và kiểm tra bảo mật.
## Khi Nào Sử Dụng
Sử dụng skill này khi:
- Triển khai xác thực và phân quyền trong bất kỳ service nào
- Bảo vệ dữ liệu nhạy cảm (PII, thông tin đăng nhập, token)
- Xác thực đầu vào người dùng và tải lên tệp
- Triển khai giới hạn tốc độ và bảo vệ DDoS
- Thiết lập ghi nhật ký kiểm toán và giám sát bảo mật
- Mã hóa dữ liệu khi nghỉ và khi truyền
- Quản lý bí mật và thông tin đăng nhập
- Triển khai kiểm tra bảo mật
- Xử lý sự cố bảo mật
- Thiết kế các endpoint API an toàn
## Khái Niệm Chính
### Nguyên Tắc Bảo Mật Cốt Lõi
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
**VI**:
1. **Defense in Depth**: Nhiều lớp kiểm soát bảo mật
2. **Least Privilege**: Cấp quyền tối thiểu cần thiết
3. **Fail Secure**: Mặc định từ chối truy cập
4. **Separation of Duties**: Các thao tác quan trọng yêu cầu nhiều phê duyệt
5. **Audit Everything**: Ghi log tất cả sự kiện liên quan đến bảo mật
6. **Encrypt Sensitive Data**: PII, token, thông tin đăng nhập phải được mã hóa
7. **Validate All Inputs**: Không bao giờ tin tưởng đầu vào người dùng
8. **Principle of Least Exposure**: Giảm thiểu bề mặt tấn công
9. **Secure by Default**: Bảo mật được tích hợp sẵn, không phải thêm vào sau
10. **Assume Breach**: Thiết kế để phát hiện và phản ứng
## Các Pattern Thường Dùng
### Xác Thực & Phân Quyền
#### Xác Thực Token JWT
Sơ đồ sau minh họa luồng xác thực khi client gửi request với 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<br/>Authorization header or cookie
alt Token not found
Middleware->>Client: 401 Unauthorized<br/>(AUTH_REQUIRED)
else Token found
Middleware->>JWTService: verifyAccessToken(token)
alt Token invalid or expired
JWTService->>Middleware: Verification failed
Middleware->>Client: 401 Unauthorized<br/>(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
```
Example from `services/iam-service/src/middlewares/auth.middleware.ts`:
```typescript
import { jwtService } from '../modules/token/jwt.service';
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_001', 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: 'AUTH_002', message: 'Invalid or expired token' }
});
}
};
};
```
Ví dụ từ `services/iam-service/src/middlewares/auth.middleware.ts`:
```typescript
import { jwtService } from '../modules/token/jwt.service';
import { logger } from '@goodgo/logger';
export const authenticate = () => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
// Trích xuất token từ header Authorization hoặc 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_001', message: 'Yêu cầu xác thực' }
});
}
// Xác minh token
const payload = await jwtService.verifyAccessToken(token);
// Gắn user vào request
req.user = {
id: payload.sub,
userId: payload.sub,
email: payload.email,
roles: payload.roles || [],
permissions: payload.permissions || []
};
next();
} catch (error) {
logger.warn('Xác thực thất bại', { error: error.message });
return res.status(401).json({
success: false,
error: { code: 'AUTH_002', message: 'Token không hợp lệ hoặc hết hạn' }
});
}
};
};
```
#### Phân Quyền Dựa Trên Quyền
Luồng quyết định phân quyền xác định xem người dùng có quyền truy cập tài nguyên hay không:
```mermaid
flowchart TD
Start([Request Received]) --> CheckAuth{User<br/>Authenticated?}
CheckAuth -->|No| Return401[Return 401<br/>AUTH_REQUIRED]
CheckAuth -->|Yes| CheckType{Authorization<br/>Type?}
CheckType -->|Role-Based| CheckRole{User has<br/>Required Role?}
CheckType -->|Permission-Based| CheckPermission{User has<br/>Resource:Action<br/>Permission?}
CheckType -->|Ownership| CheckOwnership{Resource ID<br/>matches User ID?}
CheckRole -->|No| LogDenial[Log Permission Denied<br/>with user roles]
CheckPermission -->|No| LogDenial
CheckOwnership -->|No| LogDenial
LogDenial --> Return403[Return 403<br/>FORBIDDEN]
CheckRole -->|Yes| Allow[Allow Request<br/>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
```
Example from `services/iam-service/src/middlewares/rbac.middleware.ts`:
```typescript
export const requirePermission = (
resource: string,
action: string,
scope?: string
) => {
return async (req: Request, res: Response, next: NextFunction) => {
const userId = req.user?.id || req.user?.sub;
if (!userId) {
return res.status(401).json({
success: false,
error: { code: 'UNAUTHORIZED', message: 'Authentication required' }
});
}
const hasPermission = await rbacService.hasPermission(
userId,
resource,
action,
scope
);
if (!hasPermission) {
return res.status(403).json({
success: false,
error: {
code: 'INSUFFICIENT_PERMISSIONS',
message: `Requires ${action} permission on ${resource}`
}
});
}
next();
};
};
```
Ví dụ từ `services/iam-service/src/middlewares/rbac.middleware.ts`:
```typescript
export const requirePermission = (
resource: string,
action: string,
scope?: string
) => {
return async (req: Request, res: Response, next: NextFunction) => {
const userId = req.user?.id || req.user?.sub;
if (!userId) {
return res.status(401).json({
success: false,
error: { code: 'UNAUTHORIZED', message: 'Yêu cầu xác thực' }
});
}
const hasPermission = await rbacService.hasPermission(
userId,
resource,
action,
scope
);
if (!hasPermission) {
return res.status(403).json({
success: false,
error: {
code: 'INSUFFICIENT_PERMISSIONS',
message: `Yêu cầu quyền ${action} trên ${resource}`
}
});
}
next();
};
};
```
### Xác Thực Đầu Vào
Example from `services/iam-service/src/middlewares/validation.middleware.ts`:
```typescript
import { AnyZodObject, ZodError } from 'zod';
export const validateDto = (
schema: AnyZodObject,
property: 'body' | 'query' | 'params' = 'body'
) => {
return (req: Request, res: Response, next: NextFunction) => {
try {
// Sanitize input by trimming strings
const sanitizedData = sanitizeInput(req[property]);
// Validate the sanitized data
const validatedData = schema.parse(sanitizedData);
// Replace original data with validated data
(req as any)[property] = validatedData;
next();
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request data',
details: error.errors.map(err => ({
field: err.path.join('.'),
message: err.message,
code: err.code
}))
}
});
}
throw error;
}
};
};
function sanitizeInput(data: any): any {
if (typeof data === 'string') {
return data.trim();
}
if (Array.isArray(data)) {
return data.map(sanitizeInput);
}
if (data !== null && typeof data === 'object') {
const sanitized: any = {};
for (const [key, value] of Object.entries(data)) {
sanitized[key] = sanitizeInput(value);
}
return sanitized;
}
return data;
}
```
Ví dụ từ `services/iam-service/src/middlewares/validation.middleware.ts`:
```typescript
import { AnyZodObject, ZodError } from 'zod';
export const validateDto = (
schema: AnyZodObject,
property: 'body' | 'query' | 'params' = 'body'
) => {
return (req: Request, res: Response, next: NextFunction) => {
try {
// Làm sạch đầu vào bằng cách cắt chuỗi
const sanitizedData = sanitizeInput(req[property]);
// Xác thực dữ liệu đã được làm sạch
const validatedData = schema.parse(sanitizedData);
// Thay thế dữ liệu gốc bằng dữ liệu đã xác thực
(req as any)[property] = validatedData;
next();
} catch (error) {
if (error instanceof ZodError) {
return res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Dữ liệu request không hợp lệ',
details: error.errors.map(err => ({
field: err.path.join('.'),
message: err.message,
code: err.code
}))
}
});
}
throw error;
}
};
};
function sanitizeInput(data: any): any {
if (typeof data === 'string') {
return data.trim();
}
if (Array.isArray(data)) {
return data.map(sanitizeInput);
}
if (data !== null && typeof data === 'object') {
const sanitized: any = {};
for (const [key, value] of Object.entries(data)) {
sanitized[key] = sanitizeInput(value);
}
return sanitized;
}
return data;
}
```
### Giới Hạn Tốc Độ
Example from `services/iam-service/src/middlewares/rate-limit.middleware.ts`:
```typescript
import rateLimit from 'express-rate-limit';
import { RateLimiterRedis } from 'rate-limit-redis';
import { getRedisClient } from '../config/redis.config';
export const dynamicRateLimit = async (
req: Request,
res: Response,
next: NextFunction
) => {
const userId = req.user?.id || req.user?.sub;
// Default limits
let windowMs = 15 * 60 * 1000; // 15 minutes
let max = 100; // 100 requests
if (userId) {
const roles = await rbacService.getUserRoles(userId);
// Set limits based on role
if (roles.includes('SUPER_ADMIN')) {
max = 1000;
} else if (roles.includes('ADMIN')) {
max = 500;
} else if (roles.includes('MODERATOR')) {
max = 300;
}
} else {
// Unauthenticated users - stricter limits
max = 50;
}
const limiter = rateLimit({
windowMs,
max,
store: new RateLimiterRedis({
client: getRedisClient(),
prefix: 'rl:'
}),
handler: (req, res) => {
res.status(429).json({
success: false,
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Too many requests, please try again later'
}
});
}
});
limiter(req, res, next);
};
```
Ví dụ từ `services/iam-service/src/middlewares/rate-limit.middleware.ts`:
```typescript
import rateLimit from 'express-rate-limit';
import { RateLimiterRedis } from 'rate-limit-redis';
import { getRedisClient } from '../config/redis.config';
export const dynamicRateLimit = async (
req: Request,
res: Response,
next: NextFunction
) => {
const userId = req.user?.id || req.user?.sub;
// Giới hạn mặc định
let windowMs = 15 * 60 * 1000; // 15 phút
let max = 100; // 100 requests
if (userId) {
const roles = await rbacService.getUserRoles(userId);
// Đặt giới hạn dựa trên vai trò
if (roles.includes('SUPER_ADMIN')) {
max = 1000;
} else if (roles.includes('ADMIN')) {
max = 500;
} else if (roles.includes('MODERATOR')) {
max = 300;
}
} else {
// Người dùng chưa xác thực - giới hạn nghiêm ngặt hơn
max = 50;
}
const limiter = rateLimit({
windowMs,
max,
store: new RateLimiterRedis({
client: getRedisClient(),
prefix: 'rl:'
}),
handler: (req, res) => {
res.status(429).json({
success: false,
error: {
code: 'RATE_LIMIT_EXCEEDED',
message: 'Quá nhiều yêu cầu, vui lòng thử lại sau'
}
});
}
});
limiter(req, res, next);
};
```
### Header Bảo Mật
Example from `services/iam-service/src/main.ts`:
```typescript
import helmet from 'helmet';
import cors from 'cors';
// Security middleware
app.use(helmet());
app.use(cors({
origin: appConfig.corsOrigin,
credentials: true
}));
```
Ví dụ từ `services/iam-service/src/main.ts`:
```typescript
import helmet from 'helmet';
import cors from 'cors';
// Middleware bảo mật
app.use(helmet());
app.use(cors({
origin: appConfig.corsOrigin,
credentials: true
}));
```
Traefik security headers from `infra/traefik/dynamic/middlewares.yml`:
```yaml
http:
middlewares:
secure-headers:
headers:
sslRedirect: true
stsSeconds: 31536000
contentTypeNosniff: true
browserXssFilter: true
frameDeny: true
customRequestHeaders:
X-Forwarded-Proto: "https"
```
Header bảo mật Traefik từ `infra/traefik/dynamic/middlewares.yml`:
```yaml
http:
middlewares:
secure-headers:
headers:
sslRedirect: true
stsSeconds: 31536000
contentTypeNosniff: true
browserXssFilter: true
frameDeny: true
customRequestHeaders:
X-Forwarded-Proto: "https"
```
### Ghi Log Kiểm Toán
Example from `services/iam-service/src/core/events/audit.service.ts`:
```typescript
export class AuditService {
async logAuthEvent(
eventType: string,
data: {
userId?: string;
success: boolean;
errorMessage?: string;
ipAddress?: string;
userAgent?: string;
metadata?: any;
}
): Promise<void> {
await this.prisma.authEvent.create({
data: {
userId: data.userId || null,
eventType,
eventData: data.metadata || {},
ipAddress: data.ipAddress,
userAgent: data.userAgent,
success: data.success,
errorMessage: data.errorMessage
}
});
}
}
```
Ví dụ từ `services/iam-service/src/core/events/audit.service.ts`:
```typescript
export class AuditService {
async logAuthEvent(
eventType: string,
data: {
userId?: string;
success: boolean;
errorMessage?: string;
ipAddress?: string;
userAgent?: string;
metadata?: any;
}
): Promise<void> {
await this.prisma.authEvent.create({
data: {
userId: data.userId || null,
eventType,
eventData: data.metadata || {},
ipAddress: data.ipAddress,
userAgent: data.userAgent,
success: data.success,
errorMessage: data.errorMessage
}
});
}
}
```
## Thực Hành Tốt Nhất
### Bảo Mật Mật Khẩu
Luồng hash và xác minh mật khẩu:
```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<br/>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<br/>(generic error)
end
```
- Always use bcrypt with cost factor 12+ in production
- Never log passwords or password hashes
- Use strong password requirements (min 8 chars, uppercase, lowercase, number, special char)
- Implement password reset with secure tokens
**VI**:
- Luôn sử dụng bcrypt với hệ số chi phí 12+ trong production
- Không bao giờ ghi log mật khẩu hoặc hash mật khẩu
- Sử dụng yêu cầu mật khẩu mạnh (tối thiểu 8 ký tự, chữ hoa, chữ thường, số, ký tự đặc biệt)
- Triển khai đặt lại mật khẩu với token an toàn
### Bảo Mật Token
- Hash tokens before storing in database
- Use short-lived access tokens (15 minutes)
- Use longer-lived refresh tokens (7 days) with rotation
- Store refresh tokens securely (httpOnly cookies)
- Implement token revocation
**VI**:
- Hash token trước khi lưu vào database
- Sử dụng access token có thời gian sống ngắn (15 phút)
- Sử dụng refresh token có thời gian sống dài hơn (7 ngày) với xoay vòng
- Lưu trữ refresh token an toàn (httpOnly cookies)
- Triển khai thu hồi token
### Mã Hóa Dữ Liệu
Luồng mã hóa và giải mã để bảo vệ dữ liệu nhạy cảm khi lưu trữ:
```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<br/>(16 bytes)
EncryptionService->>Crypto: Create cipher<br/>(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<br/>(AES-256-GCM)
EncryptionService->>Crypto: Set auth tag
EncryptionService->>Crypto: Decrypt ciphertext
Crypto->>EncryptionService: Plaintext
EncryptionService->>Service: Return plaintext
```
### Ngăn Chặn SQL Injection
- Always use Prisma parameterized queries (automatic)
- Never use string concatenation for queries
- Validate and sanitize all inputs
**VI**:
- Luôn sử dụng truy vấn tham số hóa của Prisma (tự động)
- Không bao giờ sử dụng nối chuỗi cho truy vấn
- Xác thực và làm sạch tất cả đầu vào
## Ví Dụ Từ Dự Án
### Middleware Xác Thực
See `services/iam-service/src/middlewares/auth.middleware.ts` for complete authentication implementation.
Xem `services/iam-service/src/middlewares/auth.middleware.ts` để có implementation xác thực hoàn chỉnh.
### Middleware RBAC
See `services/iam-service/src/middlewares/rbac.middleware.ts` for permission-based authorization.
Xem `services/iam-service/src/middlewares/rbac.middleware.ts` để có phân quyền dựa trên quyền.
### Middleware Xác Thực
See `services/iam-service/src/middlewares/validation.middleware.ts` for input validation patterns.
Xem `services/iam-service/src/middlewares/validation.middleware.ts` để có các mẫu xác thực đầu vào.
### Giới Hạn Tốc Độ
See `services/iam-service/src/middlewares/rate-limit.middleware.ts` for dynamic rate limiting.
Xem `services/iam-service/src/middlewares/rate-limit.middleware.ts` để có giới hạn tốc độ động.
## Tham Khảo Nhanh
### Danh Sách Kiểm Tra Bảo Mật
Before deploying any service:
- [ ] All endpoints require authentication (except public)
- [ ] Authorization checks implemented (RBAC/ABAC)
- [ ] Input validation with Zod schemas
- [ ] Rate limiting configured
- [ ] Error messages sanitized (no info disclosure)
- [ ] PII encrypted at rest
- [ ] Passwords hashed with bcrypt (cost 12+)
- [ ] Tokens hashed before storing
- [ ] Secrets in environment variables (never hardcoded)
- [ ] HTTPS enforced (TLS 1.2+)
- [ ] CORS configured correctly
- [ ] Security headers set (helmet)
- [ ] Audit logging enabled
- [ ] SQL injection prevented (use Prisma)
- [ ] XSS prevention (input sanitization)
- [ ] File upload validation
- [ ] Security tests passing
Trước khi triển khai bất kỳ service nào:
- [ ] Tất cả endpoint yêu cầu xác thực (trừ public)
- [ ] Kiểm tra phân quyền đã triển khai (RBAC/ABAC)
- [ ] Xác thực đầu vào với Zod schemas
- [ ] Giới hạn tốc độ đã cấu hình
- [ ] Thông báo lỗi đã được làm sạch (không tiết lộ thông tin)
- [ ] PII được mã hóa khi nghỉ
- [ ] Mật khẩu được hash với bcrypt (chi phí 12+)
- [ ] Token được hash trước khi lưu
- [ ] Bí mật trong biến môi trường (không bao giờ hardcode)
- [ ] HTTPS được bắt buộc (TLS 1.2+)
- [ ] CORS được cấu hình đúng
- [ ] Header bảo mật được đặt (helmet)
- [ ] Ghi log kiểm toán được bật
- [ ] SQL injection được ngăn chặn (sử dụng Prisma)
- [ ] Ngăn chặn XSS (làm sạch đầu vào)
- [ ] Xác thực tải lên tệp
- [ ] Kiểm tra bảo mật đã vượt qua
## Skills Liên Quan
- **[Project Rules](./project-rules.md)** - Coding standards and architecture
- **[API Design](./api-design.md)** - Secure API design patterns
- **[Testing Patterns](./testing-patterns.md)** - Security testing
## Tài Nguyên
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
**VI**:
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
- [Thực Hành Bảo Mật Node.js](https://nodejs.org/en/docs/guides/security/)
- [Thực Hành Bảo Mật Express](https://expressjs.com/en/advanced/best-practice-security.html)

View File

@@ -1,344 +0,0 @@
---
name: service-discovery-registry
description: Service discovery and registry patterns for GoodGo microservices including service registry, health check orchestration, load balancing strategies, and service mesh integration.
---
# Service Discovery & Registry Patterns
# Các Patterns Service Discovery & Registry
## When to Use This Skill / Khi Nào Sử Dụng Skill Này
Use this skill when:
Sử dụng skill này khi:
- Implementing service discovery mechanisms / Triển khai cơ chế service discovery
- Managing service registry / Quản lý service registry
- Orchestrating health checks / Điều phối health checks
- Implementing load balancing strategies / Triển khai các chiến lược load balancing
- Integrating with service mesh / Tích hợp với service mesh
- Managing dynamic service registration / Quản lý đăng ký service động
- Implementing DNS-based discovery / Triển khai discovery dựa trên DNS
- Building service catalog / Xây dựng service catalog
## Core Concepts / Khái Niệm Cốt Lõi
### Service Discovery Types / Các Loại Service Discovery
1. **Client-Side Discovery / Discovery Phía Client**: Client queries service registry / Client truy vấn service registry
2. **Server-Side Discovery / Discovery Phía Server**: Load balancer queries registry (Kubernetes DNS) / Load balancer truy vấn registry (Kubernetes DNS)
3. **Service Mesh / Service Mesh**: Automatic service discovery (Istio, Linkerd) / Service discovery tự động (Istio, Linkerd)
### Service Registry / Service Registry
- **Static Configuration / Cấu Hình Tĩnh**: Hardcoded service addresses / Địa chỉ service được hardcode
- **Dynamic Registry / Registry Động**: Services register/unregister dynamically / Services đăng ký/hủy đăng ký động
- **DNS-Based / Dựa Trên DNS**: Use DNS for service discovery (Kubernetes) / Sử dụng DNS cho service discovery (Kubernetes)
## Service Registration Flow / Luồng Đăng Ký Service
The service registration lifecycle involves startup registration, periodic heartbeats, and graceful shutdown unregistration.
Vòng đời đăng ký service bao gồm đăng ký khi khởi động, heartbeat định kỳ, và hủy đăng ký khi shutdown.
```mermaid
sequenceDiagram
participant Service as Service Instance
participant Registry as Service Registry
participant Health as Health Check
Note over Service,Registry: Service Startup
Service->>Registry: Register service info<br/>(name, url, version)
Registry->>Registry: Store service metadata
Registry-->>Service: Registration confirmed
Note over Service,Registry: Heartbeat Loop (every 30s)
loop Every 30 seconds
Service->>Health: Check own health status
Health-->>Service: Health status
Service->>Registry: Update registration<br/>(status, lastHeartbeat)
Registry->>Registry: Update service record
end
Note over Service,Registry: Service Shutdown
Service->>Registry: Unregister service
Registry->>Registry: Remove service record
Registry-->>Service: Unregistration confirmed
```
## Health Check Orchestration / Điều Phối Health Check
Health checks ensure services are available and functioning correctly. The system aggregates health status from multiple services to determine overall system health.
Health checks đảm bảo services có sẵn và hoạt động đúng. Hệ thống tổng hợp trạng thái health từ nhiều services để xác định health tổng thể của hệ thống.
```mermaid
flowchart TD
Start([Health Check Request]) --> GetServices[Get All Services from Registry]
GetServices --> CheckEach{For Each Service}
CheckEach --> CheckHealth[Check Service Health Endpoint]
CheckHealth --> HealthOK{Health OK?}
HealthOK -->|Yes| UpdateHealthy[Update Status: Healthy]
HealthOK -->|No| UpdateUnhealthy[Update Status: Unhealthy]
UpdateHealthy --> CheckTimeout{Last Heartbeat<br/>< 60s?}
UpdateUnhealthy --> CheckTimeout
CheckTimeout -->|Yes| MarkActive[Mark as Active]
CheckTimeout -->|No| MarkStale[Mark as Stale]
MarkActive --> NextService{More Services?}
MarkStale --> NextService
NextService -->|Yes| CheckEach
NextService -->|No| AggregateStatus[Aggregate Overall Status]
AggregateStatus --> CountUnhealthy[Count Unhealthy Services]
CountUnhealthy --> DetermineStatus{Unhealthy Count}
DetermineStatus -->|0| StatusHealthy[Status: Healthy]
DetermineStatus -->|< 50%| StatusDegraded[Status: Degraded]
DetermineStatus -->|>= 50%| StatusUnhealthy[Status: Unhealthy]
StatusHealthy --> ReturnResult[Return Health Status]
StatusDegraded --> ReturnResult
StatusUnhealthy --> ReturnResult
ReturnResult --> End([End])
```
## Load Balancing Strategies / Các Chiến Lược Load Balancing
Load balancing distributes requests across multiple service instances. Different strategies are used based on service characteristics and requirements.
Load balancing phân phối requests qua nhiều service instances. Các chiến lược khác nhau được sử dụng dựa trên đặc điểm và yêu cầu của service.
```mermaid
flowchart TD
Start([Incoming Request]) --> GetInstances[Get Available Service Instances]
GetInstances --> SelectStrategy{Load Balancing Strategy}
SelectStrategy -->|Round Robin| RoundRobin[Round Robin Algorithm]
SelectStrategy -->|Least Connections| LeastConn[Least Connections Algorithm]
SelectStrategy -->|Weighted| Weighted[Weighted Round Robin]
RoundRobin --> SelectNext[Select Next Instance in Order]
SelectNext --> UseInstance[Use Selected Instance]
LeastConn --> CompareConn[Compare Connection Counts]
CompareConn --> SelectMin[Select Instance with<br/>Minimum Connections]
SelectMin --> UseInstance
Weighted --> CalculateWeight[Calculate Total Weight]
CalculateWeight --> RandomSelect[Random Selection Based on Weights]
RandomSelect --> UseInstance
UseInstance --> ForwardRequest[Forward Request to Instance]
ForwardRequest --> UpdateStats[Update Statistics]
UpdateStats --> End([Request Completed])
style RoundRobin fill:#e1f5ff
style LeastConn fill:#e1f5ff
style Weighted fill:#e1f5ff
```
## Kubernetes DNS Discovery / Discovery DNS Kubernetes
Kubernetes provides built-in DNS-based service discovery. Services are automatically discoverable via DNS names.
Kubernetes cung cấp service discovery dựa trên DNS tích hợp sẵn. Services có thể được discover tự động qua tên DNS.
```typescript
// EN: Use Kubernetes DNS for service discovery
// VI: Sử dụng Kubernetes DNS cho service discovery
const serviceUrl = `http://user-service.production.svc.cluster.local`;
```
### DNS Patterns / Các Pattern DNS
- **Short form / Dạng ngắn** (same namespace / cùng namespace): `http://user-service`
- **Full form / Dạng đầy đủ** (cross-namespace / khác namespace): `http://user-service.production.svc.cluster.local`
- **With port / Với port**: `http://user-service.production.svc.cluster.local:5000`
## Service Registry Implementation / Triển Khai Service Registry
### Register Service / Đăng Ký Service
```typescript
// EN: Register service
// VI: Đăng ký service
await serviceRegistry.register({
name: 'user-service',
version: '1.0.0',
url: 'http://user-service:5000',
healthCheckUrl: 'http://user-service:5000/health',
status: 'healthy',
lastHeartbeat: new Date(),
});
```
### Discover Service / Tìm Service
```typescript
// EN: Discover service
// VI: Tìm service
const service = await serviceRegistry.discover('user-service');
if (service?.status === 'healthy') {
// EN: Use service
// VI: Sử dụng service
}
```
### List Healthy Services / Liệt Kê Services Healthy
```typescript
// EN: List all healthy services
// VI: Liệt kê tất cả services healthy
const healthyServices = await serviceRegistry.listHealthyServices();
```
## Health Check Aggregation / Tổng Hợp Health Check
Aggregate health status from multiple services to determine overall system health.
Tổng hợp trạng thái health từ nhiều services để xác định health tổng thể của hệ thống.
```typescript
// EN: Aggregate health from multiple services
// VI: Tổng hợp health từ nhiều services
const health = await healthAggregator.getAggregatedHealth();
// EN: Returns: { status: 'healthy' | 'degraded' | 'unhealthy', services: [...] }
// VI: Trả về: { status: 'healthy' | 'degraded' | 'unhealthy', services: [...] }
```
## Load Balancing Implementation / Triển Khai Load Balancing
### Round Robin / Round Robin
```typescript
const instance = loadBalancer.roundRobin(instances);
```
### Least Connections / Least Connections
```typescript
const instance = loadBalancer.leastConnections(instances);
```
### Weighted Round Robin / Weighted Round Robin
```typescript
const instance = loadBalancer.weightedRoundRobin(instances);
```
## Service Mesh Integration / Tích Hợp Service Mesh
Service mesh solutions like Istio provide automatic service discovery and advanced routing capabilities.
Các giải pháp service mesh như Istio cung cấp service discovery tự động và khả năng routing nâng cao.
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: external-service
spec:
hosts:
- external-api.example.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS
```
## Best Practices / Thực Hành Tốt
1. **Kubernetes DNS / Kubernetes DNS**: Use Kubernetes DNS for service discovery in K8s environments / Sử dụng Kubernetes DNS cho service discovery trong môi trường K8s
2. **Health Checks / Health Checks**: Implement comprehensive health checks (`/health`, `/health/live`, `/health/ready`) / Triển khai health checks toàn diện (`/health`, `/health/live`, `/health/ready`)
3. **Service Registry / Service Registry**: Use registry for dynamic services that need runtime discovery / Sử dụng registry cho các services động cần discovery tại runtime
4. **Load Balancing / Load Balancing**: Choose appropriate load balancing strategy based on service characteristics / Chọn chiến lược load balancing phù hợp dựa trên đặc điểm service
5. **Monitoring / Giám Sát**: Monitor service discovery and health check metrics / Giám sát metrics service discovery và health check
6. **Heartbeat / Heartbeat**: Implement periodic heartbeat (every 30 seconds) to keep registry updated / Triển khai heartbeat định kỳ (mỗi 30 giây) để giữ registry được cập nhật
7. **Graceful Shutdown / Shutdown Nhẹ Nhàng**: Always unregister services on shutdown to prevent stale entries / Luôn hủy đăng ký services khi shutdown để tránh entries cũ
8. **Fallback / Dự Phòng**: Provide fallback mechanisms when registry is unavailable / Cung cấp cơ chế dự phòng khi registry không khả dụng
## Common Mistakes / Các Lỗi Thường Gặp
1. **Hardcoded Service URLs / Hardcode Service URLs**: Breaks in different environments / Phá vỡ trong các môi trường khác nhau
```typescript
// ❌ BAD: Hardcoded
const url = 'http://user-service:5000';
// ✅ GOOD: Use discovery or env vars
const url = discovery.getServiceUrl('user-service');
```
2. **No Heartbeat / Không Có Heartbeat**: Stale registry entries / Entries registry cũ
```typescript
// ❌ BAD: Register once
await registry.register(service);
// ✅ GOOD: Periodic heartbeat
setInterval(() => registry.register(service), 30000);
```
3. **Missing Graceful Shutdown / Thiếu Graceful Shutdown**: Orphaned registrations / Đăng ký bị bỏ lại
```typescript
// ✅ Always unregister on shutdown
process.on('SIGTERM', async () => {
await registry.unregister(serviceName);
process.exit(0);
});
```
4. **No Fallback / Không Có Fallback**: Fails when registry unavailable / Lỗi khi registry không khả dụng
```typescript
// ❌ BAD: No fallback
const url = await registry.discover('service');
// ✅ GOOD: Fallback to default
const url = await registry.discover('service')
?? process.env.SERVICE_FALLBACK_URL;
```
## Quick Reference / Tham Khảo Nhanh
### Discovery Types / Các Loại Discovery
| Discovery Type | Implementation | Use Case |
|----------------|----------------|----------|
| **K8s DNS** | `service.namespace.svc.cluster.local` | Internal services |
| **Service Registry** | Database-backed | Dynamic services |
| **Service Mesh** | Istio/Linkerd | Complex routing |
| **Environment Vars** | `process.env.SERVICE_URL` | Simple/external |
### Health Check Endpoints / Các Endpoint Health Check
| Endpoint | Purpose |
|----------|---------|
| `/health` | Basic health |
| `/health/live` | K8s liveness probe |
| `/health/ready` | K8s readiness probe |
### Load Balancing Strategies / Các Chiến Lược Load Balancing
| Strategy | When to Use |
|----------|-------------|
| **Round Robin** | Equal capacity servers |
| **Least Connections** | Varying request durations |
| **Weighted** | Different server capacities |
### Service Registration Lifecycle / Vòng Đời Đăng Ký Service
```
Startup → Register → Heartbeat (30s) → ... → Shutdown → Unregister
```
## Resources / Tài Nguyên
- [Deployment Kubernetes](./deployment-kubernetes.md) - Kubernetes deployment patterns / Các pattern deployment Kubernetes
- [Observability & Monitoring](./observability-monitoring.md) - Health checks and monitoring / Health checks và giám sát
- [Project Rules](./project-rules.md) - GoodGo standards / Các tiêu chuẩn GoodGo
- Skill Source: `.cursor/skills/service-discovery-registry/SKILL.md`

View File

@@ -1,416 +0,0 @@
# Các Pattern Service Layer
Service layer organization and patterns for GoodGo microservices including business logic implementation, dependency injection, service composition, and separation of concerns.
> Tổ chức và các patterns của service layer cho GoodGo microservices bao gồm implement business logic, dependency injection, service composition, và separation of concerns.
## Tổng Quan
The service layer is where business logic lives in GoodGo microservices. It sits between controllers (HTTP layer) and repositories (data layer), orchestrating business operations, enforcing business rules, and coordinating data access. This guide covers service organization patterns, dependency injection, service composition, and best practices.
Service layer là nơi chứa business logic trong GoodGo microservices. Nó nằm giữa controllers (HTTP layer) và repositories (data layer), điều phối business operations, thực thi business rules, và phối hợp data access. Hướng dẫn này bao gồm các patterns tổ chức service, dependency injection, service composition, và best practices.
## Khi Nào Sử Dụng
Use service layer patterns when:
- Implementing business logic in services
- Organizing service layer code
- Using dependency injection patterns
- Composing multiple services together
- Separating concerns between controllers, services, and repositories
- Handling business rules and validations
- Implementing service composition patterns
Sử dụng service layer patterns khi:
- Implement business logic trong services
- Tổ chức service layer code
- Sử dụng dependency injection patterns
- Kết hợp nhiều services với nhau
- Tách biệt concerns giữa controllers, services, và repositories
- Xử lý business rules và validations
- Implement service composition patterns
## Khái Niệm Chính
### Kiến Trúc Service Layer
Service layer tuân theo mô hình kiến trúc ba tầng, tách biệt các concerns giữa xử lý HTTP, business logic, và truy cập dữ liệu:
```mermaid
graph TB
subgraph "HTTP Layer"
Controller["Controller<br/>- HTTP request/response<br/>- Input validation<br/>- Status codes"]
end
subgraph "Business Layer"
Service["Service<br/>- Business logic<br/>- Business rules<br/>- Orchestration<br/>- Caching<br/>- Logging"]
end
subgraph "Data Layer"
Repository["Repository<br/>- Data access<br/>- CRUD operations<br/>- Database queries"]
Database[("Database<br/>Prisma")]
end
Controller -->|"Calls"| Service
Service -->|"Uses"| Repository
Repository -->|"Queries"| Database
style Controller fill:#e1f5ff
style Service fill:#fff4e1
style Repository fill:#e8f5e9
style Database fill:#f3e5f5
```
### Trách Nhiệm Service Layer
The service layer has specific responsibilities that distinguish it from controllers and repositories.
Service layer có các trách nhiệm cụ thể phân biệt nó với controllers và repositories.
**Service Layer Responsibilities / Trách Nhiệm**:
- **Business Logic / Logic Nghiệp Vụ**: Contains domain-specific business rules and logic
- **Orchestration / Điều Phối**: Orchestrates calls to repositories and other services
- **Validation / Xác Thực**: Validates business rules (not just input validation)
- **Cross-cutting Concerns / Concerns Xuyên Suốt**: Handles caching, logging, auditing
- **Coordination / Phối Hợp**: Coordinates multiple repositories or services
- **Transport Independence / Độc Lập Transport**: Independent of HTTP transport layer
**NOT Service Responsibilities / KHÔNG Phải Trách Nhiệm**:
- HTTP request/response handling (that's controllers)
- Database queries (that's repositories)
- Authentication/authorization (that's middleware)
### Tiêm Phụ Thuộc
Services use constructor injection to receive dependencies, making them testable and loosely coupled. Luồng dependency injection cho thấy cách các components được kết nối với nhau:
```mermaid
graph LR
subgraph "Module Initialization"
Prisma[("Prisma Client")]
Cache[("Cache Service")]
end
subgraph "Dependency Creation"
Repo["UserRepository<br/>(prisma)"]
Service["UserService<br/>(repository, cache)"]
Controller["UserController<br/>(service)"]
end
subgraph "Router Setup"
Router["Router<br/>(controller methods)"]
end
Prisma -->|"Injected"| Repo
Cache -->|"Injected"| Service
Repo -->|"Injected"| Service
Service -->|"Injected"| Controller
Controller -->|"Bound to"| Router
style Prisma fill:#f3e5f5
style Cache fill:#f3e5f5
style Repo fill:#e8f5e9
style Service fill:#fff4e1
style Controller fill:#e1f5ff
style Router fill:#e1f5ff
```
**Ví Dụ Implementation:**
Services sử dụng constructor injection để nhận dependencies, làm cho chúng testable và loosely coupled:
```typescript
export class UserService {
constructor(
private userRepository: UserRepository,
private cacheService: CacheService,
private logger: Logger
) {}
}
```
## Patterns
### Luồng Request Qua Các Layer
Sơ đồ sequence sau minh họa cách một request chảy qua các layer Controller → Service → Repository:
```mermaid
sequenceDiagram
participant Client
participant Controller
participant Service
participant Cache
participant Repository
participant Database
Client->>Controller: HTTP Request (GET /users/:id)
Controller->>Controller: Validate input
Controller->>Service: getUserById(id)
Service->>Cache: Check cache
alt Cache Hit
Cache-->>Service: Return cached user
Service-->>Controller: Return user
else Cache Miss
Service->>Repository: findById(id)
Repository->>Database: Query user
Database-->>Repository: User data
Repository-->>Service: User entity
alt User Not Found
Service-->>Controller: Throw NotFoundError
Controller-->>Client: 404 Not Found
else User Found
Service->>Cache: Set cache (TTL: 5min)
Service-->>Controller: Return user
Controller-->>Client: 200 OK + User data
end
end
```
### Pattern Service Cơ Bản
Basic service implementation with repository dependency.
Implementation service cơ bản với repository dependency.
```typescript
import { logger } from '@goodgo/logger';
import { userRepository } from '../repositories/user.repository';
import { NotFoundError } from '../errors/http-error';
export class UserService {
async getUserById(id: string) {
logger.info('Fetching user by ID', { id });
const user = await userRepository.findById(id);
if (!user) {
throw new NotFoundError('User', { id });
}
return user;
}
async createUser(data: CreateUserInput) {
logger.info('Creating user', { email: data.email });
const user = await userRepository.create(data);
logger.info('User created', { userId: user.id });
return user;
}
}
```
**Tham Khảo**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
### Service Với Caching
Services implement caching to improve performance for frequently accessed data.
Services implement caching để cải thiện hiệu suất cho dữ liệu thường xuyên truy cập.
```typescript
export class UserService {
constructor(
private repository: UserRepository,
private cacheService: CacheService
) {}
async getUserById(id: string) {
const cacheKey = cacheService.keys.user(id);
// Thử cache trước
const cached = await this.cacheService.get<User>(cacheKey);
if (cached) return cached;
// Cache miss - fetch từ repository
const user = await this.repository.findById(id);
if (!user) {
throw new NotFoundError('User');
}
// Cache trong 5 phút
await this.cacheService.set(cacheKey, user, 300);
return user;
}
}
```
**Tham Khảo**: [`services/iam-service/src/modules/rbac/rbac.service.ts`](../../../services/iam-service/src/modules/rbac/rbac.service.ts)
### Kết Hợp Services
Services can depend on other services to compose complex operations. Sơ đồ sau minh họa service composition:
```mermaid
graph TB
subgraph "AccessRequestService"
ARService["AccessRequestService"]
ARRepo["AccessRequestRepository"]
end
subgraph "Dependencies"
UserService["UserService"]
RBACService["RBACService"]
end
subgraph "Supporting Services"
UserRepo["UserRepository"]
RBACRepo["RBACRepository"]
end
ARService -->|"Uses"| UserService
ARService -->|"Uses"| RBACService
ARService -->|"Uses"| ARRepo
UserService -->|"Uses"| UserRepo
RBACService -->|"Uses"| RBACRepo
style ARService fill:#fff4e1
style UserService fill:#fff4e1
style RBACService fill:#fff4e1
style ARRepo fill:#e8f5e9
style UserRepo fill:#e8f5e9
style RBACRepo fill:#e8f5e9
```
**Ví Dụ Implementation:**
Services có thể phụ thuộc vào các services khác để kết hợp các operations phức tạp:
```typescript
export class AccessRequestService {
constructor(
private accessRequestRepository: AccessRequestRepository,
private userService: UserService,
private rbacService: RBACService
) {}
async createRequest(userId: string, data: CreateRequestDto) {
// Sử dụng các services khác
const user = await this.userService.getUserById(userId);
const hasPermission = await this.rbacService.checkPermission(userId, 'CREATE_REQUEST');
if (!hasPermission) {
throw new ForbiddenError('Insufficient permissions');
}
return await this.accessRequestRepository.create({ ...data, userId });
}
}
```
### Xác Thực Business Logic
Services validate business rules beyond simple input validation.
Services validate business rules vượt quá input validation đơn giản.
```typescript
export class UserService {
async createUser(data: CreateUserInput) {
// Business rule: Kiểm tra email đã tồn tại
const existing = await this.repository.findByEmail(data.email);
if (existing) {
throw new ConflictError('User with this email already exists');
}
// Business rule: Validate organization
if (data.organizationId) {
const org = await this.orgRepository.findById(data.organizationId);
if (!org) {
throw new NotFoundError('Organization');
}
}
return await this.repository.create(data);
}
}
```
### Pattern Service Module
Organize services into modules with controllers and routers.
Tổ chức services thành modules với controllers và routers.
```typescript
export class FeatureModule {
private controller: FeatureController;
private service: FeatureService;
private router: Router;
constructor() {
const repository = new FeatureRepository(prisma);
this.service = new FeatureService(repository);
this.controller = new FeatureController(this.service);
this.router = this.createRouter();
}
getRouter(): Router {
return this.router;
}
private createRouter(): Router {
const router = Router();
router.get('/', asyncHandler(this.controller.findAll.bind(this.controller)));
router.post('/', asyncHandler(this.controller.create.bind(this.controller)));
return router;
}
}
```
**Tham Khảo**: [`services/iam-service/src/modules/feature/feature.module.ts`](../../../services/iam-service/src/modules/feature/feature.module.ts)
## Thực Hành Tốt Nhất
1. **Single Responsibility / Trách Nhiệm Đơn**: Mỗi service xử lý một domain area
2. **Dependency Injection / Tiêm Phụ Thuộc**: Sử dụng constructor injection để dễ test
3. **Business Logic Only / Chỉ Business Logic**: Giữ HTTP concerns trong controllers
4. **Use Repositories / Sử Dụng Repositories**: Không truy cập database trực tiếp
5. **Error Handling / Xử Lý Lỗi**: Throw appropriate domain errors
6. **Logging / Ghi Log**: Log các operations quan trọng và errors
7. **Caching / Caching**: Implement caching trong services, không phải repositories
8. **Composition / Kết Hợp**: Kết hợp services cho complex operations
9. **Pure Functions / Functions Thuần**: Giữ service methods pure khi có thể
10. **Transaction Coordination / Phối Hợp Transaction**: Coordinate transactions cho multi-step operations
## Lỗi Thường Gặp
1. **HTTP in Services / HTTP Trong Services**: Sử dụng `req`/`res` trong services
2. **Direct Database Access / Truy Cập Database Trực Tiếp**: Truy cập Prisma trực tiếp thay vì repositories
3. **Too Many Responsibilities / Quá Nhiều Trách Nhiệm**: Service làm quá nhiều việc
4. **No Error Handling / Không Xử Lý Lỗi**: Không throw appropriate errors
5. **Business Logic in Controllers / Business Logic Trong Controllers**: Đưa business logic vào controllers
6. **Tight Coupling / Kết Hợp Chặt**: Services tightly coupled với specific implementations
7. **Missing Validation / Thiếu Validation**: Không validate business rules
## Xử Lý Sự Cố
### Phụ Thuộc Vòng Tròn
**Problem / Vấn Đề**: Circular dependencies giữa services
**Solution / Giải Pháp**:
- Refactor để loại bỏ circular dependencies
- Extract shared logic vào common service hoặc utility
- Use dependency injection properly
### Service Quá Lớn
**Problem / Vấn Đề**: Service class quá lớn với quá nhiều methods
**Solution / Giải Pháp**:
- Split service thành multiple smaller services
- Extract related functionality vào separate services
- Use service composition instead of one large service
## Tài Nguyên
- [Feature Service](../../../services/iam-service/src/modules/feature/feature.service.ts) - Example service implementation
- [User Service](../../../services/iam-service/src/modules/identity/user/user.service.ts) - User management service
- [RBAC Service](../../../services/iam-service/src/modules/rbac/rbac.service.ts) - Role-based access control service
- [Repository Pattern](./repository-pattern.md) - Repository patterns
- [Caching Patterns](./caching-patterns.md) - Caching in services
- [Error Handling](./error-handling-patterns.md) - Error handling patterns

View File

@@ -1,841 +0,0 @@
# Các Pattern Testing
Comprehensive testing best practices for GoodGo microservices including unit tests, integration tests, E2E tests, Jest configuration, mocking strategies, and debugging techniques.
> Các thực hành tốt nhất về testing cho microservices GoodGo bao gồm unit tests, integration tests, E2E tests, cấu hình Jest, strategies mocking, và kỹ thuật debugging.
## Tổng Quan
Testing is a critical component of the GoodGo microservices platform. This guide provides comprehensive patterns and best practices for writing maintainable, reliable tests across all services. It covers test types, Jest configuration, mocking strategies, test utilities, and debugging techniques that ensure code quality and reliability.
Testing là thành phần quan trọng của nền tảng microservices GoodGo. Hướng dẫn này cung cấp các patterns và best practices toàn diện để viết các test có thể bảo trì và đáng tin cậy trên tất cả các services. Nó bao gồm các loại test, cấu hình Jest, strategies mocking, test utilities, và kỹ thuật debugging để đảm bảo chất lượng và độ tin cậy của code.
## Khi Nào Sử Dụng
Use these testing patterns when:
- Writing unit tests for services, controllers, or repositories
- Creating integration tests for middleware chains
- Building E2E tests for API endpoints
- Setting up Jest configuration for a new service
- Mocking external dependencies (Prisma, Redis, Auth SDK)
- Debugging test failures
- Improving test coverage
- Creating test utilities and factories
Sử dụng các testing patterns này khi:
- Viết unit tests cho services, controllers, hoặc repositories
- Tạo integration tests cho middleware chains
- Xây dựng E2E tests cho API endpoints
- Thiết lập cấu hình Jest cho service mới
- Mocking các dependencies bên ngoài (Prisma, Redis, Auth SDK)
- Debugging test failures
- Cải thiện test coverage
- Tạo test utilities và factories
## Khái Niệm Chính
### Các Loại Test
#### Unit Tests
Test individual functions or classes in isolation with all dependencies mocked.
Test các functions hoặc classes riêng lẻ một cách cô lập với tất cả dependencies được mock.
**Characteristics / Đặc điểm**:
- **Location / Vị trí**: Next to source files (`*.test.ts`) or in `__tests__/` directory
- **Scope / Phạm vi**: Single function, method, or class
- **Dependencies / Dependencies**: All external dependencies mocked
- **Speed / Tốc độ**: Fast (<1s per test)
- **Purpose / Mục đích**: Verify logic correctness
**Example / Ví dụ**: [`services/iam-service/src/modules/feature/__tests__/feature.service.test.ts`](../../../services/iam-service/src/modules/feature/__tests__/feature.service.test.ts)
#### Integration Tests
Test multiple components working together with some real dependencies.
Test nhiều components làm việc cùng nhau với một số dependencies thật.
**Characteristics / Đặc điểm**:
- **Location / Vị trí**: `__tests__/` directory (`*.test.ts`)
- **Scope / Phạm vi**: Multiple components interacting
- **Dependencies / Dependencies**: Mix of real and mocked dependencies
- **Speed / Tốc độ**: Medium (1-5s per test)
- **Purpose / Mục đích**: Verify component integration
**Example / Ví dụ**: [`services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts`](../../../services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts)
#### E2E Tests
Test complete request/response cycles through the entire application stack.
Test các chu kỳ request/response hoàn chỉnh qua toàn bộ application stack.
**Characteristics / Đặc điểm**:
- **Location / Vị trí**: `__tests__/*.e2e.ts`
- **Scope / Phạm vi**: Full API workflow from HTTP request to response
- **Dependencies / Dependencies**: Test database, mocked external services
- **Speed / Tốc độ**: Slow (5-10s per test)
- **Purpose / Mục đích**: Verify end-to-end functionality
**Example / Ví dụ**: [`services/iam-service/src/__tests__/feature.e2e.ts`](../../../services/iam-service/src/__tests__/feature.e2e.ts)
### Testing Pyramid / Kim Tự Tháp Testing
The testing pyramid illustrates the recommended distribution of test types. Most tests should be fast unit tests, with fewer integration tests, and the fewest E2E tests.
Kim tự tháp testing minh họa phân bố được khuyến nghị của các loại test. Hầu hết tests nên là unit tests nhanh, với ít integration tests hơn, và ít E2E tests nhất.
```mermaid
graph TD
subgraph pyramid["Testing Pyramid"]
E2E["E2E Tests<br/>Fewest<br/>5-10s each<br/>Full stack"]
Integration["Integration Tests<br/>Medium<br/>1-5s each<br/>Components"]
Unit["Unit Tests<br/>Most<br/>&lt;1s each<br/>Isolated"]
end
Unit --> Integration
Integration --> E2E
style E2E fill:#e1f5ff
style Integration fill:#b3e5fc
style Unit fill:#81d4fa
```
**Key Principles / Nguyên Tắc Chính:**
- **Unit Tests (Base) / Unit Tests (Nền)**: Many fast tests covering individual functions/classes / Nhiều tests nhanh bao phủ các functions/classes riêng lẻ
- **Integration Tests (Middle) / Integration Tests (Giữa)**: Fewer tests verifying component interactions / Ít tests hơn xác minh tương tác giữa các components
- **E2E Tests (Top) / E2E Tests (Đỉnh)**: Fewest tests validating complete workflows / Ít tests nhất xác thực các workflows hoàn chỉnh
## Cấu Hình Jest
### Cấu Hình Chuẩn
The standard Jest configuration for GoodGo services includes TypeScript support, coverage thresholds, and proper test environment setup.
**Location / Vị trí**: [`services/iam-service/jest.config.ts`](../../../services/iam-service/jest.config.ts)
```typescript
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: [
'**/__tests__/**/*.test.ts',
'**/__tests__/**/*.spec.ts',
'**/__tests__/**/*.e2e.ts',
'**/?(*.)+(spec|test).ts'
],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/main.ts',
'!src/config/**/*.ts',
'!src/**/*.config.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
coverageThreshold: {
global: {
branches: 70,
functions: 70,
lines: 70,
statements: 70
}
},
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setupTests.ts'],
testTimeout: 10000,
clearMocks: true,
resetModules: true
};
export default config;
```
### Điểm Cấu Hình Quan Trọng
- **preset: 'ts-jest'**: Enables TypeScript support / Bật hỗ trợ TypeScript
- **testEnvironment: 'node'**: Sets Node.js environment (not browser) / Thiết lập môi trường Node.js (không phải browser)
- **coverageThreshold**: Enforces minimum 70% coverage across all metrics / Yêu cầu tối thiểu 70% coverage trên tất cả các metrics
- **setupFilesAfterEnv**: Loads test setup file before each test suite / Tải file setup test trước mỗi test suite
- **clearMocks**: Automatically clears mock calls between tests / Tự động xóa mock calls giữa các test
- **resetModules**: Resets module cache for test isolation / Reset module cache để cô lập test
## Files Thiết Lập
### File Thiết Lập Test
The setup file (`setupTests.ts`) configures global mocks and test utilities that are available across all tests.
**Location / Vị trí**: [`services/iam-service/src/__tests__/setupTests.ts`](../../../services/iam-service/src/__tests__/setupTests.ts)
**Key Features / Tính Năng Chính**:
- Mock external services (logger, tracing, database, Redis) / Mock các services bên ngoài
- Configure test environment variables / Cấu hình biến môi trường test
- Set up global test utilities / Thiết lập test utilities toàn cục
- Initialize Prometheus mocks / Khởi tạo Prometheus mocks
**Example / Ví dụ**:
```typescript
import { jest } from '@jest/globals';
Mock environment variables for tests
// VI: Mock biến môi trường cho tests
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL = 'postgresql://test:test@localhost:5432/test_db';
process.env.REDIS_URL = 'redis://localhost:6379/1';
Mock external services to avoid real network calls
// VI: Mock các service bên ngoài để tránh gọi mạng thật
jest.mock('@goodgo/logger', () => ({
logger: {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
},
}));
Global test utilities
// VI: Utilities test toàn cục
global.testUtils = {
createMockReq: (overrides = {}) => ({
body: {},
params: {},
query: {},
headers: {},
...overrides,
}),
createMockRes: () => {
const res: any = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
res.send = jest.fn().mockReturnValue(res);
return res;
},
createMockNext: () => jest.fn(),
};
```
### Test Execution Flow / Luồng Thực Thi Test
The test execution flow shows the lifecycle hooks and execution order in Jest test suites.
Luồng thực thi test cho thấy các lifecycle hooks và thứ tự thực thi trong các test suites của Jest.
```mermaid
flowchart TD
Start([Test Suite Starts]) --> BeforeAll[beforeAll<br/>Run once before all tests]
BeforeAll --> BeforeEach[beforeEach<br/>Run before each test]
BeforeEach --> Test[Execute Test<br/>Arrange-Act-Assert]
Test --> AfterEach[afterEach<br/>Run after each test]
AfterEach --> MoreTests{More Tests?}
MoreTests -->|Yes| BeforeEach
MoreTests -->|No| AfterAll[afterAll<br/>Run once after all tests]
AfterAll --> End([Test Suite Ends])
style Start fill:#c8e6c9
style End fill:#c8e6c9
style Test fill:#fff9c4
style BeforeAll fill:#e1bee7
style AfterAll fill:#e1bee7
style BeforeEach fill:#bbdefb
style AfterEach fill:#bbdefb
```
**Execution Order / Thứ Tự Thực Thi:**
1. `beforeAll`: Setup that runs once for the entire suite (e.g., database connection) / Setup chạy một lần cho toàn bộ suite (ví dụ: kết nối database)
2. `beforeEach`: Setup that runs before each test (e.g., reset mocks, create test data) / Setup chạy trước mỗi test (ví dụ: reset mocks, tạo test data)
3. **Test Execution / Thực Thi Test**: The actual test code following AAA pattern / Code test thực tế tuân theo pattern AAA
4. `afterEach`: Cleanup after each test (e.g., clear mocks, reset state) / Dọn dẹp sau mỗi test (ví dụ: clear mocks, reset state)
5. `afterAll`: Final cleanup after all tests (e.g., close database connection) / Dọn dẹp cuối cùng sau tất cả tests (ví dụ: đóng kết nối database)
## Các Pattern Thường Dùng
### Pattern Unit Test
**Structure / Cấu trúc**: Follow the AAA (Arrange-Act-Assert) pattern for clarity / Tuân theo pattern AAA để rõ ràng.
```typescript
describe('FeatureService', () => {
let service: FeatureService;
let mockRepository: any;
beforeEach(() => {
Clear all mocks before each test
// VI: Xóa tất cả mocks trước mỗi test
jest.clearAllMocks();
mockRepository = {
findById: jest.fn(),
create: jest.fn(),
};
service = new FeatureService(mockRepository);
});
describe('create', () => {
it('should create a feature successfully', async () => {
Arrange
// VI: Chuẩn bị
const testData = { name: 'test-feature', title: 'Test Feature' };
const mockFeature = {
id: 'test-id',
name: testData.name,
// ... other fields
};
mockRepository.create.mockResolvedValue(mockFeature);
Act
// VI: Thực hiện
const result = await service.create(testData);
Assert
// VI: Kiểm tra
expect(mockRepository.create).toHaveBeenCalledWith(testData);
expect(result).toEqual(mockFeature);
});
});
});
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/modules/feature/__tests__/feature.service.test.ts`](../../../services/iam-service/src/modules/feature/__tests__/feature.service.test.ts)
### Pattern Integration Test
Integration tests verify that multiple components work together correctly / Integration tests xác minh nhiều components làm việc cùng nhau đúng cách.
```typescript
describe('Auth Middleware', () => {
const mockNext = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('should authenticate valid token and attach user to request', () => {
Arrange
// VI: Chuẩn bị
const mockReq = createMockReq({
headers: { authorization: 'Bearer valid-token' },
});
const mockRes = createMockRes();
Act
// VI: Thực hiện
const middleware = authenticate({ secret: jwtSecret });
middleware(mockReq as Request, mockRes as Response, mockNext);
Assert
// VI: Kiểm tra
expect(mockNext).toHaveBeenCalled();
expect(mockReq.user).toBeDefined();
});
});
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts`](../../../services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts)
### Pattern E2E Test
E2E tests use supertest to test complete HTTP request/response cycles / E2E tests sử dụng supertest để test các chu kỳ HTTP request/response hoàn chỉnh.
```typescript
import request from 'supertest';
import express from 'express';
import { createRouter } from '../routes';
describe('Feature Endpoints E2E', () => {
let app: express.Application;
beforeAll(() => {
Set up test environment
// VI: Thiết lập môi trường test
process.env.NODE_ENV = 'test';
app = express();
app.use(express.json());
app.use(createRouter());
});
describe('POST /api/v1/features', () => {
it('should create a feature successfully', async () => {
Arrange
// VI: Chuẩn bị
const featureData = {
name: 'test-feature',
title: 'Test Feature',
description: 'A test feature for E2E testing'
};
Act
// VI: Thực hiện
const response = await request(app)
.post('/api/v1/features')
.send(featureData)
.expect(201);
Assert
// VI: Kiểm tra
expect(response.body).toMatchObject({
success: true,
message: 'Feature created successfully / Feature đã được tạo thành công',
});
});
});
});
```
**Real Example / Ví dụ Thực tế**: [`services/iam-service/src/__tests__/feature.e2e.ts`](../../../services/iam-service/src/__tests__/feature.e2e.ts)
## Chiến Lược Mocking
### Mocking Strategy by Test Type / Chiến Lược Mocking Theo Loại Test
Different test types require different levels of mocking. This diagram shows what should be mocked at each test level.
Các loại test khác nhau yêu cầu mức độ mocking khác nhau. Biểu đồ này cho thấy những gì nên được mock ở mỗi mức test.
```mermaid
graph TB
subgraph UnitTests["Unit Tests"]
UnitCode[Application Code]
UnitMock[All Dependencies Mocked<br/>- Database<br/>- External APIs<br/>- Services<br/>- Repositories]
UnitCode --> UnitMock
end
subgraph IntegrationTests["Integration Tests"]
IntCode[Application Code]
IntReal[Real Internal Components]
IntMock[External Dependencies Mocked<br/>- External APIs<br/>- Third-party Services]
IntCode --> IntReal
IntReal --> IntMock
end
subgraph E2ETests["E2E Tests"]
E2ECode[Application Code]
E2EReal[Real Internal Stack<br/>- Database<br/>- Services<br/>- Middleware]
E2EMock[Only External Services Mocked<br/>- Payment APIs<br/>- Email Services<br/>- Third-party APIs]
E2ECode --> E2EReal
E2EReal --> E2EMock
end
style UnitTests fill:#ffcdd2
style IntegrationTests fill:#fff9c4
style E2ETests fill:#c8e6c9
style UnitMock fill:#f8bbd0
style IntMock fill:#ffe082
style E2EMock fill:#a5d6a7
```
**Mocking Guidelines / Hướng Dẫn Mocking:**
- **Unit Tests**: Mock all external dependencies to test logic in isolation / Mock tất cả dependencies bên ngoài để test logic một cách cô lập
- **Integration Tests**: Use real internal components, mock only external services / Sử dụng các components nội bộ thật, chỉ mock các services bên ngoài
- **E2E Tests**: Use real database and internal stack, mock only third-party external services / Sử dụng database thật và internal stack, chỉ mock các services bên ngoài của bên thứ ba
### Mock Database Prisma
Mock Prisma client to avoid real database connections in unit tests / Mock Prisma client để tránh kết nối database thật trong unit tests.
```typescript
// In setupTests.ts or individual test files
jest.mock('../config/database.config', () => ({
connectDatabase: jest.fn(),
prisma: {
$queryRaw: jest.fn(),
$disconnect: jest.fn(),
feature: {
create: jest.fn(),
findMany: jest.fn(),
findUnique: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
},
},
}));
// Usage in tests
const { prisma } = require('../config/database.config');
test('should create user', async () => {
prisma.feature.create.mockResolvedValue({
id: 'test-id',
name: 'test-feature',
// ... other fields
});
const result = await createFeature({ name: 'test-feature' });
expect(result).toBeDefined();
});
```
### Mock Redis
Mock Redis client for caching and session management tests / Mock Redis client cho các test caching và quản lý session.
```typescript
jest.mock('../config/redis.config', () => ({
getRedisClient: jest.fn().mockReturnValue({
call: jest.fn(),
connect: jest.fn(),
disconnect: jest.fn(),
}),
}));
```
### Mock External APIs
Mock external HTTP calls using Jest mocks / Mock các HTTP calls bên ngoài sử dụng Jest mocks.
```typescript
jest.mock('axios');
import axios from 'axios';
const mockedAxios = axios as jest.Mocked<typeof axios>;
test('should fetch external data', async () => {
mockedAxios.get.mockResolvedValue({
data: { result: 'success' }
});
const result = await fetchExternalData();
expect(result).toEqual({ result: 'success' });
});
```
### Mock Auth SDK
Mock authentication SDK functions for middleware and service tests / Mock các functions của authentication SDK cho middleware và service tests.
```typescript
jest.mock('@goodgo/auth-sdk', () => ({
createToken: jest.fn(),
verifyToken: jest.fn(),
extractTokenFromHeader: jest.fn(),
}));
// Setup mock implementations
(verifyToken as jest.Mock).mockImplementation((token) => {
if (token === 'valid-token') {
return { userId: '123', role: 'user' };
}
throw new Error('Invalid token');
});
```
## Utilities Test
### Pattern Test Factory
Create reusable test data factories for consistent test data / Tạo các factories dữ liệu test có thể tái sử dụng để có dữ liệu test nhất quán.
```typescript
export class TestFactory {
static createUser(overrides = {}) {
return {
id: 'test-user-1',
email: 'test@example.com',
name: 'Test User',
createdAt: new Date(),
...overrides
};
}
static createAuthToken(userId: string) {
return jwt.sign({ userId }, 'test-secret');
}
static async cleanDatabase() {
await prisma.user.deleteMany();
await prisma.feature.deleteMany();
}
}
// Usage
const user = TestFactory.createUser({ name: 'Custom Name' });
const token = TestFactory.createAuthToken(user.id);
```
### Helpers Mock Request/Response
Use global test utilities for creating mock Express request/response objects / Sử dụng test utilities toàn cục để tạo các objects mock Express request/response.
```typescript
// Available via global.testUtils (from setupTests.ts)
const mockReq = global.testUtils.createMockReq({
headers: { authorization: 'Bearer token' },
params: { id: '123' },
});
const mockRes = global.testUtils.createMockRes();
const mockNext = global.testUtils.createMockNext();
```
## Các Tình Huống Test Thường Gặp
### Test Xử Lý Lỗi
```typescript
it('should handle database errors gracefully', async () => {
Arrange - Mock database error
// VI: Chuẩn bị - Mock lỗi database
prismaMock.user.findUnique.mockRejectedValue(
new Error('Database connection failed')
);
Act & Assert
// VI: Thực hiện & Kiểm tra
await expect(userService.findById('123')).rejects.toThrow();
// Or for HTTP endpoints
const response = await request(app)
.get('/api/users/123')
.expect(500);
expect(response.body).toEqual({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'Internal server error / Lỗi máy chủ nội bộ',
},
});
});
```
### Test Validation
```typescript
describe('Validation', () => {
it('should reject invalid email', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({ email: 'invalid-email', password: '123456' })
.expect(400);
expect(response.body.error.code).toBe('VALIDATION_ERROR');
});
});
```
### Test Authorization
```typescript
it('should deny access for user with incorrect role', () => {
const mockReq = createMockReq({
user: { userId: 'user-123', role: 'user' },
});
const middleware = authorize('admin');
middleware(mockReq as Request, mockRes as Response, mockNext);
expect(mockNext).not.toHaveBeenCalled();
expect(mockStatus).toHaveBeenCalledWith(403);
});
```
## Lệnh Test
Add these scripts to `package.json` / Thêm các scripts này vào `package.json`:
```json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:unit": "jest --testPathPattern=\\.test\\.ts$",
"test:e2e": "jest --testPathPattern=\\.e2e\\.ts$",
"test:ci": "jest --coverage --silent --maxWorkers=2"
}
}
```
**Usage / Sử dụng**:
- `pnpm test`: Run all tests / Chạy tất cả tests
- `pnpm test:watch`: Run tests in watch mode / Chạy tests ở chế độ watch
- `pnpm test:coverage`: Generate coverage report / Tạo báo cáo coverage
- `pnpm test:unit`: Run only unit tests / Chỉ chạy unit tests
- `pnpm test:e2e`: Run only E2E tests / Chỉ chạy E2E tests
- `pnpm test:ci`: Run tests optimized for CI/CD / Chạy tests tối ưu cho CI/CD
## Debugging Tests
### Cấu Hình Debug VS Code
Create `.vscode/launch.json` for debugging tests / Tạo `.vscode/launch.json` để debug tests:
```json
{
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"runtimeExecutable": "npm",
"runtimeArgs": ["test", "--", "--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
```
### Mẹo Debug
1. **Use `test.only()`** to run a single test / Sử dụng `test.only()` để chạy một test duy nhất:
```typescript
it.only('should test specific behavior', () => {
// Only this test will run
});
```
2. **Use `--detectOpenHandles`** for async issues / Sử dụng `--detectOpenHandles` cho các vấn đề async:
```bash
pnpm test --detectOpenHandles
```
3. **Use `--runInBand`** for sequential execution / Sử dụng `--runInBand` cho thực thi tuần tự:
```bash
pnpm test --runInBand
```
4. **Add temporary console.log** / Thêm console.log tạm thời:
```typescript
console.log('Debug value:', someValue);
```
5. **Use debugger breakpoints** in VS Code / Sử dụng breakpoints debugger trong VS Code
## Thực Hành Tốt Nhất
### Tổ Chức Test
- ✅ Each test is independent and isolated / Mỗi test độc lập và cô lập
- ✅ Tests follow AAA pattern (Arrange-Act-Assert) / Tests tuân theo pattern AAA
- ✅ Use descriptive test names that explain what is being tested / Sử dụng tên test mô tả giải thích điều đang được test
- ✅ Group related tests using `describe` blocks / Nhóm các test liên quan sử dụng `describe` blocks
- ✅ Use `beforeEach`/`afterEach` for setup/cleanup / Sử dụng `beforeEach`/`afterEach` cho setup/cleanup
### Mocking
- ✅ Mock external dependencies (database, APIs, services) / Mock các dependencies bên ngoài
- ✅ Use `jest.clearAllMocks()` in `beforeEach` to reset mocks / Sử dụng `jest.clearAllMocks()` trong `beforeEach` để reset mocks
- ✅ Verify mock calls to ensure correct behavior / Xác minh mock calls để đảm bảo hành vi đúng
- ✅ Keep mocks simple and focused / Giữ mocks đơn giản và tập trung
### Coverage
- ✅ Maintain >70% code coverage (as per Jest config) / Duy trì >70% code coverage (theo cấu hình Jest)
- ✅ Focus on covering critical business logic / Tập trung vào bao phủ logic nghiệp vụ quan trọng
- ✅ Don't sacrifice test quality for coverage percentage / Không hy sinh chất lượng test cho phần trăm coverage
- ✅ Review coverage reports regularly / Xem xét báo cáo coverage thường xuyên
### Dữ Liệu Test
- ✅ Use factories for creating test data / Sử dụng factories để tạo dữ liệu test
- ✅ Keep test data realistic and representative / Giữ dữ liệu test thực tế và đại diện
- ✅ Clean up test data after tests (if using real database) / Dọn dẹp dữ liệu test sau tests (nếu dùng database thật)
- ✅ Use meaningful test values, not just `'test'` or `123` / Sử dụng giá trị test có ý nghĩa, không chỉ `'test'` hoặc `123`
### Test Lỗi
- ✅ Test both success and error scenarios / Test cả kịch bản thành công và lỗi
- ✅ Test edge cases and boundary conditions / Test edge cases và điều kiện biên
- ✅ Test validation errors / Test lỗi validation
- ✅ Test error messages and error codes / Test thông báo lỗi và mã lỗi
### Hiệu Suất
- ✅ Keep unit tests fast (<1s each) / Giữ unit tests nhanh (<1s mỗi test)
- ✅ Avoid unnecessary async operations in unit tests / Tránh các thao tác async không cần thiết trong unit tests
- ✅ Use `--maxWorkers` in CI for parallel execution / Sử dụng `--maxWorkers` trong CI cho thực thi song song
- ✅ Don't test implementation details, test behavior / Không test chi tiết implementation, test hành vi
## Ví Dụ Từ Dự Án
### Ví Dụ Test Thực Tế
1. **Unit Test**: [`services/iam-service/src/modules/feature/__tests__/feature.service.test.ts`](../../../services/iam-service/src/modules/feature/__tests__/feature.service.test.ts)
2. **Integration Test**: [`services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts`](../../../services/iam-service/src/middlewares/__tests__/auth.middleware.test.ts)
3. **E2E Test**: [`services/iam-service/src/__tests__/feature.e2e.ts`](../../../services/iam-service/src/__tests__/feature.e2e.ts)
4. **Setup File**: [`services/iam-service/src/__tests__/setupTests.ts`](../../../services/iam-service/src/__tests__/setupTests.ts)
5. **Jest Config**: [`services/iam-service/jest.config.ts`](../../../services/iam-service/jest.config.ts)
### Ví Dụ Cấu Trúc Test
- **Repository Tests**: [`services/iam-service/src/modules/feature/__tests__/feature.repository.test.ts`](../../../services/iam-service/src/modules/feature/__tests__/feature.repository.test.ts)
- **Controller Tests**: [`services/iam-service/src/modules/health/__tests__/health.controller.test.ts`](../../../services/iam-service/src/modules/health/__tests__/health.controller.test.ts)
- **Middleware Tests**: [`services/iam-service/src/middlewares/__tests__/correlation.middleware.test.ts`](../../../services/iam-service/src/middlewares/__tests__/correlation.middleware.test.ts)
## Tham Khảo Nhanh
### Cây Quyết Định Loại Test
```
Need to test complete HTTP flow?
├─ Yes → E2E Test (*.e2e.ts)
└─ No → Multiple components interacting?
├─ Yes → Integration Test (*.test.ts)
└─ No → Unit Test (*.test.ts)
```
### Các Matcher Jest Thường Dùng
| Matcher | Purpose / Mục đích | Example / Ví dụ |
|---------|-------------------|-----------------|
| `toBe()` | Exact equality / So sánh chính xác | `expect(value).toBe(5)` |
| `toEqual()` | Deep equality / So sánh sâu | `expect(obj).toEqual({a: 1})` |
| `toMatchObject()` | Partial match / So sánh một phần | `expect(obj).toMatchObject({a: 1})` |
| `toHaveBeenCalled()` | Function called / Function đã được gọi | `expect(mockFn).toHaveBeenCalled()` |
| `toHaveBeenCalledWith()` | Called with args / Gọi với args | `expect(mockFn).toHaveBeenCalledWith('arg')` |
| `toThrow()` | Throws error / Ném lỗi | `expect(() => fn()).toThrow()` |
| `toBeDefined()` | Not undefined / Không undefined | `expect(value).toBeDefined()` |
| `toBeNull()` | Is null / Là null | `expect(value).toBeNull()` |
| `toContain()` | Array/string contains / Chứa trong array/string | `expect(array).toContain('item')` |
### Helpers Mock Function
```typescript
// Create mock
const mockFn = jest.fn();
// Set return value
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockRejectedValue(new Error('error'));
// Set implementation
mockFn.mockImplementation((arg) => arg * 2);
// Clear/reset
mockFn.mockClear(); // Clear call history
mockFn.mockReset(); // Reset to initial state
jest.clearAllMocks(); // Clear all mocks
```
## Skills Liên Quan
- **[Comment Code](./comment-code.md)**: Writing bilingual comments in tests / Viết comments song ngữ trong tests
- **[Security](./security.md)**: Testing security-critical code / Test code bảo mật quan trọng
- **[API Design](./api-design.md)**: Testing API endpoints and responses / Test API endpoints và responses
- **[Project Rules](./project-rules.md)**: Code organization for tests / Tổ chức code cho tests
## Tài Nguyên
### Tài Liệu Chính Thức
- [Jest Documentation](https://jestjs.io/docs/getting-started)
- [Supertest Documentation](https://github.com/visionmedia/supertest)
- [jest-mock-extended](https://github.com/marchaos/jest-mock-extended)
### Tài Liệu Nội Bộ
- [Service Development Guide](../guides/development.md)
- [Local Development Setup](../guides/local-development.md)
- [Troubleshooting Guide](../guides/troubleshooting.md)
### Công Cụ
- **Jest**: JavaScript testing framework / Framework testing JavaScript
- **Supertest**: HTTP assertion library / Thư viện assertion HTTP
- **jest-mock-extended**: Enhanced mocking for TypeScript / Mock nâng cao cho TypeScript
- **ioredis-mock**: Redis mock for testing / Mock Redis cho testing