chore: Remove outdated documentation files

- Deleted several obsolete documentation files related to API design, API gateway patterns, API 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 helps streamline the documentation and focus on current practices and standards, ensuring that only relevant and up-to-date information is maintained.
This commit is contained in:
Ho Ngoc Hai
2026-01-14 10:27:15 +07:00
parent 4d69684fa4
commit d09b84e2e0
26 changed files with 0 additions and 7509 deletions

View File

@@ -1,171 +0,0 @@
---
trigger: always_on
---
# 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
## 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
## URL Structure
```
https://api.goodgo.com/v1/{resource}/{id}/{sub-resource}
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
```
## 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
```typescript
// Success
interface SuccessResponse<T> {
success: true;
data: T;
pagination?: { page: number; limit: number; total: number; totalPages: number };
}
// Error
interface ErrorResponse {
success: false;
error: { code: string; message: string; details?: any; field?: string };
}
```
## Key Patterns
### Request DTO
```typescript
export class CreateUserDto {
@IsEmail() @IsNotEmpty() email: string;
@MinLength(6) @IsNotEmpty() password: string;
@IsOptional() name?: string;
}
export class QueryUsersDto {
@IsOptional() @Min(1) page?: number = 1;
@IsOptional() @Min(1) @Max(100) limit?: number = 10;
@IsOptional() search?: string;
@IsOptional() @IsIn(['createdAt', 'name']) sortBy?: string = 'createdAt';
@IsOptional() @IsIn(['asc', 'desc']) order?: 'asc' | 'desc' = 'desc';
}
```
### Controller
```typescript
@Get()
async list(@Query() query: QueryUsersDto) {
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')
async getById(@Param('id') id: string) {
const user = await this.userService.findById(id);
if (!user) throw new NotFoundException({ success: false, error: { code: 'NOT_FOUND', message: 'User not found' } });
return { success: true, data: UserResponseDto.fromEntity(user) };
}
```
## Best Practices
- **Resource Naming**: Use plural nouns (`/users`), kebab-case for multi-word
- **Versioning**: Include version in URL (`/v1/users`), maintain backward compatibility
- **Security**: Use HTTPS, implement rate limiting, validate all inputs
- **Performance**: Implement pagination, use field filtering, cache responses
- **Documentation**: Keep OpenAPI spec up to date, include examples
## Common Mistakes
1. **Using Verbs in URLs**: Non-RESTful endpoints
```
# BAD: POST /api/v1/createUser
# GOOD: POST /api/v1/users
```
2. **Inconsistent Response Format**: Different structures for different endpoints
```typescript
// BAD: res.json({ user: data }) vs res.json({ result: data })
// GOOD: res.json({ success: true, data })
```
3. **Wrong HTTP Status Codes**: Using 200 for errors
```typescript
// BAD: res.status(200).json({ error: 'Not found' });
// GOOD: res.status(404).json({ success: false, error: { code: 'NOT_FOUND' } });
```
4. **Missing Pagination**: Returning all records
```typescript
// BAD: prisma.user.findMany()
// GOOD: prisma.user.findMany({ skip: (page - 1) * limit, take: limit })
```
## Quick Reference
| HTTP Method | Action | Idempotent | Status Codes |
|-------------|--------|------------|--------------|
| **GET** | Retrieve | Yes | 200, 404 |
| **POST** | Create | No | 201, 400, 409 |
| **PUT** | Full update | Yes | 200, 400, 404 |
| **PATCH** | Partial update | Yes | 200, 400, 404 |
| **DELETE** | Remove | Yes | 204, 404 |
**Response Format:**
```typescript
// Success: { success: true, data: T, pagination?: {...} }
// Error: { success: false, error: { code: string, message: string } }
```
**Common Error Codes:**
- `400` - Bad Request (validation)
- `401` - Unauthorized (no token)
- `403` - Forbidden (no permission)
- `404` - Not Found
- `409` - Conflict (duplicate)
- `422` - Unprocessable (business rule)
- `429` - Rate limited
## Resources
- [OpenAPI Specification](https://spec.openapis.org/oas/latest.html) - Official OpenAPI docs
- [REST API Design](https://restfulapi.net/) - REST best practices
- [Detailed Code Examples](./references/REFERENCE.md)
- [API Versioning Strategy](../api-versioning-strategy/SKILL.md) - Versioning patterns
- [API Gateway Advanced](../api-gateway-advanced/SKILL.md) - Gateway patterns
- [Middleware Patterns](../middleware-patterns/SKILL.md) - Request handling

View File

@@ -1,182 +0,0 @@
---
trigger: always_on
---
# 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 (Istio/Linkerd) with Traefik
- Implementing advanced routing strategies
- Adding gateway-level circuit breakers
- Implementing API versioning at gateway
- Optimizing gateway performance
## Core Concepts
### API Gateway Responsibilities
1. **Request Routing**: Route requests to appropriate services
2. **API Composition**: Aggregate multiple service responses
3. **Protocol Translation**: Convert between protocols (HTTP, gRPC, GraphQL)
4. **Request/Response Transformation**: Modify requests and responses
5. **Security**: Authentication, authorization, rate limiting
6. **Resilience**: Circuit breaker, retry, timeout at gateway level
7. **Observability**: Logging, metrics, tracing
### API Composition Patterns
- **Aggregation**: Combine multiple service responses into single response
- **Chaining**: Call services sequentially, use previous response in next call
- **Fan-out/Fan-in**: Call multiple services in parallel, aggregate results
## Key Patterns
### API Composition
```typescript
// Fan-out: Call multiple services in parallel
async getUserProfile(userId: string) {
const [user, orders, payments] = await Promise.all([
this.userClient.get(`/users/${userId}`),
this.orderClient.get(`/orders?userId=${userId}`),
this.paymentClient.get(`/payments?userId=${userId}`),
]);
return {
user: user.data,
orders: orders.data.orders,
paymentHistory: payments.data.payments,
};
}
// Chaining: Sequential calls with compensation
async createOrderWithPayment(data) {
const order = await this.orderClient.post('/orders', data);
try {
const payment = await this.paymentClient.post('/payments', { orderId: order.data.id });
return { order: order.data, payment: payment.data };
} catch (error) {
await this.orderClient.delete(`/orders/${order.data.id}`); // Compensate
throw error;
}
}
```
### Gateway Caching
```typescript
export function gatewayCache(ttl: number = 60) {
return async (req, res, next) => {
if (req.method !== 'GET') return next();
const cacheKey = `gateway:${req.path}:${JSON.stringify(req.query)}`;
const cached = await cacheService.get(cacheKey);
if (cached) return res.json(cached);
const originalJson = res.json.bind(res);
res.json = (data) => {
cacheService.set(cacheKey, data, ttl);
return originalJson(data);
};
next();
};
}
```
### Traefik Circuit Breaker
```yaml
middlewares:
circuit-breaker:
circuitBreaker:
expression: "NetworkErrorRatio() > 0.50"
timeout:
forwardingTimeouts:
dialTimeout: 5s
responseHeaderTimeout: 10s
```
## Best Practices
- **API Composition**: Use for aggregating related data from multiple services
- **Caching**: Cache at gateway for frequently accessed data
- **Circuit Breaker**: Implement at gateway to protect downstream services
- **Transformation**: Keep transformations simple and testable
- **Versioning**: Use path-based or header-based versioning
- **Monitoring**: Monitor gateway metrics (latency, error rate)
## Common Mistakes
1. **No Timeout at Gateway**: Requests hanging forever
```yaml
# GOOD: Set timeout
middlewares:
timeout:
forwardingTimeouts:
dialTimeout: 5s
responseHeaderTimeout: 10s
```
2. **Missing Circuit Breaker**: Cascading failures
```yaml
# GOOD: Add circuit breaker for each service
middlewares:
circuit-breaker:
circuitBreaker:
expression: "NetworkErrorRatio() > 0.50"
```
3. **No Caching for Static Data**: Unnecessary service load
```typescript
// GOOD: Cache at gateway
app.get('/api/config', gatewayCache(3600), handler);
```
4. **N+1 API Calls from Client**: Multiple round trips
```typescript
// BAD: Client makes 3 calls
// GOOD: Use API composition GET /user-profile/123
```
## Quick Reference
| Pattern | Use Case | Implementation |
|---------|----------|----------------|
| **API Composition** | Aggregate multiple services | Promise.all() |
| **Request Transform** | Modify incoming requests | Middleware |
| **Response Transform** | Standardize responses | Override res.json |
| **Gateway Caching** | Cache GET responses | Redis/Memory |
| **Circuit Breaker** | Protect from failures | Traefik middleware |
**Traefik Middleware Order:**
```
1. Rate Limiting
2. Authentication
3. Circuit Breaker
4. Retry
5. Headers Transform
6. Compression
```
**API Composition Patterns:**
```typescript
// Fan-out (parallel)
const [users, orders] = await Promise.all([userClient.get('/users'), orderClient.get('/orders')]);
// Chaining (sequential)
const order = await orderClient.create(data);
const payment = await paymentClient.process(order.id);
```
## Resources
- [Traefik Documentation](https://doc.traefik.io/traefik/) - Official Traefik docs
- [API Gateway Pattern](https://microservices.io/patterns/apigateway.html) - Gateway patterns
- [Service Mesh](https://istio.io/) - Istio service mesh
- [Detailed Code Examples](./references/REFERENCE.md)
- [Middleware Patterns](../middleware-patterns/SKILL.md) - Middleware patterns
- [Resilience Patterns](../resilience-patterns/SKILL.md) - Circuit breaker patterns

View File

@@ -1,432 +0,0 @@
---
trigger: always_on
---
# 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
## 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;
```
### Version Router
```typescript
// src/routes/v1/index.ts
// EN: Version 1 routes
// VI: Routes version 1
import { Router } from 'express';
import userRoutes from './users';
const router = Router();
router.use('/users', userRoutes);
export default router;
// src/routes/v2/index.ts
// EN: Version 2 routes with breaking changes
// VI: Routes version 2 với breaking changes
import { Router } from 'express';
import userRoutes from './users'; // EN: Different implementation / VI: Implementation khác
const router = Router();
router.use('/users', userRoutes); // EN: Different response format / VI: Format response khác
export default router;
```
## Header-Based Versioning
### Version Negotiation Middleware
```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();
}
```
### Version-Aware Controller
```typescript
// src/modules/user/user.controller.ts
// EN: Version-aware controller
// VI: Controller nhận biết version
export class UserController {
async getUser(req: Request, res: Response): Promise<void> {
const version = req.apiVersion || 2;
if (version === 1) {
// EN: Version 1 response format
// VI: Format response version 1
const user = await this.userService.findById(req.params.id);
res.json({
id: user.id,
email: user.email,
name: user.name,
});
} else {
// EN: Version 2 response format
// VI: Format response version 2
const user = await this.userService.findById(req.params.id);
res.json({
success: true,
data: {
user: {
id: user.id,
email: user.email,
name: user.name,
profile: user.profile, // EN: New field in v2 / VI: Field mới trong v2
},
},
metadata: {
version: '2.0.0',
timestamp: new Date().toISOString(),
},
});
}
}
}
```
## 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();
}
```
## API Deprecation
### 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);
```
### Deprecation Documentation
```typescript
// src/docs/deprecation.md
// EN: Deprecation notices
// VI: Thông báo deprecation
## API Version 1 Deprecation
**Deprecated**: 2024-01-01
**Sunset Date**: 2024-12-31
**Migration Guide**: [Migration Guide](./migration-v1-to-v2.md)
### Changes in Version 2
- New response format
- Additional user profile fields
- Updated authentication flow
```
## 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
};
}
}
```
## Breaking Changes Migration
### Migration 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);
}
}
```
## 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
```
4. **No Migration Guide**: Clients don't know how to upgrade
```
# ✅ Always provide:
- Changelog
- Migration guide
- Sunset date
- Deprecation warnings
```
## 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"
```
**Version Detection:**
```typescript
// URL path
const version = req.path.match(/\/v(\d+)\//)?.[1] || '2';
// Header
const version = req.headers.accept?.match(/vnd\.goodgo\.v(\d+)/)?.[1];
```

View File

@@ -1,325 +0,0 @@
---
trigger: always_on
---
# 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:
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
```
Request → L1 Cache → Hit? Return
↓ Miss
L2 Cache → Hit? Return + Warm L1
↓ Miss
Data Source (DB/API) → Store in L1 & L2 → Return
```
## 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:
```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)
## Quick Reference
| Cache Layer | Speed | Capacity | TTL | Scope |
|-------------|-------|----------|-----|-------|
| **L1 (Memory)** | <1ms | 10k keys | 60s-5min | Per instance |
| **L2 (Redis)** | <5ms | Large | Configurable | Shared |
**TTL Guidelines:**
| Data Type | TTL | Example |
|-----------|-----|---------|
| Session data | 15-60min | User sessions |
| Permissions | 5min | RBAC cache |
| User profiles | 10min | Profile data |
| Config | 30min | Feature flags |
| Static data | 1-2hr | Reference data |
**Key Patterns:**
```typescript
// Entity
`user:${userId}`
`session:${sessionId}`
// Entity + Sub-resource
`user:${userId}:permissions`
`user:${userId}:roles`
// List/Collection
`users:list:page:${page}`
`products:category:${categoryId}`
```
**Essential Operations:**
```typescript
// Get/Set
await cache.get<T>(key);
await cache.set(key, value, ttl);
// Get or fetch
await cache.getOrSet(key, fetchFn, ttl);
// Invalidate
await cache.del(key);
await cache.invalidatePattern('user:123:*');
```
## 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,484 +0,0 @@
---
trigger: always_on
---
# 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
### 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 # EN: Switch to green after verification / VI: Chuyển sang green sau khi xác minh
ports:
- port: 80
targetPort: 5000
```
### Blue-Green Switch Script
```bash
#!/bin/bash
# scripts/deployment/blue-green-switch.sh
# EN: Switch between blue and green deployments
# VI: Chuyển đổi giữa blue và green deployments
SERVICE_NAME=$1
CURRENT_VERSION=$(kubectl get service $SERVICE_NAME -o jsonpath='{.spec.selector.version}')
NEW_VERSION=$([ "$CURRENT_VERSION" = "blue" ] && echo "green" || echo "blue")
echo "Switching from $CURRENT_VERSION to $NEW_VERSION"
# EN: Update service selector
# VI: Cập nhật service selector
kubectl patch service $SERVICE_NAME -p "{\"spec\":{\"selector\":{\"version\":\"$NEW_VERSION\"}}}"
# EN: Wait for rollout
# VI: Đợi rollout
kubectl rollout status deployment/$SERVICE_NAME-$NEW_VERSION
# EN: Run smoke tests
# VI: Chạy smoke tests
./scripts/deployment/smoke-tests.sh $SERVICE_NAME
if [ $? -ne 0 ]; then
echo "Smoke tests failed, rolling back"
kubectl patch service $SERVICE_NAME -p "{\"spec\":{\"selector\":{\"version\":\"$CURRENT_VERSION\"}}}"
exit 1
fi
echo "Successfully switched to $NEW_VERSION"
```
## Canary Deployment
### 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 # 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
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
```
### Progressive Canary Rollout
```bash
#!/bin/bash
# scripts/deployment/canary-rollout.sh
# EN: Progressive canary rollout
# VI: Progressive canary rollout
SERVICE_NAME=$1
CANARY_PERCENTAGES=(10 25 50 75 100)
for PERCENTAGE in "${CANARY_PERCENTAGES[@]}"; do
echo "Rolling out to $PERCENTAGE%"
# EN: Update VirtualService weight
# VI: Cập nhật VirtualService weight
kubectl patch virtualservice $SERVICE_NAME -p "{\"spec\":{\"http\":[{\"route\":[{\"destination\":{\"subset\":\"canary\"},\"weight\":$PERCENTAGE},{\"destination\":{\"subset\":\"stable\"},\"weight\":$((100-PERCENTAGE))}]}]}}"
# EN: Wait for traffic to stabilize
# VI: Đợi traffic ổn định
sleep 60
# EN: Run health checks
# VI: Chạy health checks
./scripts/deployment/health-checks.sh $SERVICE_NAME
if [ $? -ne 0 ]; then
echo "Health checks failed at $PERCENTAGE%, rolling back"
kubectl patch virtualservice $SERVICE_NAME -p "{\"spec\":{\"http\":[{\"route\":[{\"destination\":{\"subset\":\"canary\"},\"weight\":0},{\"destination\":{\"subset\":\"stable\"},\"weight\":100}]}]}}"
exit 1
fi
echo "Successfully rolled out to $PERCENTAGE%"
done
echo "Canary rollout complete"
```
## Automated Rollback
### Rollback Script
```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
```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
// 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
```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
```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/servi

View File

@@ -1,430 +0,0 @@
---
trigger: always_on
---
# 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 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
## Common Mistakes
1. **Stating the Obvious**: Comments that repeat what code says
```typescript
// ❌ BAD: States the obvious
// EN: Set x to 5
// VI: Gán x bằng 5
const x = 5;
// ✅ GOOD: Explains why
// EN: Default timeout in seconds for API calls
// VI: Timeout mặc định (giây) cho các API call
const x = 5;
```
2. **Missing Translation**: Incomplete bilingual comments
```typescript
// ❌ BAD: Missing Vietnamese
// EN: Calculate discount
const discount = price * 0.1;
// ✅ GOOD: Both languages
// EN: Calculate 10% discount for members
// VI: Tính giảm giá 10% cho thành viên
const discount = price * 0.1;
```
3. **Outdated Comments**: Comments not matching code
```typescript
// ❌ BAD: Comment says email, code uses phone
// EN: Validate email format
// VI: Xác thực định dạng email
validatePhone(phone);
// ✅ GOOD: Keep comments in sync with code
// EN: Validate phone number format
// VI: Xác thực định dạng số điện thoại
validatePhone(phone);
```
4. **No JSDoc for Public APIs**: Missing documentation for exports
```typescript
// ❌ BAD: Exported function without JSDoc
export function processOrder(order: Order) { ... }
// ✅ GOOD: Full JSDoc for exported functions
/**
* EN: Process order and update inventory
* VI: Xử lý đơn hàng và cập nhật kho
* @param order - Order to process / Đơn hàng cần xử lý
* @returns ProcessingResult / Kết quả xử lý
*/
export function processOrder(order: Order): ProcessingResult { ... }
```
## 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ì]
```
## Resources
- [Project Rules](../project-rules/SKILL.md) - Code organization standards
- [Documentation](../documentation/SKILL.md) - Documentation patterns
- [API Design](../api-design/SKILL.md) - API documentation standards
- [Testing Patterns](../testing-patterns/SKILL.md) - Test documentation

View File

@@ -1,35 +0,0 @@
---
trigger: always_on
---
# 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
- Handling configuration versioning
- Building configuration services
- Implementing A/B testing with feature flags
- Managing configuration across multiple environments
## Core Concepts
### Configuration Types
1. **Static Configuration**: Environment variables, config files
2. **Dynamic Configuration**: Feature flags, runtime configs
3. **Secrets**: Sensitive data (API keys, passwords)
4. **Feature Flags**: Runtime feature toggles
### Configuration Sources
- Environment variables
- Configuration files (JSON, YAML)
- Configuration service (external)
- Feature flag service
- Secrets manager (Kubernetes Secrets, Vault)

View File

@@ -1,269 +0,0 @@
---
trigger: always_on
---
# 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
- Implementing optimistic locking strategies
- Building event sourcing systems
- Ensuring data integrity in distributed systems
## Core Concepts
### ACID vs BASE
**ACID (Traditional Databases):**
- Atomicity: All or nothing
- Consistency: Data always valid
- Isolation: Concurrent transactions isolated
- Durability: Committed changes persist
**BASE (Distributed Systems):**
- Basic Availability: System available most of the time
- Soft state: State may change over time
- Eventual consistency: Consistency achieved eventually
### Consistency Models
| Model | Description | Use Case |
|-------|-------------|----------|
| **Strong** | All nodes see same data at same time | Financial transactions |
| **Weak** | No guarantees about when consistency occurs | Caching |
| **Eventual** | System becomes consistent over time | Read models, analytics |
| **Causal** | Related operations maintain order | User sessions |
### Distributed Transaction Challenges
- Network partitions
- Service failures
- Clock synchronization
- Partial failures
- Two-Phase Commit (2PC) limitations
## Saga Pattern
### Orchestration vs Choreography
| Approach | Central Control | Resilience | Complexity | Best For |
|----------|-----------------|------------|------------|----------|
| **Orchestration** | Yes (single coordinator) | Lower (SPOF) | Easier to debug | Complex workflows |
| **Choreography** | No (event-driven) | Higher | Harder to trace | Simple flows, loose coupling |
### Saga Execution Flow
```
Execute: Step1 -> Step2 -> Step3 -> Complete
| | |
v v v
Compensate: <- Step2.undo <- Step1.undo (on failure)
```
### Key Saga Interfaces
```typescript
interface SagaStep {
name: string;
execute: () => Promise<any>;
compensate: (context: any) => Promise<void>;
retry?: number;
}
interface SagaContext {
sagaId: string;
steps: SagaStep[];
currentStep: number;
data: Record<string, any>;
status: 'pending' | 'running' | 'completed' | 'compensating' | 'failed';
}
```
See [./references/REFERENCE.md](./references/REFERENCE.md) for full Saga orchestrator implementation.
## Idempotency Pattern
Ensures operations can be safely retried without side effects.
### Key Concepts
- **Idempotency Key**: Unique identifier for each operation
- **Result Storage**: Cache results to return on duplicates
- **TTL**: Time-to-live for idempotency records
### Pattern
```typescript
const key = `${operation}:${userId}:${hash(requestData)}`;
await idempotencyHandler.execute(key, () => operation());
```
See [./references/REFERENCE.md](./references/REFERENCE.md) for full implementation.
## Optimistic Locking
Prevents lost updates in concurrent scenarios using version fields.
### Pattern
```typescript
await prisma.entity.update({
where: { id, version: currentVersion },
data: { ...updates, version: { increment: 1 } }
});
```
### Prisma Schema
```prisma
model Entity {
id String @id @default(cuid())
version Int @default(1)
// ... other fields
}
```
See [./references/REFERENCE.md](./references/REFERENCE.md) for full optimistic locking service.
## CQRS Pattern
Command Query Responsibility Segregation separates read and write operations.
### Architecture
```
Write Path: Command -> Write Model -> Event -> Event Store
Read Path: Query -> Read Model (denormalized, optimized for reads)
```
### Key Benefits
- Optimized read models for query performance
- Independent scaling of read/write operations
- Eventual consistency between models
See [./references/REFERENCE.md](./references/REFERENCE.md) for implementation details.
## Conflict Resolution Strategies
| Strategy | Description | Use Case |
|----------|-------------|----------|
| **Last Write Wins** | Latest timestamp wins | Simple scenarios |
| **First Write Wins** | Earliest timestamp wins | Preserving original data |
| **Merge** | Combine both versions | Non-conflicting fields |
| **Manual** | Store for human review | Critical data conflicts |
## Outbox Pattern
Ensures reliable event publishing by storing events in the same transaction as business data.
### Flow
1. Execute business operation in transaction
2. Store event in outbox table (same transaction)
3. Background processor publishes and marks as sent
See [./references/REFERENCE.md](./references/REFERENCE.md) for implementation.
## Best Practices
### Saga Pattern
- Design compensations for every step
- Make steps idempotent for retries
- Set timeouts for saga execution
- Monitor saga execution and compensation
- Choose orchestration for complex workflows, choreography for simple ones
### Idempotency
- Generate keys from request data
- Store keys with results
- Set appropriate TTL for records
- Regularly clean expired records
### Eventual Consistency
- Accept that consistency is eventual
- Use separate read models for queries
- Define resolution strategies upfront
- Monitor consistency lag
### Optimistic Locking
- Add version fields to entities
- Implement retry with exponential backoff
- Handle conflicts gracefully
- Use for read-heavy workloads
## Common Mistakes
### No Compensation Logic
```typescript
// BAD: No compensation
steps: [{ execute: () => createOrder() }]
// GOOD: Always define compensation
steps: [{
execute: () => createOrder(),
compensate: (ctx) => cancelOrder(ctx.orderId)
}]
```
### Missing Idempotency
```typescript
// BAD: Creates duplicate on retry
await createPayment(orderId);
// GOOD: Idempotent check
const existing = await findPayment(idempotencyKey);
if (existing) return existing;
await createPayment(orderId);
```
### Ignoring Partial Failures
```typescript
// BAD: No error handling
await step1(); await step2(); await step3();
// GOOD: Saga orchestration
await sagaOrchestrator.execute(context);
```
### No Version Field
```prisma
// GOOD: Add version for optimistic locking
model Entity {
version Int @default(1)
}
```
## Quick Reference
| Pattern | Use Case | Complexity |
|---------|----------|------------|
| **Saga (Orchestrated)** | Complex multi-step workflows | High |
| **Saga (Choreography)** | Simple event-driven flows | Medium |
| **Outbox Pattern** | Guaranteed event publishing | Medium |
| **Idempotency** | Retry-safe operations | Low |
| **Optimistic Lock** | Concurrent updates | Low |
| **CQRS** | Read/write optimization | High |
| **Dead Letter Queue** | Failed message handling | Medium |
## Resources
### Internal References
- [Detailed Code Examples](./references/REFERENCE.md) - Full implementations for all patterns
- [Event-Driven Architecture](../event-driven-architecture/SKILL.md) - Event patterns
- [Error Handling Patterns](../error-handling-patterns/SKILL.md) - Error handling
- [Database & Prisma](../database-prisma/SKILL.md) - Database patterns
- [Project Rules](../project-rules/SKILL.md) - GoodGo coding standards
### External Resources
- [Saga Pattern](https://microservices.io/patterns/data/saga.html) - Saga pattern overview
- [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) - Event sourcing pattern
- [CQRS Pattern](https://martinfowler.com/bliki/CQRS.html) - CQRS pattern

View File

@@ -1,184 +0,0 @@
---
trigger: always_on
---
# 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
## Key Patterns
### Prisma Setup
```bash
npm install @prisma/client prisma
npx prisma init
```
### Schema Definition
```prisma
model User {
id String @id @default(cuid())
email String @unique
name String?
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
@@index([email])
@@index([createdAt])
@@map("users")
}
```
### Database Connection
```typescript
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'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
```
### Repository Pattern
```typescript
export class UserRepository {
async findById(id: string) {
return prisma.user.findUnique({ where: { id }, include: { profile: true } });
}
async findAll({ page = 1, limit = 10, search }: QueryOptions) {
const where = search ? { OR: [
{ email: { contains: search, mode: 'insensitive' } },
{ name: { contains: search, mode: 'insensitive' } }
]} : {};
const [data, total] = await Promise.all([
prisma.user.findMany({ where, skip: (page - 1) * limit, take: limit }),
prisma.user.count({ where })
]);
return { data, total };
}
async create(data: CreateUserDto) {
return prisma.user.create({ data });
}
}
```
### Transactions
```typescript
await prisma.$transaction(async (tx) => {
await tx.account.update({ where: { id: from }, data: { balance: { decrement: amount } } });
await tx.account.update({ where: { id: to }, data: { balance: { increment: amount } } });
}, { maxWait: 5000, timeout: 10000 });
```
## Best Practices
- **Schema Design**: Use appropriate field types, add indexes for frequently queried fields
- **Performance**: Use select to fetch only needed fields, implement pagination
- **Security**: Never expose sensitive fields, use parameterized queries
- **Maintenance**: Keep migrations small and focused, test before production
## Common Mistakes
1. **N+1 Query Problem**: Fetching related data in a loop
```typescript
// BAD: N+1 queries
for (const user of users) { await prisma.post.findMany({ where: { authorId: user.id } }); }
// GOOD: Include relations
await prisma.user.findMany({ include: { posts: true } });
```
2. **No Indexes**: Missing indexes on frequently queried columns
```prisma
// GOOD: Add index
@@index([createdAt])
```
3. **Raw Queries Without Parameters**: SQL injection risk
```typescript
// BAD: prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`);
// GOOD: prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`;
```
4. **Not Using Transactions**: Data inconsistency risk
```typescript
// GOOD: Use transaction for related operations
await prisma.$transaction([update1, update2]);
```
5. **Exposing Internal IDs**: Leaking database structure
```prisma
// GOOD: Use CUID or UUID
model User { id String @id @default(cuid()) }
```
## Quick Reference
| Operation | Command |
|-----------|---------|
| **Create migration** | `npx prisma migrate dev --name <name>` |
| **Apply migrations** | `npx prisma migrate deploy` |
| **Reset database** | `npx prisma migrate reset` |
| **Generate client** | `npx prisma generate` |
| **Open Studio** | `npx prisma studio` |
| **Seed database** | `npx prisma db seed` |
**Common Query Patterns:**
```typescript
await prisma.user.findUnique({ where: { id } }); // Find unique
await prisma.user.findMany({ include: { posts: true } }); // With relations
await prisma.user.findMany({ select: { id: true } }); // Select fields
await prisma.user.findMany({ skip: 10, take: 10 }); // Pagination
await prisma.$transaction(async (tx) => { ... }); // Transaction
```
**Schema Field Types:**
| Type | PostgreSQL | Description |
|------|------------|-------------|
| `String` | TEXT | Variable-length string |
| `Int` | INTEGER | 32-bit integer |
| `BigInt` | BIGINT | 64-bit integer |
| `Float` | DOUBLE PRECISION | Floating point |
| `Boolean` | BOOLEAN | True/false |
| `DateTime` | TIMESTAMP | Date and time |
| `Json` | JSONB | JSON data |
## Resources
- [Prisma Documentation](https://www.prisma.io/docs) - Official Prisma docs
- [Prisma Schema Reference](https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference)
- [Detailed Code Examples](./references/REFERENCE.md)
- [Repository Pattern](../repository-pattern/SKILL.md) - Data access patterns
- [Caching Patterns](../caching-patterns/SKILL.md) - Query caching
- [Testing Patterns](../testing-patterns/SKILL.md) - Database mocking

View File

@@ -1,183 +0,0 @@
---
trigger: always_on
---
# 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
### 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
## Key Patterns
### Deployment Manifest
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: goodgo
spec:
replicas: 3
selector:
matchLabels:
app: auth-service
template:
spec:
containers:
- name: auth-service
image: goodgo/auth-service:v1.0.0
resources:
requests: { memory: "256Mi", cpu: "250m" }
limits: { memory: "512Mi", cpu: "500m" }
livenessProbe:
httpGet: { path: /health, port: 3000 }
initialDelaySeconds: 30
readinessProbe:
httpGet: { path: /ready, port: 3000 }
initialDelaySeconds: 5
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef: { name: db-secrets, key: url }
```
### HPA Configuration
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: auth-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
```
### Ingress
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts: [api.goodgo.com]
secretName: api-tls-secret
rules:
- host: api.goodgo.com
http:
paths:
- path: /auth
pathType: Prefix
backend:
service: { name: auth-service, port: { number: 80 } }
```
## Best Practices
- **Resource Management**: Always set resource requests and limits, use HPA for scaling
- **Configuration**: Use ConfigMaps for config, Secrets for sensitive data
- **Health Checks**: Implement both liveness and readiness probes
- **Deployment**: Use rolling updates, set maxSurge/maxUnavailable appropriately
- **Security**: Run as non-root, use network policies, update base images regularly
- **Monitoring**: Expose metrics endpoint, set up alerts
## Common Mistakes
1. **No Resource Limits**: Pods consuming all node resources
```yaml
# GOOD: Set limits
resources:
requests: { memory: "256Mi", cpu: "250m" }
limits: { memory: "512Mi", cpu: "500m" }
```
2. **Missing Health Checks**: K8s can't detect unhealthy pods
```yaml
# GOOD: Add probes
livenessProbe:
httpGet: { path: /health, port: 3000 }
readinessProbe:
httpGet: { path: /ready, port: 3000 }
```
3. **Hardcoded Secrets**: Exposing sensitive data
```yaml
# BAD: value: "secret123"
# GOOD: valueFrom: secretKeyRef: { name: secrets, key: password }
```
4. **Using `latest` Tag**: Unpredictable deployments
```yaml
# BAD: image: app:latest
# GOOD: image: app:v1.2.3
```
## Quick Reference
| Resource | Command |
|----------|---------|
| **Apply manifests** | `kubectl apply -f kubernetes/` |
| **Get pods** | `kubectl get pods -n goodgo` |
| **Get logs** | `kubectl logs -f deployment/app -n goodgo` |
| **Scale** | `kubectl scale deployment/app --replicas=5` |
| **Rollback** | `kubectl rollout undo deployment/app` |
| **Port forward** | `kubectl port-forward svc/app 3000:80` |
| **Exec into pod** | `kubectl exec -it pod-name -- /bin/sh` |
**Resource Sizing Guidelines:**
| Service Type | Memory Request | Memory Limit | CPU Request | CPU Limit |
|--------------|----------------|--------------|-------------|-----------|
| Microservice | 256Mi | 512Mi | 250m | 500m |
| API Gateway | 512Mi | 1Gi | 500m | 1000m |
| Database | 1Gi | 2Gi | 500m | 1000m |
**Health Check Defaults:**
```yaml
livenessProbe:
initialDelaySeconds: 30 # Wait for app startup
periodSeconds: 10 # Check every 10s
failureThreshold: 3 # Restart after 3 failures
readinessProbe:
initialDelaySeconds: 5 # Start checking early
periodSeconds: 5 # Check frequently
failureThreshold: 3 # Remove from LB after 3 failures
```
## Resources
- [Kubernetes Documentation](https://kubernetes.io/docs/) - Official K8s docs
- [Helm](https://helm.sh/docs/) - K8s package manager
- [Detailed Manifests](./references/REFERENCE.md)
- [Infrastructure as Code](../infrastructure-as-code/SKILL.md) - Terraform patterns
- [Observability & Monitoring](../observability-monitoring/SKILL.md) - Health checks
- [Service Discovery](../service-discovery-registry/SKILL.md) - K8s DNS patterns

View File

@@ -1,444 +0,0 @@
---
trigger: always_on
---
# Documentation Writing Guidelines
## Documentation Structure
```
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
```
## 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
## 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
```
## Resources
- [Project Rules](../project-rules/SKILL.md) - Project structure and standards
- [Comment Code](../comment-code/SKILL.md) - Code commenting standards
- [API Design](../api-design/SKILL.md) - API documentation
- [Testing Patterns](../testing-patterns/SKILL.md) - Test documentation

View File

@@ -1,363 +0,0 @@
---
trigger: always_on
---
# 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 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.
## Quick Reference
| Error Class | Status | When to Use |
|-------------|--------|-------------|
| `NotFoundError` | 404 | Resource not found |
| `BadRequestError` | 400 | Invalid request |
| `ValidationError` | 422 | Input validation failed |
| `UnauthorizedError` | 401 | Auth required |
| `ForbiddenError` | 403 | Access denied |
| `ConflictError` | 409 | Duplicate/conflict |
| `RateLimitError` | 429 | Too many requests |
| `InternalServerError` | 500 | Server error |
**Error Response Format:**
```typescript
{
success: false,
error: {
code: "RESOURCE_001",
message: "User not found",
details?: { /* validation details */ }
},
timestamp: "2024-01-01T00:00:00.000Z"
}
```
**Common Error Codes:**
| Code | Category | Description |
|------|----------|-------------|
| `AUTH_001` | Auth | Unauthorized |
| `AUTH_003` | Auth | Invalid token |
| `VALIDATION_001` | Validation | Validation failed |
| `RESOURCE_001` | Resource | Not found |
| `RESOURCE_002` | Resource | Already exists |
| `DB_001` | Database | Database error |
| `SYS_001` | System | Internal error |
**Essential Imports:**
```typescript
import { NotFoundError, ValidationError, ConflictError } from '../errors/http-error';
import { ErrorCode } from '../errors/error-codes';
import { asyncHandler } from '../middlewares/async.middleware';
```
## 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,270 +0,0 @@
---
trigger: always_on
---
# 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
| Aspect | Request-Response | Event-Driven |
|--------|------------------|--------------|
| Communication | Synchronous | Asynchronous |
| Coupling | Tight | Loose |
| Blocking | Yes | No |
| Consistency | Immediate | Eventual |
| Infrastructure | Traefik API Gateway | Kafka |
### Kafka Fundamentals
**Topics**: Named streams of events (e.g., `user.created`, `order.placed`)
- Organized by domain and action
- Divided into partitions for parallelism
**Partitions**: Physical division of topics
- Enables horizontal scaling
- Maintains ordering per partition key
- Multiple consumers can process different partitions
**Consumer Groups**: Group of consumers working together
- Each partition consumed by only one consumer in group
- Enables parallel processing
- Automatically rebalances on consumer join/leave
**Producers**: Services that publish events to topics
**Consumers**: Services that subscribe to topics and process events
### Event Structure
```typescript
interface BaseEvent {
eventId: string; // Unique event identifier
eventType: string; // Event type (e.g., "user.created")
eventVersion: string; // Schema version (e.g., "1.0.0")
timestamp: string; // ISO 8601 timestamp
source: string; // Service that published the event
correlationId?: string; // Request correlation ID
traceId?: string; // Distributed tracing ID
data: unknown; // Event payload
}
```
### Event Naming Conventions
**Event Type Format**: `{domain}.{action}.v{version}`
- `user.created.v1`
- `order.placed.v1`
- `payment.processed.v2`
**Topic Naming**: `{domain}.{entity}.{action}`
- `user.created`
- `order.placed`
- `payment.processed`
## Key Patterns
### 1. Event Publishing
```typescript
// Fire-and-forget with error logging
eventPublisher.publish('user.created', event, { partitionKey: user.id })
.catch(err => logger.error('Failed to publish', { err }));
```
### 2. Event Consuming
```typescript
consumer.on('user.created', {
handle: async (event) => {
await processEvent(event);
},
});
await consumer.start(['user.created']);
```
### 3. Outbox Pattern (Transactional)
Store events in database within same transaction, then publish asynchronously:
```typescript
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data });
await outboxService.addToOutbox('user.created', userData, 'user.created');
return user;
});
```
### 4. Dead Letter Queue (DLQ)
After max retries, send failed events to DLQ topic for manual inspection:
```typescript
after maxRetries send to topic.dlq
```
### 5. Idempotency
Consumers must handle duplicate events:
```typescript
if (await this.isProcessed(event.eventId)) return;
await processEvent(event);
await this.markProcessed(event.eventId);
```
## Best Practices
### 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
### Event Size Limits
- Recommended: < 1MB per event
- Kafka default: 1MB (configurable)
- For large payloads: Store data elsewhere, send reference in event
### Performance Optimization
- **Batch Publishing**: Group multiple events for better throughput
- **Async Publishing**: Don't block request handlers
- **Consumer Parallelism**: Use multiple partitions and consumers
- **Connection Pooling**: Reuse Kafka client instances
- **Compression**: Enable compression for better network usage
## Common Mistakes
1. **Blocking on Publish**: Slowing down request handlers
```typescript
// BAD: Await in request handler
await eventPublisher.publish('user.created', event);
res.json({ success: true });
// GOOD: Fire and forget with error logging
eventPublisher.publish('user.created', event)
.catch(err => logger.error('Failed to publish', { err }));
res.json({ success: true });
```
2. **No Idempotency**: Duplicate event processing issues
```typescript
// BAD: No duplicate check
async handle(event) {
await createUser(event.data);
}
// GOOD: Check for duplicates
async handle(event) {
if (await this.isProcessed(event.eventId)) return;
await createUser(event.data);
await this.markProcessed(event.eventId);
}
```
3. **Missing Partition Key**: Events for same entity out of order
```typescript
// BAD: No partition key
await publish('user.updated', event);
// GOOD: Use entity ID as partition key
await publish('user.updated', event, { partitionKey: userId });
```
4. **No Dead Letter Queue**: Lost events on failure
```typescript
// GOOD: Always implement DLQ for failed events
after maxRetries → send to topic.dlq
```
5. **Breaking Schema Changes**: Use versioning strategy instead
6. **Global Ordering Expectations**: Understand partition ordering only
## Quick Reference
| Concept | Description |
|---------|-------------|
| **Topic** | Named stream of events (e.g., `user.created`) |
| **Partition** | Division of topic for parallelism |
| **Consumer Group** | Consumers sharing workload |
| **Offset** | Position in partition |
**Event Structure:**
```typescript
{
eventId: "uuid", // Unique identifier
eventType: "user.created", // Event type
eventVersion: "1.0.0", // Schema version
timestamp: "ISO-8601", // When published
source: "auth-service", // Publisher service
correlationId: "uuid", // Request trace ID
data: { ... } // Event payload
}
```
**Topic Naming:**
```
{domain}.{action}
user.created
order.placed
payment.processed
```
**Essential Commands:**
```bash
# List topics
kafka-topics --list --bootstrap-server localhost:9092
# Create topic
kafka-topics --create --topic user.created --partitions 3
# Consume from beginning
kafka-console-consumer --topic user.created --from-beginning
```
**KafkaJS Quick Setup:**
```typescript
import { Kafka } from 'kafkajs';
const kafka = new Kafka({ brokers: ['localhost:9092'], clientId: 'my-app' });
const producer = kafka.producer();
const consumer = kafka.consumer({ groupId: 'my-group' });
```
**Environment Variables:**
```bash
KAFKA_BROKERS=localhost:9092
KAFKA_CLIENT_ID=my-service
KAFKA_CONSUMER_GROUP_ID=my-service-consumers
SCHEMA_REGISTRY_URL=http://localhost:8081
```
## Resources
- [Detailed Reference](./references/REFERENCE.md) - Full code examples and implementation details
- [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
- [Resilience Patterns](../resilience-patterns/SKILL.md) - Circuit breaker, retry patterns
- [Error Handling Patterns](../error-handling-patterns/SKILL.md) - Error handling best practices
- [Observability & Monitoring](../observability-monitoring/SKILL.md) - Logging, metrics, tracing
- [Project Rules](../project-rules/SKILL.md) - GoodGo coding standards

View File

@@ -1,415 +0,0 @@
---
trigger: always_on
---
# 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
- Managing multi-environment infrastructure
- Versioning infrastructure
- Automating infrastructure provisioning
## Core Concepts
### Infrastructure as Code Benefits
1. **Version Control**: Track infrastructure changes
2. **Reproducibility**: Consistent environments
3. **Automation**: Reduce manual errors
4. **Testing**: Test infrastructure changes
5. **Collaboration**: Team can review changes
### IaC Tools
- **Terraform**: Infrastructure provisioning
- **Kubernetes Manifests**: K8s resource definitions
- **Helm**: K8s package manager
- **ArgoCD/Flux**: GitOps tools
## Terraform Patterns
### Module Structure
```
infra/terraform/
├── modules/
│ ├── kubernetes-cluster/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── postgresql/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ └── redis/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── environments/
│ ├── staging/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ └── production/
│ ├── main.tf
│ └── terraform.tfvars
└── shared/
└── backend.tf
```
### Terraform Module Example
```hcl
# infra/terraform/modules/postgresql/main.tf
# EN: PostgreSQL module
# VI: Module PostgreSQL
variable "database_name" {
description = "Database name"
type = string
}
variable "environment" {
description = "Environment name"
type = string
}
resource "google_sql_database_instance" "postgres" {
name = "${var.database_name}-${var.environment}"
database_version = "POSTGRES_15"
region = "us-central1"
settings {
tier = "db-f1-micro"
backup_configuration {
enabled = true
start_time = "03:00"
}
}
}
resource "google_sql_database" "database" {
name = var.database_name
instance = google_sql_database_instance.postgres.name
}
output "connection_name" {
value = google_sql_database_instance.postgres.connection_name
}
output "database_url" {
value = "postgresql://user:pass@${google_sql_database_instance.postgres.ip_address}/${var.database_name}"
sensitive = true
}
```
### Using Modules
```hcl
# infra/terraform/environments/staging/main.tf
# EN: Use PostgreSQL module
# VI: Sử dụng module PostgreSQL
module "postgresql" {
source = "../../modules/postgresql"
database_name = "goodgo_staging"
environment = "staging"
}
output "database_url" {
value = module.postgresql.database_url
sensitive = true
}
```
## GitOps Patterns
### ArgoCD Setup
```yaml
# infra/gitops/argocd/applications/user-service.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: user-service
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/goodgo/platform
targetRevision: main
path: deployments/production/kubernetes/user-service
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
```
### Flux Setup
```yaml
# infra/gitops/flux/kustomizations/user-service.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: user-service
namespace: flux-system
spec:
interval: 5m
path: ./deployments/production/kubernetes/user-service
prune: true
sourceRef:
kind: GitRepository
name: platform-repo
validation: client
```
## Infrastructure Testing
### Terraform Testing
```bash
#!/bin/bash
# scripts/infra/test-terraform.sh
# EN: Test Terraform changes
# VI: Test các thay đổi Terraform
cd infra/terraform/environments/staging
# EN: Validate Terraform
# VI: Validate Terraform
terraform init
terraform validate
# EN: Plan changes
# VI: Plan changes
terraform plan -out=tfplan
# EN: Review plan
# VI: Review plan
terraform show tfplan
```
### Infrastructure Validation
```typescript
// scripts/infra/validate-k8s.ts
// EN: Validate Kubernetes manifests
// VI: Validate Kubernetes manifests
import { execSync } from 'child_process';
function validateKubernetesManifests(path: string): boolean {
try {
execSync(`kubectl apply --dry-run=client -f ${path}`, {
stdio: 'inherit',
});
console.log('Kubernetes manifests are valid');
return true;
} catch (error) {
console.error('Kubernetes validation failed', error);
return false;
}
}
```
## Environment Management
### Environment Configuration
```hcl
# infra/terraform/environments/staging/terraform.tfvars
environment = "staging"
cluster_name = "goodgo-staging"
node_count = 3
instance_type = "t3.medium"
# infra/terraform/environments/production/terraform.tfvars
environment = "production"
cluster_name = "goodgo-production"
node_count = 5
instance_type = "t3.large"
```
### Multi-Environment Module
```hcl
# infra/terraform/modules/service/main.tf
variable "environment" {
type = string
}
variable "replicas" {
type = map(number)
default = {
staging = 2
production = 5
}
}
resource "kubernetes_deployment" "service" {
metadata {
name = "${var.service_name}-${var.environment}"
}
spec {
replicas = var.replicas[var.environment]
# ...
}
}
```
## Kubernetes Operators
### Custom Resource Definition
```yaml
# infra/operators/database-operator/crd.yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: databases.example.com
spec:
group: example.com
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
databaseName:
type: string
environment:
type: string
```
## Best Practices
1. **Version Control**: Keep all infrastructure in version control
2. **Modules**: Create reusable Terraform modules
3. **Testing**: Test infrastructure changes before applying
4. **GitOps**: Use GitOps for Kubernetes deployments
5. **Environment Isolation**: Separate environments completely
6. **State Management**: Use remote state backends
7. **Secrets**: Never commit secrets, use secrets managers
## Common Mistakes
1. **Committing Secrets**: Exposed credentials
```hcl
# ❌ BAD: Hardcoded secrets
password = "my-secret-password"
# ✅ GOOD: Use variables + secrets manager
password = var.db_password # From env or secrets manager
```
2. **No Remote State**: State conflicts in team
```hcl
# ❌ BAD: Local state
# (no backend config)
# ✅ GOOD: Remote state
terraform {
backend "s3" {
bucket = "terraform-state"
key = "staging/terraform.tfstate"
}
}
```
3. **No State Locking**: Concurrent modifications
```hcl
# ✅ Enable state locking
backend "s3" {
dynamodb_table = "terraform-locks"
}
```
4. **Applying Without Plan Review**: Unexpected changes
```bash
# ❌ BAD: Direct apply
terraform apply
# ✅ GOOD: Plan first, review, then apply
terraform plan -out=tfplan
terraform show tfplan # Review
terraform apply tfplan
```
## Quick Reference
| Tool | Purpose | State |
|------|---------|-------|
| **Terraform** | Cloud resources | Stateful |
| **Kubernetes** | Container orchestration | Declarative |
| **Helm** | K8s package manager | Template |
| **ArgoCD/Flux** | GitOps deployment | Git-synced |
**Terraform Commands:**
```bash
terraform init # Initialize
terraform validate # Validate syntax
terraform plan # Preview changes
terraform apply # Apply changes
terraform destroy # Remove resources
terraform state list # List state resources
```
**Module Structure:**
```
modules/service/
├── main.tf # Resources
├── variables.tf # Input variables
├── outputs.tf # Output values
└── README.md # Documentation
```
**Environment Pattern:**
```
environments/
├── staging/
│ ├── main.tf
│ └── terraform.tfvars
└── production/
├── main.tf
└── terraform.tfvars
```
**GitOps Workflow:**
```
Git Push → CI Validates → ArgoCD Syncs → K8s Applied
Auto-heal if drift
```
**Best Practices Checklist:**
- [ ] Remote state backend configured
- [ ] State locking enabled
- [ ] No secrets in code
- [ ] Modules for reusable components
- [ ] Environment-specific tfvars
- [ ] PR review for all changes
## Resources
- [Terraform Documentation](https://www.terraform.io/docs)
- [ArgoCD Documentation](https://argo-cd.readthedocs.io/)
- [Flux Documentation](https://fluxcd.io/docs/)
- [Deployment Kubernetes](../deployment-kubernetes/SKILL.md) - K8s patterns
- [Project Rules](../project-rules/SKILL.md) - GoodGo standards

View File

@@ -1,225 +0,0 @@
---
trigger: always_on
---
# 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
## Protocol Comparison
| Protocol | Use Case | Latency | Complexity | Best For |
|----------|----------|---------|------------|----------|
| **REST** | External APIs, CRUD | Medium | Low | Browser clients, simple APIs |
| **gRPC** | Internal high-perf | Low | High | Service-to-service, streaming |
| **GraphQL** | Flexible queries | Medium | Medium | Mobile apps, complex data |
### Protocol Selection Guidelines
```
External/Public API → REST
Internal service-to-service → gRPC (performance) or REST (simplicity)
Complex data fetching → GraphQL
Real-time streaming → gRPC or WebSocket
```
## HTTP/REST Client Pattern
```typescript
import axios from 'axios';
const client = axios.create({
baseURL: process.env.USER_SERVICE_URL,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'x-service-auth': process.env.INTERNAL_API_KEY,
},
});
// Add correlation ID for tracing
client.interceptors.request.use((config) => {
config.headers['x-correlation-id'] = generateCorrelationId();
return config;
});
```
## gRPC Pattern
### Proto Definition
```protobuf
syntax = "proto3";
package goodgo.user.v1;
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc StreamUserUpdates(StreamRequest) returns (stream UserUpdate);
}
message GetUserRequest { string user_id = 1; }
message GetUserResponse { User user = 1; }
```
### Client Usage
```typescript
const client = new GrpcClient({
protoPath: './proto/user_service.proto',
packageName: 'goodgo.user.v1',
serviceName: 'UserService',
serverUrl: 'localhost:50051',
});
const user = await client.call('getUser', { user_id: '123' });
```
## GraphQL Pattern
### Client Usage
```typescript
const client = new GraphQLClient(process.env.USER_SERVICE_GRAPHQL_URL, {
headers: { 'x-service-auth': process.env.INTERNAL_API_KEY },
});
const GET_USER = `query GetUser($id: ID!) { user(id: $id) { id email name } }`;
const data = await client.request(GET_USER, { id: '123' });
```
## Service Authentication
### Internal Auth Middleware
```typescript
export const internalAuthMiddleware = (req, res, next) => {
const token = req.headers['x-service-auth'];
if (token !== process.env.INTERNAL_API_KEY) {
return res.status(403).json({
success: false,
error: { code: 'INVALID_SERVICE_AUTH', message: 'Invalid authentication' }
});
}
next();
};
```
### Essential Headers
| Header | Purpose |
|--------|---------|
| `x-service-auth` | Internal authentication token |
| `x-correlation-id` | Request tracing across services |
| `x-request-id` | Unique request identification |
## Best Practices
### Performance Optimization
- **Connection Pooling**: Reuse HTTP connections
- **Keep-Alive**: Enable persistent connections
- **Compression**: Use gzip for HTTP responses
- **Timeouts**: Always set appropriate timeouts
- **Circuit Breaker**: Prevent cascade failures
### Security
- **Service Authentication**: Always authenticate internal calls
- **TLS/mTLS**: Use TLS for all communication
- **Secrets Management**: Use environment variables
- **Rate Limiting**: Implement for service clients
### Observability
- **Logging**: Log all calls with correlation IDs
- **Metrics**: Track duration, success rate, errors
- **Tracing**: Add distributed tracing
- **Health Checks**: Monitor service health
## Common Mistakes
1. **No Service Authentication**
```typescript
// BAD: No auth header
await client.get('/api/users');
// GOOD: Include service auth
await client.get('/api/users', {
headers: { 'x-service-auth': process.env.INTERNAL_API_KEY }
});
```
2. **Missing Timeouts**
```typescript
// BAD: No timeout
await axios.get(url);
// GOOD: Set timeout
await axios.get(url, { timeout: 5000 });
```
3. **No Circuit Breaker**
```typescript
// GOOD: Use circuit breaker for external calls
await circuitBreaker.fire(() => serviceClient.get('/api/users'));
```
4. **Hardcoded Service URLs**
```typescript
// BAD
const url = 'http://user-service:5000';
// GOOD
const url = process.env.USER_SERVICE_URL;
```
## Quick Reference
**HTTP Client Setup:**
```typescript
const client = axios.create({
baseURL: process.env.SERVICE_URL,
timeout: 5000,
headers: { 'x-service-auth': process.env.INTERNAL_API_KEY }
});
```
**gRPC Client Setup:**
```typescript
const client = new GrpcClient({
protoPath: './proto/service.proto',
serviceName: 'UserService',
serverUrl: 'localhost:50051'
});
```
**GraphQL Client Setup:**
```typescript
const client = new GraphQLClient(endpoint, {
headers: { 'x-service-auth': process.env.INTERNAL_API_KEY }
});
```
**Error Classes:**
```typescript
ServiceUnavailableError // Service is down
ServiceTimeoutError // Request timeout
ServiceError // Generic service error
```
## Resources
- [Detailed Reference](./references/REFERENCE.md) - Full code examples and implementation details
- [gRPC Documentation](https://grpc.io/docs/) - Official gRPC documentation
- [GraphQL Documentation](https://graphql.org/learn/) - GraphQL learning resources
- [Protocol Buffers](https://developers.google.com/protocol-buffers) - Protocol Buffer guide
- [Resilience Patterns](../resilience-patterns/SKILL.md) - Circuit breaker, retry patterns
- [Security](../security/SKILL.md) - Authentication, authorization patterns
- [API Design](../api-design/SKILL.md) - RESTful API patterns
- [Project Rules](../project-rules/SKILL.md) - GoodGo coding standards

View File

@@ -1,356 +0,0 @@
---
trigger: always_on
---
# 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
```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
```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
```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

View File

@@ -1,314 +0,0 @@
---
trigger: always_on
---
# 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
```
## 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' });
}
};
};
```
### 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);
}
};
};
```
### 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 });
}));
```
### 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: unknown) {
const transformed = {
success: true,
data,
timestamp: new Date().toISOString(),
};
return originalJson(transformed);
};
next();
};
};
```
### 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();
};
```
## 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)`.
## Quick Reference
**Middleware Order (Critical):**
```
1. Security (helmet, cors)
2. Rate Limiting
3. Correlation ID
4. Body Parsing (json, urlencoded)
5. Request Logging
6. Metrics
7. Routes
8. Error Handling (LAST)
```
| Middleware Type | Signature | When to Use |
|-----------------|-----------|-------------|
| **Standard** | `(req, res, next) => void` | Sync operations |
| **Async** | `async (req, res, next) => Promise<void>` | Async operations |
| **Error** | `(err, req, res, next) => void` | Error handling |
**Common Patterns:**
```typescript
// Async wrapper
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
// Conditional middleware
const conditionalMiddleware = (condition, middleware) =>
condition ? middleware : (req, res, next) => next();
// Factory pattern
const authenticate = (options = {}) => async (req, res, next) => { /* ... */ };
```
**Essential Imports:**
```typescript
import { Request, Response, NextFunction } from 'express';
import { z, ZodError } from 'zod';
import { logger } from '@goodgo/logger';
```
## Resources
- [Express Middleware](https://expressjs.com/en/guide/writing-middleware.html) - Express middleware guide
- [Error Handling](../error-handling-patterns/SKILL.md) - Error middleware patterns
- [Security](../security/SKILL.md) - Auth middleware patterns
- [API Design](../api-design/SKILL.md) - Request/response patterns
- [Resilience Patterns](../resilience-patterns/SKILL.md) - Circuit breaker middleware
- [Observability & Monitoring](../observability-monitoring/SKILL.md) - Logging middleware
- [Project Rules](../project-rules/SKILL.md) - GoodGo standards

View File

@@ -1,195 +0,0 @@
---
trigger: always_on
---
# 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)
## Key Patterns
### Structured Logging
```typescript
import winston from 'winston';
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: { service: process.env.SERVICE_NAME }
});
// Request logging middleware
export const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
logger.info('HTTP Request', {
method: req.method, url: req.url,
status: res.statusCode, duration: Date.now() - start,
correlationId: req.headers['x-correlation-id']
});
});
next();
};
```
### Metrics Collection
```typescript
import { Counter, Histogram, Gauge } from 'prom-client';
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']
});
```
### Distributed Tracing
```typescript
import { NodeSDK } from '@opentelemetry/sdk-node';
import { trace, SpanStatusCode } from '@opentelemetry/api';
export const tracedOperation = async (name: string, fn: Function) => {
const span = trace.getTracer('app').startSpan(name);
try {
const result = await fn();
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error) {
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
throw error;
} finally {
span.end();
}
};
```
### Health Checks
```typescript
// Liveness - is the service running?
app.get('/health/live', (req, res) => res.json({ status: 'ok' }));
// Readiness - is the service ready for traffic?
app.get('/health/ready', async (req, res) => {
const dbOk = await prisma.$queryRaw`SELECT 1`.then(() => true).catch(() => false);
const redisOk = await redis.ping().then(() => true).catch(() => false);
const ready = dbOk && redisOk;
res.status(ready ? 200 : 503).json({ ready, db: dbOk, redis: redisOk });
});
```
## Best Practices
- **Logging**: Use structured JSON format with correlation IDs
- **Metrics**: Use standard types (Counter, Gauge, Histogram) with low-cardinality labels
- **Tracing**: Add traces for critical operations, sample appropriately
- **Alerting**: Alert on symptoms, include runbook links, avoid alert fatigue
- **Security**: Never log sensitive data (passwords, tokens, PII)
## Common Mistakes
1. **Logging Sensitive Data**: Exposing PII in logs
```typescript
// BAD: logger.info('User login', { email, password, token });
// GOOD: logger.info('User login', { email, userId });
```
2. **High Cardinality Labels**: Too many metric label values
```typescript
// BAD: httpRequests.labels(method, route, userId).inc();
// GOOD: httpRequests.labels(method, route, statusCode).inc();
```
3. **No Correlation IDs**: Can't trace requests across services
```typescript
// GOOD: Include correlationId in all logs
logger.info('Processing', { correlationId: req.headers['x-correlation-id'] });
```
4. **Wrong Log Levels**: Using ERROR for non-errors
```typescript
// BAD: logger.error('User not found');
// GOOD: logger.info('User not found', { userId });
```
5. **No Health Checks**: Service status unknown
```typescript
// GOOD: Implement both endpoints
app.get('/health/live', livenessCheck);
app.get('/health/ready', readinessCheck);
```
## Quick Reference
| Pillar | Tool | Endpoint |
|--------|------|----------|
| **Logs** | Winston/Loki | stdout -> Loki |
| **Metrics** | Prometheus | `/metrics` |
| **Traces** | Jaeger | `http://jaeger:14268` |
**Log Levels:**
| Level | When to Use |
|-------|-------------|
| `error` | Errors requiring attention |
| `warn` | Potential issues, degradation |
| `info` | Business events, state changes |
| `debug` | Development details |
**Essential Metrics:**
```typescript
rate(http_requests_total[5m]) // Request rate
rate(http_requests_total{status=~"5.."}[5m]) // Error rate
histogram_quantile(0.95, http_request_duration_bucket) // Latency p95
```
**Health Check Endpoints:**
| Endpoint | Purpose | Used By |
|----------|---------|---------|
| `/health/live` | Is process running? | K8s liveness probe |
| `/health/ready` | Ready for traffic? | K8s readiness probe |
| `/health` | Full status | Monitoring |
## Resources
- [OpenTelemetry](https://opentelemetry.io/docs/) - Distributed tracing standard
- [Prometheus](https://prometheus.io/docs/) - Metrics and alerting
- [Grafana](https://grafana.com/docs/) - Visualization
- [Detailed Code Examples](./references/REFERENCE.md)
- [Deployment Kubernetes](../deployment-kubernetes/SKILL.md) - K8s health probes
- [Resilience Patterns](../resilience-patterns/SKILL.md) - Circuit breaker metrics

View File

@@ -1,457 +0,0 @@
---
trigger: always_on
---
# 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
- Identifyings N+1 query problems
- Optimizing batch operations
- Improving application startup time
- Reducing memory usage
- Optimizing API response times
## Core Concepts
### Performance Metrics
1. **Response Time**: Time to process request
2. **Throughput**: Requests processed per second
3. **Memory Usage**: Memory consumption
4. **CPU Usage**: CPU utilization
5. **Database Query Time**: Query execution time
### Optimization Areas
- Database queries
- Memory management
- Connection pooling
- Caching
- Batch processing
- Lazy loading
## Database Query Optimization
### Query Analysis
```typescript
// src/core/db/query-analyzer.ts
// EN: Query performance analyzer
// VI: Phân tích hiệu suất query
import { PrismaClient } from '@prisma/client';
import { logger } from '@goodgo/logger';
export class QueryAnalyzer {
constructor(private prisma: PrismaClient) {}
/**
* EN: Analyze query performance
* VI: Phân tích hiệu suất query
*/
async analyzeQuery(query: string): Promise<any> {
const result = await this.prisma.$queryRawUnsafe(`EXPLAIN ANALYZE ${query}`);
logger.info('Query analysis', { query, result });
return result;
}
/**
* EN: Check for missing indexes
* VI: Kiểm tra indexes bị thiếu
*/
async checkIndexes(tableName: string): Promise<any> {
const indexes = await this.prisma.$queryRawUnsafe(`
SELECT * FROM pg_indexes WHERE tablename = '${tableName}'
`);
return indexes;
}
}
```
### N+1 Query Prevention
```typescript
// EN: Bad: N+1 queries
// VI: Xấu: N+1 queries
async function getUsersWithOrdersBad(): Promise<User[]> {
const users = await userRepository.findAll();
for (const user of users) {
user.orders = await orderRepository.findByUserId(user.id); // N+1!
}
return users;
}
// EN: Good: Use include/join
// VI: Tốt: Sử dụng include/join
async function getUsersWithOrdersGood(): Promise<User[]> {
return await userRepository.findAll({
include: {
orders: true, // EN: Single query with join / VI: Single query với join
},
});
}
```
### Batch Operations
```typescript
// src/core/db/batch-operations.ts
// EN: Batch database operations
// VI: Batch database operations
export class BatchOperations {
/**
* EN: Batch create operations
* VI: Batch create operations
*/
async batchCreate<T>(items: T[], batchSize: number = 100): Promise<T[]> {
const results: T[] = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await prisma.$transaction(
batch.map((item) => prisma.item.create({ data: item }))
);
results.push(...batchResults);
}
return results;
}
/**
* EN: Batch update operations
* VI: Batch update operations
*/
async batchUpdate(
updates: Array<{ id: string; data: any }>,
batchSize: number = 100
): Promise<void> {
for (let i = 0; i < updates.length; i += batchSize) {
const batch = updates.slice(i, i + batchSize);
await prisma.$transaction(
batch.map(({ id, data }) => prisma.item.update({ where: { id }, data }))
);
}
}
}
```
## Memory Leak Detection
### Memory Profiling
```typescript
// src/core/performance/memory-profiler.ts
// EN: Memory leak detection
// VI: Phát hiện memory leak
import { performance } from 'perf_hooks';
import { logger } from '@goodgo/logger';
export class MemoryProfiler {
private baseline?: NodeJS.MemoryUsage;
private interval?: NodeJS.Timeout;
/**
* EN: Start memory profiling
* VI: Bắt đầu memory profiling
*/
start(): void {
this.baseline = process.memoryUsage();
this.interval = setInterval(() => {
const current = process.memoryUsage();
const delta = {
heapUsed: current.heapUsed - (this.baseline?.heapUsed || 0),
heapTotal: current.heapTotal - (this.baseline?.heapTotal || 0),
external: current.external - (this.baseline?.external || 0),
};
logger.info('Memory usage', {
current: {
heapUsed: `${(current.heapUsed / 1024 / 1024).toFixed(2)} MB`,
heapTotal: `${(current.heapTotal / 1024 / 1024).toFixed(2)} MB`,
},
delta: {
heapUsed: `${(delta.heapUsed / 1024 / 1024).toFixed(2)} MB`,
},
});
// EN: Alert if memory growth is excessive
// VI: Cảnh báo nếu memory tăng quá mức
if (delta.heapUsed > 100 * 1024 * 1024) {
// 100MB growth
logger.warn('Potential memory leak detected', { delta });
}
}, 60000); // EN: Check every minute / VI: Kiểm tra mỗi phút
}
stop(): void {
if (this.interval) {
clearInterval(this.interval);
}
}
/**
* EN: Force garbage collection (if --expose-gc flag is set)
* VI: Ép garbage collection (nếu --expose-gc flag được set)
*/
forceGC(): void {
if (global.gc) {
global.gc();
}
}
}
```
## Connection Pooling
### Database Connection Pool
```typescript
// src/config/database.config.ts
// EN: Optimize Prisma connection pool
// VI: Tối ưu Prisma connection pool
export const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
});
// EN: Connection pool configuration in DATABASE_URL
// VI: Cấu hình connection pool trong DATABASE_URL
// DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=20&pool_timeout=10"
```
### HTTP Connection Pool
```typescript
// src/core/clients/http-pool.config.ts
// EN: HTTP connection pooling with keep-alive
// VI: HTTP connection pooling với keep-alive
import axios from 'axios';
import https from 'https';
const httpAgent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
maxSockets: 50,
maxFreeSockets: 10,
timeout: 60000,
});
export const httpClient = axios.create({
httpAgent,
httpsAgent: httpAgent,
timeout: 30000,
});
```
## Performance Monitoring
```typescript
// src/core/performance/performance-monitor.ts
// EN: Performance monitoring middleware
// VI: Middleware giám sát hiệu suất
import { Request, Response, NextFunction } from 'express';
import { performance } from 'perf_hooks';
import { logger } from '@goodgo/logger';
export function performanceMonitor(req: Request, res: Response, next: NextFunction) {
const start = performance.now();
const startMemory = process.memoryUsage();
res.on('finish', () => {
const duration = performance.now() - start;
const endMemory = process.memoryUsage();
logger.info('Request performance', {
method: req.method,
path: req.path,
status: res.statusCode,
duration: `${duration.toFixed(2)}ms`,
memoryDelta: `${((endMemory.heapUsed - startMemory.heapUsed) / 1024 / 1024).toFixed(2)} MB`,
});
// EN: Alert on slow requests
// VI: Cảnh báo trên requests chậm
if (duration > 1000) {
logger.warn('Slow request detected', {
path: req.path,
duration: `${duration.toFixed(2)}ms`,
});
}
});
next();
}
```
## Caching Optimization
```typescript
// src/core/cache/cache-optimizer.ts
// EN: Cache optimization strategies
// VI: Chiến lược tối ưu cache
export class CacheOptimizer {
/**
* EN: Preload frequently accessed data
* VI: Tải trước dữ liệu thường truy cập
*/
async preloadHotData(keys: string[]): Promise<void> {
for (const key of keys) {
// EN: Fetch and cache data
// VI: Fetch và cache dữ liệu
await cacheService.getOrSet(key, async () => {
return await this.fetchData(key);
}, 3600);
}
}
/**
* EN: Batch cache operations
* VI: Batch cache operations
*/
async batchGet(keys: string[]): Promise<Map<string, any>> {
const results = new Map<string, any>();
const missing: string[] = [];
// EN: Check cache for all keys
// VI: Kiểm tra cache cho tất cả keys
for (const key of keys) {
const cached = await cacheService.get(key);
if (cached) {
results.set(key, cached);
} else {
missing.push(key);
}
}
// EN: Fetch missing data
// VI: Fetch dữ liệu thiếu
if (missing.length > 0) {
const data = await this.fetchBatch(missing);
for (const [key, value] of Object.entries(data)) {
results.set(key, value);
await cacheService.set(key, value, 3600);
}
}
return results;
}
}
```
## Best Practices
1. **Database**: Use indexes, avoid N+1 queries, use batch operations
2. **Memory**: Monitor memory usage, detect leaks, use streaming for large data
3. **Caching**: Cache frequently accessed data, use appropriate TTLs
4. **Connection Pooling**: Configure pool sizes appropriately
5. **Profiling**: Profile regularly to identify bottlenecks
6. **Monitoring**: Monitor performance metrics continuously
## Common Mistakes
1. **N+1 Queries**: Fetching related data in loops
```typescript
// ❌ BAD: N+1
for (const user of users) {
user.orders = await orderRepo.findByUserId(user.id);
}
// ✅ GOOD: Use include
const users = await userRepo.findAll({ include: { orders: true } });
```
2. **No Connection Pooling**: Creating new connections per request
```typescript
// ❌ BAD: No pooling
const client = new PrismaClient(); // per request
// ✅ GOOD: Reuse singleton
import { prisma } from './db'; // shared instance
```
3. **Missing Indexes**: Slow queries on frequently searched columns
```prisma
// ✅ Add indexes in schema
@@index([createdAt])
@@index([userId, status])
```
4. **Unbounded Queries**: Fetching all data without limits
```typescript
// ❌ BAD
const all = await repo.findAll();
// ✅ GOOD
const page = await repo.findAll({ take: 100, skip: offset });
```
## Quick Reference
| Metric | Target | Alert Threshold |
|--------|--------|-----------------|
| **Response Time (p95)** | <200ms | >500ms |
| **Memory Usage** | <70% | >85% |
| **CPU Usage** | <60% | >80% |
| **Query Time** | <50ms | >200ms |
**Profiling Commands:**
```bash
# CPU profiling
node --prof app.js
node --prof-process isolate-*.log > profile.txt
# Memory snapshot
node --inspect app.js # Use Chrome DevTools
# Clinic.js
npx clinic doctor -- node app.js
npx clinic flame -- node app.js
```
**Prisma Query Optimization:**
```typescript
// Select only needed fields
await prisma.user.findMany({ select: { id: true, email: true } });
// Use include for relations
await prisma.user.findMany({ include: { posts: true } });
// Batch operations
await prisma.$transaction([...operations]);
// Raw query for complex cases
await prisma.$queryRaw`SELECT ... FROM ...`;
```
**Connection Pool Config:**
```bash
# In DATABASE_URL
?connection_limit=20&pool_timeout=10
```
**Performance Checklist:**
- [ ] Add database indexes for frequent queries
- [ ] Use pagination for list endpoints
- [ ] Implement caching for hot data
- [ ] Enable connection pooling
- [ ] Profile and monitor regularly
- [ ] Use batch operations for bulk updates
## Resources
- [Node.js Performance](https://nodejs.org/en/docs/guides/simple-profiling/)
- [Prisma Performance](https://www.prisma.io/docs/guides/performance-and-optimization)
- [Caching Patterns](../caching-patterns/SKILL.md) - Caching strategies
- [Observability & Monitoring](../observability-monitoring/SKILL.md) - Monitoring patterns
- [Database & Prisma](../database-prisma/SKILL.md) - Database patterns

View File

@@ -1,329 +0,0 @@
---
trigger: always_on
---
# 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
## 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`
## 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
```
## Common Mistakes
1. **Wrong Directory Structure**: Placing files in wrong locations
```
# ❌ BAD: Controllers outside modules
src/controllers/user.controller.ts
# ✅ GOOD: Controllers inside modules
src/modules/user/user.controller.ts
```
2. **Inconsistent Naming**: Mixing naming conventions
```typescript
// ❌ BAD: Mixed naming
UserService.ts
user_repository.ts
userController.ts
// ✅ GOOD: Consistent kebab-case
user.service.ts
user.repository.ts
user.controller.ts
```
3. **Wrong Response Format**: Not following API response standard
```typescript
// ❌ BAD: Inconsistent format
res.json({ user: data });
res.json({ error: "Not found" });
// ✅ GOOD: Standard format
res.json({ success: true, data: user });
res.json({ success: false, error: { code: 'NOT_FOUND', message: 'User not found' } });
```
4. **Missing Bilingual Comments**: Only one language
```typescript
// ❌ BAD: English only
// Initialize database
// ✅ GOOD: Bilingual
// EN: Initialize database connection
// VI: Khởi tạo kết nối database
```
## Quick Reference
| Category | Pattern/Standard |
|----------|-----------------|
| **Service structure** | `src/{config,modules,middlewares,routes,main.ts}` |
| **File naming** | `kebab-case.type.ts` (e.g., `user.controller.ts`) |
| **Package naming** | `@goodgo/package-name` |
| **API response** | `{ success: true, data }` / `{ success: false, error: { code, message } }` |
| **Password hashing** | bcrypt, cost 12 |
| **JWT tokens** | Access: 15min, Refresh: 7 days |
| **Coverage target** | >80% for unit tests |
| **Commits** | `type(scope): subject` (conventional commits) |
**Common Commands:**
```bash
# Add dependency
pnpm --filter @goodgo/service-name add package-name
# Run migrations
pnpm --filter @goodgo/service-name prisma migrate dev
# Run tests
pnpm --filter @goodgo/service-name test
# Start dev server
pnpm --filter @goodgo/service-name dev
```
## 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,273 +0,0 @@
---
trigger: always_on
---
# 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
### 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
## 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;
});
```
### 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.
## Quick Reference
**BaseRepository Methods:**
| Method | Returns | Description |
|--------|---------|-------------|
| `findById(id)` | `T \| null` | Find by primary key |
| `findByUnique(field, value)` | `T \| null` | Find by unique field |
| `findAll(options)` | `T[]` | Find with filters |
| `create(data)` | `T` | Create entity |
| `update(id, data)` | `T` | Update entity |
| `delete(id)` | `void` | Delete entity |
| `count(where)` | `number` | Count entities |
| `exists(id)` | `boolean` | Check existence |
| `transaction(cb)` | `R` | Execute transaction |
**Query Options:**
```typescript
// Pagination
{ skip: 0, take: 10 }
// Filtering
{ where: { isActive: true } }
// Sorting
{ orderBy: { createdAt: 'desc' } }
// Relations
{ include: { roles: true } }
// Field selection
{ select: { id: true, email: true } }
```
**Repository Template:**
```typescript
export class EntityRepository extends BaseRepository<Entity, CreateDto, UpdateDto> {
constructor(prisma: PrismaClient) {
super(prisma, 'entity');
}
// Custom query methods
async findByField(value: string): Promise<Entity | null> {
return this.prisma.entity.findUnique({ where: { field: value } });
}
}
```
**Essential Imports:**
```typescript
import { PrismaClient } from '@prisma/client';
import { BaseRepository, IRepository } from '../modules/common/repository';
import { DatabaseError } from '../errors/http-error';
```
## 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,421 +0,0 @@
---
trigger: always_on
---
# 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:
```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:
```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:
```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();
}
}
```
### Bulkhead Pattern
Isolate failures to prevent spread:
```typescript
import PQueue from 'p-queue';
// Create separate queues for different operations
const externalApiQueue = new PQueue({
concurrency: 10, // Max 10 concurrent calls
timeout: 30000 // 30 second timeout per operation
});
const databaseQueue = new PQueue({
concurrency: 20
});
// Usage - operations are isolated
async function fetchExternalData(id: string) {
return externalApiQueue.add(async () => {
return await externalApi.getData(id);
});
}
async function queryDatabase(query: string) {
return databaseQueue.add(async () => {
return await database.execute(query);
});
}
```
### Combined Resilience Service
```typescript
// src/core/resilience/resilience.service.ts
import CircuitBreaker from 'opossum';
import { logger } from '@goodgo/logger';
interface ResilienceOptions {
timeout?: number;
maxRetries?: number;
circuitBreaker?: boolean;
fallback?: () => Promise<any>;
}
export class ResilienceService {
async execute<T>(
operation: () => Promise<T>,
name: string,
options: ResilienceOptions = {}
): Promise<T> {
const {
timeout = 5000,
maxRetries = 3,
circuitBreaker = true,
fallback
} = options;
let fn = operation;
// Wrap with timeout
fn = () => this.withTimeout(operation(), timeout);
// Wrap with retry
fn = () => this.retryWithBackoff(fn, maxRetries);
// Wrap with circuit breaker
if (circuitBreaker) {
const breaker = this.createCircuitBreaker(fn, name);
try {
return await breaker.fire();
} catch (error) {
if (fallback) {
logger.warn(`${name}: Using fallback`, { error: error.message });
return await fallback();
}
throw error;
}
}
try {
return await fn();
} catch (error) {
if (fallback) {
return await fallback();
}
throw error;
}
}
private withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
const timeout = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Operation timeout')), ms);
});
return Promise.race([promise, timeout]);
}
private async retryWithBackoff<T>(
fn: () => Promise<T>,
maxRetries: number
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = 1000 * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Retry exhausted');
}
private createCircuitBreaker<T>(
fn: () => Promise<T>,
name: string
): CircuitBreaker<[], T> {
return new CircuitBreaker(fn, {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000,
name
});
}
}
// Usage
const resilience = new ResilienceService();
const result = await resilience.execute(
() => externalApi.fetchUser(userId),
'fetch-user',
{
timeout: 3000,
maxRetries: 2,
fallback: () => Promise.resolve({ id: userId, name: 'Unknown' })
}
);
```
### Health Check with Resilience
```typescript
// src/health/health.controller.ts
export class HealthController {
async checkDependencies(): Promise<HealthStatus> {
const checks = await Promise.allSettled([
this.checkDatabase(),
this.checkRedis(),
this.checkExternalApi()
]);
const results = {
database: checks[0].status === 'fulfilled' ? 'healthy' : 'unhealthy',
redis: checks[1].status === 'fulfilled' ? 'healthy' : 'unhealthy',
externalApi: checks[2].status === 'fulfilled' ? 'healthy' : 'degraded'
};
// Service is healthy even if external API is down (graceful degradation)
const isHealthy = results.database === 'healthy' && results.redis === 'healthy';
return {
status: isHealthy ? 'healthy' : 'unhealthy',
dependencies: results
};
}
}
```
## 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)
```typescript
// ❌ BAD: Retry all errors
catch (error) {
await retry(operation);
}
// ✅ GOOD: Only retry transient errors
catch (error) {
if (isTransientError(error)) {
await retry(operation);
} else {
throw error;
}
}
```
2. **No Timeout**: Missing timeouts on external calls
```typescript
// ❌ BAD: No timeout
const data = await externalApi.fetch();
// ✅ GOOD: With timeout
const data = await withTimeout(externalApi.fetch(), 5000);
```
3. **No Fallback**: No graceful degradation strategy
```typescript
// ❌ BAD: Service crashes if dependency fails
const user = await userService.get(id);
// ✅ GOOD: Fallback to cached/default data
const user = await userService.get(id).catch(() => cachedUser);
```
4. **Too Many Retries**: Excessive retries causing performance issues
```typescript
// ❌ BAD: Too many retries with short delay
retry(fn, { maxRetries: 10, delay: 100 });
// ✅ GOOD: Limited retries with exponential backoff
retry(fn, { maxRetries: 3, baseDelay: 1000, exponential: true });
```
5. **Circuit Breaker Misconfiguration**: Wrong thresholds
```typescript
// ❌ BAD: Circuit opens too easily or never
{ errorThresholdPercentage: 5 } // Opens after 5% errors
{ errorThresholdPercentage: 99 } // Almost never opens
// ✅ GOOD: Balanced threshold
{ errorThresholdPercentage: 50, resetTimeout: 30000 }
```
## Quick Reference
| Pattern | Use Case | Key Config |
|---------|----------|------------|
| **Circuit Breaker** | External API calls | threshold: 50%, reset: 30s |
| **Retry** | Transient failures | max: 3, exponential backoff |
| **Timeout** | All external calls | 3-5s for API, 30s for batch |
| **Bulkhead** | Resource isolation | 10-20 concurrent ops |
| **Fallback** | Critical operations | Cache, default, or degraded |
**Opossum Circuit Breaker States:**
```
CLOSED → (errors exceed threshold) → OPEN
OPEN → (reset timeout expires) → HALF-OPEN
HALF-OPEN → (success) → CLOSED
HALF-OPEN → (failure) → OPEN
```
**Retry Delays (Exponential Backoff):**
```
Attempt 1: 1s
Attempt 2: 2s
Attempt 3: 4s
Attempt 4: 8s
```
**Essential Imports:**
```typescript
import CircuitBreaker from 'opossum';
import PQueue from 'p-queue';
import { logger } from '@goodgo/logger';
```
## Resources
- [Opossum Documentation](https://nodeshift.dev/opossum/) - Circuit breaker library
- [Microsoft Resilience Patterns](https://docs.microsoft.com/en-us/azure/architecture/patterns/category/resiliency)
- [API Gateway Advanced](../api-gateway-advanced/SKILL.md) - Gateway circuit breaker
- [Observability & Monitoring](../observability-monitoring/SKILL.md) - Health checks, metrics
- [Event-Driven Architecture](../event-driven-architecture/SKILL.md) - Event retry patterns
- [Error Handling Patterns](../error-handling-patterns/SKILL.md) - Error handling
- [Project Rules](../project-rules/SKILL.md) - GoodGo standards

View File

@@ -1,305 +0,0 @@
---
trigger: always_on
---
# 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
Extract tokens from Authorization header or cookies, verify with `jwtService.verifyAccessToken()`, and attach user payload to request. Return 401 for missing/invalid tokens.
```typescript
// Key pattern - see references/REFERENCE.md for full implementation
const authHeader = req.headers.authorization;
const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : req.cookies?.access_token;
const payload = await jwtService.verifyAccessToken(token);
req.user = { id: payload.sub, roles: payload.roles || [], permissions: payload.permissions || [] };
```
### Role-Based Authorization (RBAC)
Use `requireRole()` middleware for role checks and `requirePermission()` for fine-grained access control with `resource:action` format.
```typescript
// Usage pattern
router.post('/api/v1/users', authenticate(), requirePermission('users', 'create'), userController.create);
router.delete('/api/v1/admin', authenticate(), requireRole('admin', 'superadmin'), adminController.delete);
```
### Resource Ownership
Validate users can only access their own resources using `requireOwnership()` middleware.
## Data Protection
### Encryption
- Use AES-256-GCM for encrypting PII at rest
- Store encrypted data as `iv:tag:ciphertext` format
- Require 32+ character ENCRYPTION_KEY
### Password Hashing
- Always use bcrypt with cost factor 12 in production
- Never log passwords - sanitize before logging
### Token Storage
- Hash tokens with SHA-256 before database storage
- Use `crypto.randomBytes(32)` for secure token generation
## Input Validation
### Zod Schema Validation
Always validate inputs with Zod schemas before processing:
```typescript
const CreateUserDto = z.object({
email: z.string().email(),
password: z.string().min(8).regex(/[A-Z]/).regex(/[a-z]/).regex(/[0-9]/).regex(/[^A-Za-z0-9]/),
phone: z.string().regex(/^\+[1-9]\d{1,14}$/).optional(),
name: z.string().min(1).max(255)
});
// In controller
const dto = CreateUserDto.parse(req.body);
```
### File Upload Validation
- Check file size (max 10MB default)
- Validate MIME type against whitelist
- Verify content with `file-type` library to prevent MIME spoofing
### SQL Injection Prevention
Always use Prisma parameterized queries - never string concatenation for queries.
## Rate Limiting
Configure Redis-backed rate limiting for all endpoints:
| Limiter Type | Window | Max Requests | Use Case |
|-------------|--------|--------------|----------|
| Standard | 15 min | 100 | General API endpoints |
| Strict | 1 hour | 10 | Sensitive operations |
| Login | 15 min | 5 | Authentication endpoints |
```typescript
router.post('/api/v1/auth/login', loginLimiter, authController.login);
```
## Security Headers
Use Helmet middleware with CSP, HSTS, and additional headers:
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: DENY`
- `X-XSS-Protection: 1; mode=block`
- `Referrer-Policy: strict-origin-when-cross-origin`
## CORS Configuration
- Whitelist allowed origins from environment variables
- Enable credentials for authenticated requests
- Set `maxAge: 86400` (24 hours) for preflight caching
## Secrets Management
- Never hardcode secrets - use environment variables
- Validate secrets with Zod schema at startup
- Use secret managers in production (AWS Secrets Manager, Vault, etc.)
- Rotate secrets quarterly
```typescript
const secretsSchema = z.object({
JWT_SECRET: z.string().min(32),
DATABASE_URL: z.string().url(),
ENCRYPTION_KEY: z.string().min(32).optional()
});
```
## Audit Logging
Log all security-relevant events with sanitized details:
```typescript
await auditService.logSecurityEvent('LOGIN_SUCCESS', user.id, { email: user.email }, req);
await auditService.logSecurityEvent('PERMISSION_DENIED', user.id, { resource, action }, req);
```
Sanitize sensitive fields: password, token, secret, ssn, creditCard.
## Error Handling
- Log full errors internally
- Never expose user existence (use generic "Invalid credentials")
- Show stack traces only in development
- Return sanitized error codes in production
## Security Testing
Write tests for:
- SQL injection prevention
- XSS attack prevention
- Authentication enforcement
- Authorization enforcement
- Rate limiting effectiveness
## Best Practices
- All endpoints require authentication (except public)
- Authorization checks at every protected route
- Input validation with Zod on all user input
- Rate limiting on all endpoints
- Error messages sanitized in production
- PII encrypted at rest with AES-256-GCM
- Passwords hashed with bcrypt (cost 12+)
- Tokens hashed before database storage
- HTTPS enforced (TLS 1.2+)
- Security headers via Helmet
- Audit logging for security events
- Dependencies scanned for vulnerabilities
- File uploads validated (size, type, content)
## Common Mistakes
### 1. Weak Password Hashing
```typescript
// BAD: Low cost factor
const hash = await bcrypt.hash(password, 8);
// GOOD: Use cost 12
const hash = await bcrypt.hash(password, 12);
```
### 2. Hardcoded Secrets
```typescript
// BAD
const JWT_SECRET = "my-secret-key";
// GOOD
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) throw new Error('JWT_SECRET required');
```
### 3. Missing Input Validation
```typescript
// BAD
const user = await prisma.user.findUnique({ where: { id: req.params.id } });
// GOOD
const { id } = z.object({ id: z.string().cuid() }).parse(req.params);
const user = await prisma.user.findUnique({ where: { id } });
```
### 4. Logging Sensitive Data
```typescript
// BAD
logger.info('User login', { email, password });
// GOOD
logger.info('User login', { email, password: '[REDACTED]' });
```
### 5. Exposing User Existence
```typescript
// BAD
if (!user) throw new Error('User not found');
// GOOD
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
throw new Error('Invalid credentials');
}
```
## Quick Reference
| Security Area | Implementation |
|--------------|----------------|
| **Password hashing** | `bcrypt.hash(password, 12)` |
| **JWT Access Token** | 15 minutes expiry |
| **JWT Refresh Token** | 7 days expiry |
| **Rate limiting** | Standard: 100/15min, Strict: 10/hour, Login: 5/15min |
| **Encryption** | AES-256-GCM for PII |
| **Input validation** | Zod schemas, always parse before use |
| **SQL injection** | Use Prisma (parameterized by default) |
| **Security headers** | helmet middleware |
| **CORS** | Whitelist origins, credentials: true |
**Essential Imports:**
```typescript
import bcrypt from 'bcrypt';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import { z } from 'zod';
import { jwtService } from '@goodgo/auth-sdk';
```
## 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
## Resources
- [Detailed Code Examples](./references/REFERENCE.md) - Full implementation examples
- [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,20 +0,0 @@
---
trigger: always_on
---
# Service Discovery & Registry Patterns
## 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

View File

@@ -1,248 +0,0 @@
---
trigger: always_on
---
# 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 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:
```typescript
export class UserService {
constructor(
private userRepository: UserRepository,
private cacheService: CacheService
) {}
}
```
## Patterns
### 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:
```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
## Quick Reference
**Service Responsibilities:**
| Layer | Responsibility | Example |
|-------|----------------|---------|
| **Controller** | HTTP handling, validation | Parse request, send response |
| **Service** | Business logic | Validate rules, orchestrate |
| **Repository** | Data access | CRUD operations |
**Service Template:**
```typescript
export class EntityService {
constructor(
private repository: EntityRepository,
private cache?: CacheService
) {}
async findById(id: string): Promise<Entity> {
const entity = await this.repository.findById(id);
if (!entity) throw new NotFoundError('Entity');
return entity;
}
async create(data: CreateDto): Promise<Entity> {
// Business validation
await this.validateBusinessRules(data);
return this.repository.create(data);
}
}
```
**Dependency Injection Pattern:**
```typescript
// Module setup
const repository = new EntityRepository(prisma);
const service = new EntityService(repository, cacheService);
const controller = new EntityController(service);
```
**Common Patterns:**
| Pattern | When to Use |
|---------|-------------|
| **Caching** | Frequently accessed data |
| **Composition** | Complex operations across domains |
| **Validation** | Business rule enforcement |
| **Logging** | Audit and debugging |
## 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,179 +0,0 @@
---
trigger: always_on
---
# 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
| Type | Location | Speed | Dependencies |
|------|----------|-------|--------------|
| **Unit** | `*.test.ts` (next to source) | <1s | All mocked |
| **Integration** | `__tests__/` | 1-5s | Partial mocking |
| **E2E** | `__tests__/*.e2e.ts` | 5-10s | Test DB, mocked external |
## Key Patterns
### Jest Configuration
```typescript
// jest.config.ts
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.ts', '**/__tests__/**/*.e2e.ts'],
coverageThreshold: { global: { branches: 70, functions: 70, lines: 70 } },
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setupTests.ts'],
};
```
### Unit Test Pattern
```typescript
describe('FeatureService', () => {
let service: FeatureService;
let mockRepository: any;
beforeEach(() => {
mockRepository = { findById: jest.fn(), create: jest.fn() };
service = new FeatureService(mockRepository);
});
it('should return feature when found', async () => {
mockRepository.findById.mockResolvedValue({ id: '1', name: 'Test' });
const result = await service.findById('1');
expect(result).toEqual({ id: '1', name: 'Test' });
});
});
```
### E2E Test Pattern
```typescript
describe('POST /api/features', () => {
it('should create a new feature', async () => {
const response = await request
.post('/api/features')
.set('Authorization', 'Bearer valid-token')
.send({ name: 'New Feature' })
.expect(201);
expect(response.body).toMatchObject({ success: true, data: { name: 'New Feature' } });
});
});
```
### Mock Prisma
```typescript
import { mockDeep } from 'jest-mock-extended';
import { PrismaClient } from '@prisma/client';
export const prismaMock = mockDeep<PrismaClient>();
jest.mock('../prisma', () => ({ default: prismaMock }));
// Usage
prismaMock.user.create.mockResolvedValue({ id: '1', email: 'test@example.com' });
```
### Test Factory
```typescript
export class TestFactory {
static createUser(overrides = {}) {
return { id: 'test-user-1', email: 'test@example.com', ...overrides };
}
static createAuthToken(userId: string) {
return jwt.sign({ userId }, 'test-secret');
}
}
```
## Best Practices
- 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
## Common Mistakes
1. **Testing Implementation Details**: Tests break on refactoring
```typescript
// BAD: expect(service['privateMethod']()).toBe(true);
// GOOD: expect(await service.processOrder(order)).toEqual({ success: true });
```
2. **Shared Mutable State**: Tests affect each other
```typescript
// BAD: let counter = 0; (shared across tests)
// GOOD: beforeEach(() => { counter = 0; });
```
3. **Not Mocking External Services**: Tests are slow and flaky
```typescript
// BAD: await fetch('https://api.example.com/data');
// GOOD: jest.spyOn(httpClient, 'get').mockResolvedValue({ data: mockData });
```
4. **Missing Edge Cases**: Only testing happy path
```typescript
// GOOD: Include error cases
test('creates user', async () => { ... });
test('throws on duplicate email', async () => { ... });
test('validates email format', async () => { ... });
```
## Quick Reference
**Coverage Targets:**
- Global: 70%+ (branches, functions, lines)
- Critical paths: 90%+
- Repositories/Services: 80%+
**Essential Commands:**
```bash
pnpm test # Run all tests
pnpm test:watch # Watch mode
pnpm test:coverage # With coverage
pnpm test -- --runInBand # Sequential (for debugging)
pnpm test -- UserService # Run specific test file
```
**Mock Imports:**
```typescript
import { mockDeep } from 'jest-mock-extended';
import { prismaMock } from '../__mocks__/prisma';
import supertest from 'supertest';
```
**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
## Resources
- [Jest Documentation](https://jestjs.io/docs/getting-started) - Testing framework
- [Supertest](https://github.com/ladjs/supertest) - HTTP assertions
- [jest-mock-extended](https://github.com/marchaos/jest-mock-extended) - TypeScript mocks
- [Detailed Code Examples](./references/REFERENCE.md)
- [Database Prisma](../database-prisma/SKILL.md) - Database mocking patterns
- [Error Handling](../error-handling-patterns/SKILL.md) - Error testing