From a0dda79478aba167a59be44086463eb5e8edf2bb Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Thu, 1 Jan 2026 11:05:36 +0700 Subject: [PATCH] Add Quick Reference and Common Mistakes sections to 14 skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhanced skills with practical quick reference tables and common mistakes: Skills updated: - database-prisma: CRUD patterns, schema types, query examples - caching-patterns: TTL guidelines, key patterns, cache layers - observability-monitoring: Log levels, metrics, health checks - middleware-patterns: Middleware order, async patterns, imports - error-handling-patterns: Error classes, response format, codes - repository-pattern: BaseRepository methods, query options - deployment-kubernetes: kubectl commands, resource sizing - configuration-management: Config types, Zod validation - event-driven-architecture: Kafka concepts, event structure - performance-optimization: Profiling, connection pooling - service-layer-patterns: Layer responsibilities, DI patterns - data-consistency-patterns: Saga patterns, idempotency - inter-service-communication: Protocol selection, client setup - cicd-advanced-patterns: Deployment strategies, rollback All sections include: - Quick Reference tables with practical patterns - Common Mistakes with BAD/GOOD code examples - Essential commands and imports 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .cursor/skills/caching-patterns/SKILL.md | 45 ++++++++ .../skills/cicd-advanced-patterns/SKILL.md | 68 ++++++++++++ .../skills/configuration-management/SKILL.md | 78 +++++++++++++ .../skills/data-consistency-patterns/SKILL.md | 79 +++++++++++++ .cursor/skills/database-prisma/SKILL.md | 105 +++++++++++++++++- .cursor/skills/deployment-kubernetes/SKILL.md | 86 +++++++++++++- .../skills/error-handling-patterns/SKILL.md | 44 ++++++++ .../skills/event-driven-architecture/SKILL.md | 95 ++++++++++++++++ .../inter-service-communication/SKILL.md | 80 +++++++++++++ .cursor/skills/middleware-patterns/SKILL.md | 41 +++++++ .../skills/observability-monitoring/SKILL.md | 95 +++++++++++++++- .../skills/performance-optimization/SKILL.md | 90 +++++++++++++++ .cursor/skills/repository-pattern/SKILL.md | 54 +++++++++ .../skills/service-layer-patterns/SKILL.md | 47 ++++++++ 14 files changed, 1004 insertions(+), 3 deletions(-) diff --git a/.cursor/skills/caching-patterns/SKILL.md b/.cursor/skills/caching-patterns/SKILL.md index 9b7dc588..b08be128 100644 --- a/.cursor/skills/caching-patterns/SKILL.md +++ b/.cursor/skills/caching-patterns/SKILL.md @@ -275,6 +275,51 @@ async getWithCache(key: string): Promise { - 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(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 diff --git a/.cursor/skills/cicd-advanced-patterns/SKILL.md b/.cursor/skills/cicd-advanced-patterns/SKILL.md index eedf6e8e..a36c4c21 100644 --- a/.cursor/skills/cicd-advanced-patterns/SKILL.md +++ b/.cursor/skills/cicd-advanced-patterns/SKILL.md @@ -440,6 +440,74 @@ jobs: 5. **Health Checks**: Monitor health continuously 6. **Gates**: Use deployment gates for critical deployments +## Common Mistakes + +1. **No Rollback Plan**: Can't recover from failed deployment + ```yaml + # ✅ Always have rollback command ready + kubectl rollout undo deployment/service + ``` + +2. **Skipping Smoke Tests**: Catching issues too late + ```yaml + # ✅ Run smoke tests immediately after deploy + - name: Smoke Tests + run: ./scripts/smoke-tests.sh + ``` + +3. **100% Traffic Switch**: All-or-nothing failures + ```yaml + # ❌ BAD: Immediate full switch + # ✅ GOOD: Gradual rollout (10% → 50% → 100%) + ``` + +4. **No Health Monitoring**: Missing deployment issues + ```yaml + # ✅ Monitor health after deployment + - name: Monitor Health + run: kubectl rollout status deployment/service --timeout=5m + ``` + +## Quick Reference + +| Strategy | Risk | Downtime | Resource Cost | +|----------|------|----------|---------------| +| **Blue-Green** | Low | Zero | 2x (temporary) | +| **Canary** | Low | Zero | +10-20% | +| **Rolling** | Medium | Zero | 1x | +| **Recreate** | High | Yes | 1x | + +**Deployment Commands:** +```bash +# Apply deployment +kubectl apply -f kubernetes/ + +# Check rollout status +kubectl rollout status deployment/service + +# Rollback +kubectl rollout undo deployment/service + +# Canary traffic split (Istio) +kubectl apply -f virtualservice-canary.yaml +``` + +**GitHub Actions Triggers:** +```yaml +on: + push: + branches: [main] # Deploy to prod + tags: ['v*'] # Release + pull_request: + branches: [main] # PR checks +``` + +**Deployment Gates:** +``` +Build → Test → Security Scan → Deploy Staging +→ Smoke Tests → Manual Approval → Deploy Prod +``` + ## Resources - [Kubernetes Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) diff --git a/.cursor/skills/configuration-management/SKILL.md b/.cursor/skills/configuration-management/SKILL.md index 4a7bb107..dfc3f6ff 100644 --- a/.cursor/skills/configuration-management/SKILL.md +++ b/.cursor/skills/configuration-management/SKILL.md @@ -406,6 +406,84 @@ export function getAppConfig() { 5. **Reloading**: Support dynamic reloading where possible 6. **Monitoring**: Monitor configuration changes +## Common Mistakes + +1. **Not Validating Config**: App crashes with cryptic errors + ```typescript + // ❌ BAD: No validation + const port = process.env.PORT; + + // ✅ GOOD: Validate at startup + const config = AppConfigSchema.parse(process.env); + ``` + +2. **Committing Secrets**: Exposing credentials + ```bash + # ❌ BAD: Secrets in code + JWT_SECRET=my-secret-key + + # ✅ GOOD: Use .env.example with placeholders + JWT_SECRET= + ``` + +3. **Feature Flags Without Fallback**: Breaking when flag not found + ```typescript + // ❌ BAD: No fallback + if (await featureFlags.isEnabled('new-feature')) { ... } + + // ✅ GOOD: Default to disabled + if (await featureFlags.isEnabled('new-feature') ?? false) { ... } + ``` + +4. **Inconsistent Env Naming**: Confusion across environments + ```bash + # ❌ BAD: Inconsistent + DB_URL, DATABASE_CONNECTION, postgres_url + + # ✅ GOOD: Consistent pattern + DATABASE_URL, REDIS_URL, KAFKA_URL + ``` + +## Quick Reference + +| Config Type | Source | Example | +|-------------|--------|---------| +| **Static** | Environment vars | `DATABASE_URL` | +| **Dynamic** | Config service | `rate_limit_threshold` | +| **Secrets** | K8s Secrets/Vault | `JWT_SECRET` | +| **Feature Flags** | Database | `new_checkout_flow` | + +**Environment Variable Naming:** +```bash +# Pattern: SERVICE_CATEGORY_NAME +DATABASE_URL # Connection strings +REDIS_HOST # Service hosts +JWT_SECRET # Secrets (use K8s secrets) +LOG_LEVEL # Configuration +FEATURE_NEW_UI # Feature flags +``` + +**Zod Validation Pattern:** +```typescript +const ConfigSchema = z.object({ + port: z.coerce.number().default(3000), + nodeEnv: z.enum(['development', 'staging', 'production']), + databaseUrl: z.string().url(), + logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'), +}); + +export const config = ConfigSchema.parse(process.env); +``` + +**Feature Flag Check Pattern:** +```typescript +// Simple check +const enabled = await featureFlags.isEnabled('feature-key'); + +// With user context (for gradual rollout) +const enabled = await featureFlags.isEnabled('feature-key', userId); +``` + ## Resources - [Feature Flags Pattern](https://martinfowler.com/articles/feature-toggles.html) diff --git a/.cursor/skills/data-consistency-patterns/SKILL.md b/.cursor/skills/data-consistency-patterns/SKILL.md index bbb9e44b..fc40f7fc 100644 --- a/.cursor/skills/data-consistency-patterns/SKILL.md +++ b/.cursor/skills/data-consistency-patterns/SKILL.md @@ -692,6 +692,85 @@ describe('OrderProcessingSaga', () => { }); ``` +## Common Mistakes + +1. **No Compensation Logic**: Saga steps without rollback + ```typescript + // ❌ BAD: No compensation + steps: [{ execute: () => createOrder() }] + + // ✅ GOOD: Always define compensation + steps: [{ + execute: () => createOrder(), + compensate: (ctx) => cancelOrder(ctx.orderId) + }] + ``` + +2. **Missing Idempotency**: Duplicate processing on retry + ```typescript + // ❌ BAD: Creates duplicate + await createPayment(orderId); + + // ✅ GOOD: Idempotent check + const existing = await findPayment(idempotencyKey); + if (existing) return existing; + await createPayment(orderId); + ``` + +3. **Ignoring Partial Failures**: Not handling step failures + ```typescript + // ❌ BAD: No error handling + await step1(); await step2(); await step3(); + + // ✅ GOOD: Saga orchestration + await sagaOrchestrator.execute(context); + ``` + +4. **No Version Field**: Concurrent update conflicts + ```prisma + // ✅ 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 | + +**Saga Steps:** +``` +Execute: Step1 → Step2 → Step3 → Complete +Compensate: ← Step2.undo ← Step1.undo (on failure) +``` + +**Idempotency Key Pattern:** +```typescript +const key = `${operation}:${userId}:${hash(requestData)}`; +await idempotencyHandler.execute(key, () => operation()); +``` + +**Optimistic Lock Query:** +```typescript +await prisma.entity.update({ + where: { id, version: currentVersion }, + data: { ...updates, version: { increment: 1 } } +}); +``` + +**Consistency Models:** +| Model | Latency | Use Case | +|-------|---------|----------| +| **Strong** | High | Financial transactions | +| **Eventual** | Low | Read models, analytics | +| **Causal** | Medium | User sessions | + ## Resources - [Saga Pattern](https://microservices.io/patterns/data/saga.html) - Saga pattern overview diff --git a/.cursor/skills/database-prisma/SKILL.md b/.cursor/skills/database-prisma/SKILL.md index c2224f12..89e57033 100644 --- a/.cursor/skills/database-prisma/SKILL.md +++ b/.cursor/skills/database-prisma/SKILL.md @@ -476,4 +476,107 @@ describe('UserRepository', () => { - Keep migrations small and focused - Test migrations before production - Backup before major changes - - Monitor query performance \ No newline at end of file + - Monitor query performance + +## Common Mistakes + +1. **N+1 Query Problem**: Fetching related data in a loop + ```typescript + // ❌ BAD: N+1 queries + const users = await prisma.user.findMany(); + for (const user of users) { + const posts = await prisma.post.findMany({ where: { authorId: user.id } }); + } + + // ✅ GOOD: Include relations + const users = await prisma.user.findMany({ + include: { posts: true } + }); + ``` + +2. **No Indexes**: Missing indexes on frequently queried columns + ```prisma + // ❌ BAD: No index + model User { + email String @unique + createdAt DateTime @default(now()) + } + + // ✅ GOOD: Add index for queries + model User { + email String @unique + createdAt DateTime @default(now()) + @@index([createdAt]) + } + ``` + +3. **Raw Queries Without Parameters**: SQL injection risk + ```typescript + // ❌ BAD: SQL injection risk + prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`); + + // ✅ GOOD: Parameterized query + prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`; + ``` + +4. **Not Using Transactions**: Data inconsistency risk + ```typescript + // ❌ BAD: No transaction + await prisma.account.update({ where: { id: from }, data: { balance: { decrement: amount } } }); + await prisma.account.update({ where: { id: to }, data: { balance: { increment: amount } } }); + + // ✅ GOOD: Use transaction + await prisma.$transaction([ + prisma.account.update({ where: { id: from }, data: { balance: { decrement: amount } } }), + prisma.account.update({ where: { id: to }, data: { balance: { increment: amount } } }) + ]); + ``` + +5. **Exposing Internal IDs**: Leaking database structure + ```typescript + // ❌ BAD: Exposing auto-increment ID + model User { id Int @id @default(autoincrement()) } + + // ✅ GOOD: Use CUID or UUID + model User { id String @id @default(cuid()) } + ``` + +## Quick Reference + +| Operation | Prisma Command | +|-----------|----------------| +| **Create migration** | `npx prisma migrate dev --name ` | +| **Apply migrations** | `npx prisma migrate deploy` | +| **Reset database** | `npx prisma migrate reset` | +| **Generate client** | `npx prisma generate` | +| **Open Prisma Studio** | `npx prisma studio` | +| **Seed database** | `npx prisma db seed` | + +**Common Query Patterns:** +```typescript +// Find unique +await prisma.user.findUnique({ where: { id } }); + +// Find with relations +await prisma.user.findMany({ include: { posts: true } }); + +// Select specific fields +await prisma.user.findMany({ select: { id: true, email: true } }); + +// Pagination +await prisma.user.findMany({ skip: 10, take: 10 }); + +// Transaction +await prisma.$transaction(async (tx) => { /* operations */ }); +``` + +**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 | \ No newline at end of file diff --git a/.cursor/skills/deployment-kubernetes/SKILL.md b/.cursor/skills/deployment-kubernetes/SKILL.md index 882ee5a1..3643aa38 100644 --- a/.cursor/skills/deployment-kubernetes/SKILL.md +++ b/.cursor/skills/deployment-kubernetes/SKILL.md @@ -484,4 +484,88 @@ kubectl describe pod pod-name -n goodgo | grep -A 5 Limits 6. **Monitoring** - Expose metrics endpoint - Set up alerts for critical issues - - Monitor resource usage and performance \ No newline at end of file + - Monitor resource usage and performance + +## Common Mistakes + +1. **No Resource Limits**: Pods consuming all node resources + ```yaml + # ❌ BAD: No limits + containers: + - name: app + image: app:latest + + # ✅ GOOD: Set limits + containers: + - name: app + image: app:latest + resources: + requests: { memory: "256Mi", cpu: "250m" } + limits: { memory: "512Mi", cpu: "500m" } + ``` + +2. **Missing Health Checks**: K8s can't detect unhealthy pods + ```yaml + # ✅ Always add liveness and readiness probes + livenessProbe: + httpGet: { path: /health, port: 3000 } + initialDelaySeconds: 30 + readinessProbe: + httpGet: { path: /ready, port: 3000 } + initialDelaySeconds: 5 + ``` + +3. **Hardcoded Secrets**: Exposing sensitive data + ```yaml + # ❌ BAD: Hardcoded + env: + - name: DB_PASSWORD + value: "secret123" + + # ✅ GOOD: Use secrets + env: + - name: DB_PASSWORD + valueFrom: + secretKeyRef: { name: db-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 +``` \ No newline at end of file diff --git a/.cursor/skills/error-handling-patterns/SKILL.md b/.cursor/skills/error-handling-patterns/SKILL.md index 87a78d99..ba53143a 100644 --- a/.cursor/skills/error-handling-patterns/SKILL.md +++ b/.cursor/skills/error-handling-patterns/SKILL.md @@ -312,6 +312,50 @@ Standardized error response format: **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 diff --git a/.cursor/skills/event-driven-architecture/SKILL.md b/.cursor/skills/event-driven-architecture/SKILL.md index 986c68f0..128de897 100644 --- a/.cursor/skills/event-driven-architecture/SKILL.md +++ b/.cursor/skills/event-driven-architecture/SKILL.md @@ -1581,6 +1581,101 @@ eventSource.addEventListener('user.created', (event) => { }); ``` +## 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 + // ✅ Always implement DLQ for failed events + after maxRetries → send to topic.dlq + ``` + +## 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' }); +``` + ## Resources - [KafkaJS Documentation](https://kafka.js.org/) - Node.js Kafka client diff --git a/.cursor/skills/inter-service-communication/SKILL.md b/.cursor/skills/inter-service-communication/SKILL.md index d9dd12cb..ccaad734 100644 --- a/.cursor/skills/inter-service-communication/SKILL.md +++ b/.cursor/skills/inter-service-communication/SKILL.md @@ -1079,6 +1079,86 @@ describe('Service Communication E2E', () => { }); ``` +## Common Mistakes + +1. **No Service Authentication**: Unsecured internal calls + ```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**: Hanging requests + ```typescript + // ❌ BAD: No timeout + await axios.get(url); + + // ✅ GOOD: Set timeout + await axios.get(url, { timeout: 5000 }); + ``` + +3. **No Circuit Breaker**: Cascade failures + ```typescript + // ✅ Use circuit breaker for external calls + await circuitBreaker.fire(() => serviceClient.get('/api/users')); + ``` + +4. **Hardcoded Service URLs**: Breaks in different environments + ```typescript + // ❌ BAD + const url = 'http://user-service:5000'; + + // ✅ GOOD + const url = process.env.USER_SERVICE_URL; + ``` + +## Quick Reference + +| Protocol | Use Case | Latency | Complexity | +|----------|----------|---------|------------| +| **REST** | External APIs, CRUD | Medium | Low | +| **gRPC** | Internal high-perf | Low | High | +| **GraphQL** | Flexible queries | Medium | Medium | + +**Protocol Selection:** +``` +External/Public API → REST +Internal service-to-service → gRPC +Complex data fetching → GraphQL +Real-time streaming → gRPC/WebSocket +``` + +**HTTP Client Setup:** +```typescript +import axios from 'axios'; + +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' +}); +``` + +**Essential Headers:** +| Header | Purpose | +|--------|---------| +| `x-service-auth` | Internal authentication | +| `x-correlation-id` | Request tracing | +| `x-request-id` | Request identification | + ## Resources - [gRPC Documentation](https://grpc.io/docs/) - Official gRPC documentation diff --git a/.cursor/skills/middleware-patterns/SKILL.md b/.cursor/skills/middleware-patterns/SKILL.md index 125561c7..3ff2dd66 100644 --- a/.cursor/skills/middleware-patterns/SKILL.md +++ b/.cursor/skills/middleware-patterns/SKILL.md @@ -264,6 +264,47 @@ export const requestLogger = (req: Request, res: Response, next: NextFunction) = **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` | 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 - [Correlation Middleware](../../services/iam-service/src/middlewares/correlation.middleware.ts) diff --git a/.cursor/skills/observability-monitoring/SKILL.md b/.cursor/skills/observability-monitoring/SKILL.md index b5b6b17c..660591d0 100644 --- a/.cursor/skills/observability-monitoring/SKILL.md +++ b/.cursor/skills/observability-monitoring/SKILL.md @@ -489,4 +489,97 @@ groups: - Alert on symptoms, not causes - Include runbook links in alerts - Avoid alert fatigue with proper thresholds - - Test alerting rules regularly \ No newline at end of file + - Test alerting rules regularly + +## Common Mistakes + +1. **Logging Sensitive Data**: Exposing PII in logs + ```typescript + // ❌ BAD: Logging sensitive data + logger.info('User login', { email, password, token }); + + // ✅ GOOD: Sanitize sensitive fields + logger.info('User login', { email, userId }); + ``` + +2. **High Cardinality Labels**: Too many metric label values + ```typescript + // ❌ BAD: userId as label (millions of values) + httpRequests.labels(method, route, userId).inc(); + + // ✅ GOOD: Low cardinality labels only + httpRequests.labels(method, route, statusCode).inc(); + ``` + +3. **No Correlation IDs**: Can't trace requests across services + ```typescript + // ❌ BAD: No correlation + logger.info('Processing request'); + + // ✅ GOOD: Include correlation ID + logger.info('Processing request', { correlationId: req.headers['x-correlation-id'] }); + ``` + +4. **Wrong Log Levels**: Using ERROR for non-errors + ```typescript + // ❌ BAD: Wrong level + logger.error('User not found'); // Not an error, expected case + + // ✅ GOOD: Appropriate level + logger.info('User not found', { userId }); + logger.error('Database connection failed', { error }); + ``` + +5. **No Health Checks**: Service status unknown + ```typescript + // ❌ BAD: No health endpoint + + // ✅ GOOD: Comprehensive health checks + 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 +// Request rate +rate(http_requests_total[5m]) + +// Error rate +rate(http_requests_total{status=~"5.."}[5m]) + +// Latency (p95) +histogram_quantile(0.95, http_request_duration_seconds_bucket) + +// Active connections +active_connections +``` + +**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 | + +**Essential Imports:** +```typescript +import { logger } from '@goodgo/logger'; +import { Counter, Histogram, Gauge } from 'prom-client'; +import { trace, SpanStatusCode } from '@opentelemetry/api'; +``` \ No newline at end of file diff --git a/.cursor/skills/performance-optimization/SKILL.md b/.cursor/skills/performance-optimization/SKILL.md index 1b309b6a..ee3d66c6 100644 --- a/.cursor/skills/performance-optimization/SKILL.md +++ b/.cursor/skills/performance-optimization/SKILL.md @@ -360,6 +360,96 @@ export class CacheOptimizer { 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/) diff --git a/.cursor/skills/repository-pattern/SKILL.md b/.cursor/skills/repository-pattern/SKILL.md index 582abbc7..0ec5cc4b 100644 --- a/.cursor/skills/repository-pattern/SKILL.md +++ b/.cursor/skills/repository-pattern/SKILL.md @@ -213,6 +213,60 @@ const usersWithRoles = await userRepository.findAll({ **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 { + constructor(prisma: PrismaClient) { + super(prisma, 'entity'); + } + + // Custom query methods + async findByField(value: string): Promise { + 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 diff --git a/.cursor/skills/service-layer-patterns/SKILL.md b/.cursor/skills/service-layer-patterns/SKILL.md index cea832d7..89e1fc68 100644 --- a/.cursor/skills/service-layer-patterns/SKILL.md +++ b/.cursor/skills/service-layer-patterns/SKILL.md @@ -195,6 +195,53 @@ export class FeatureModule { 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 { + const entity = await this.repository.findById(id); + if (!entity) throw new NotFoundError('Entity'); + return entity; + } + + async create(data: CreateDto): Promise { + // 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