Add Quick Reference and Common Mistakes sections to 14 skills
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 <noreply@anthropic.com>
This commit is contained in:
@@ -275,6 +275,51 @@ async getWithCache(key: string): Promise<Data | null> {
|
||||
- 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
|
||||
|
||||
@@ -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/)
|
||||
|
||||
@@ -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=<your-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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -476,4 +476,107 @@ describe('UserRepository', () => {
|
||||
- Keep migrations small and focused
|
||||
- Test migrations before production
|
||||
- Backup before major changes
|
||||
- Monitor query performance
|
||||
- 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 <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 |
|
||||
@@ -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
|
||||
- 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
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<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
|
||||
|
||||
- [Correlation Middleware](../../services/iam-service/src/middlewares/correlation.middleware.ts)
|
||||
|
||||
@@ -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
|
||||
- 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';
|
||||
```
|
||||
@@ -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/)
|
||||
|
||||
@@ -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<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
|
||||
|
||||
@@ -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<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
|
||||
|
||||
Reference in New Issue
Block a user