Microservices Architecture
Purpose
Guide microservices decomposition, communication patterns, data ownership, and migration strategies.
Agent Protocol
Trigger
User request includes: microservice, micro-services, service decomposition, distributed system, saga, cqrs, event sourcing, service mesh.
Input Context
- Business domain description / bounded context map
- Current monolith architecture (if migrating)
- Team topology (Conway's Law)
- Non-functional requirements (latency, throughput, consistency, availability)
- Technology preferences (message broker, container platform, language)
Output Artifact
A markdown document containing:
- Service decomposition model (bounded contexts with responsibilities)
- Communication pattern selection (sync/async/event) per service pair
- Data ownership strategy (database per service, shared nothing)
- Infrastructure recommendations (service mesh, API gateway, message broker)
- Migration strategy (strangler fig, parallel run)
Response Format
Produce the artifact directly. No preamble, no postamble, no explanations. No filler, no hedging, no transitions. Strip articles a/an/the where unambiguous. Compress output — why use many token when few do trick. If monolith is appropriate, output Monolith recommended. Reason: [reason]. and stop.
Completion Criteria
- Decomposition bounded contexts explicitly mapped to business capabilities
- Each service pair has communication pattern documented with rationale
- Data consistency strategy for each transaction spanning services
- Infrastructure recommendations with concrete tools/versions
- Migration path with ordered phases
Max Response Length
4096 tokens
Decision Tree
Should You Use Microservices?
What is your situation?
├── Small team (<10 devs), early stage product, uncertain domain
│ └── Monolith recommended. Reason: microservices add accidental complexity before you understand the domain.
├── Medium team, well-understood domain, scaling issues in monolith
│ └── Decompose selectively: extract hot paths first (bounded contexts)
├── Large organization, multiple teams, clear domain boundaries
│ └── Microservices aligned to team topology (Conway's Law)
└── Migrating from monolith with growing complexity
└── Strangler Fig: extract one bounded context at a time
How to Decompose?
What boundary defines this service?
├── Business capability (orders, payments, shipping)
│ └── Standard approach: map to business functions
├── DDD subdomain (core, supporting, generic)
│ └── Follow bounded contexts from domain modeling
├── Team topology (one team = one or more services)
│ └── Conway's Law: services mirror communication structure
└── Data ownership (candidate for extraction)
└── If a data domain changes independently, it's a decomposition candidate
Communication Pattern Selection
What does the caller need?
├── Immediate response, strong consistency, low latency needed
│ └── Synchronous (HTTP/gRPC) — but beware cascading failures
├── Fire-and-forget, eventual consistency OK, decouple in time
│ └── Async (message queue / event) — resilient, scalable
├── Distributed transaction spanning 3+ services
│ └── Saga orchestration — central coordinator
├── Guaranteed event publication DB → broker
│ └── Transactional outbox — write event in same DB transaction
└── Need to rebuild state from events or audit trail
└── Event sourcing — store events as source of truth
Workflow
Step 1: Decompose by Bounded Context
| Pattern |
Approach |
When |
| Business Capability |
Map to business functions (orders, payments, shipping) |
Clear organizational boundaries |
| Subdomain |
Follow DDD bounded contexts |
Complex domain, multiple subdomains |
| Strangler Fig |
Incrementally replace monolith features |
Brownfield migration |
| Self-contained Service |
Service owns its data, API, and logic |
Least coupling, maximum autonomy |
Decomposition rules:
- Service must be independently deployable
- Service must own its data exclusively (no shared databases)
- Service must be team-sizable (2-pizza team)
- Service communication must be via network calls (no in-process)
Step 2: Select Communication Patterns
| Pattern |
Type |
Consistency |
Latency |
Use Case |
| Synchronous (HTTP/gRPC) |
Request-response |
Strong (if transactional) |
Higher |
Queries, commands needing immediate response |
| Asynchronous (Message Queue) |
Event-driven |
Eventual |
Lower |
Cross-service notifications, long-running processes |
| Saga |
Choreography / Orchestration |
Eventual |
Medium |
Distributed transaction spanning services |
| Transactional Outbox |
Reliable event publishing |
Strong |
Low + async |
Guaranteed event delivery with DB transaction |
Saga variants:
- Choreography: Each service publishes events after local transaction. Lower complexity, harder to trace.
- Orchestration: Central coordinator tells each service what to do. Higher complexity, easier to trace, single point of failure.
- Selection rule: 3+ services in saga? Use orchestration. <3? Choreography is acceptable.
Step 3: Define Data Ownership
| Pattern |
Description |
Trade-off |
| Database per Service |
Each service owns its database |
No shared schema, data duplication, eventual consistency |
| CQRS |
Separate read and write models |
Optimized queries, eventual consistency, higher complexity |
| Event Sourcing |
Store events, derive state |
Complete audit trail, complex querying, storage growth |
| Saga (data) |
Compensating transactions |
No distributed lock, eventual consistency, compensating logic needed |
Data ownership rules:
- No service ever accesses another service's database directly
- No shared database between services (exception: read-only reference data)
- Data duplication is acceptable and expected (each service owns its view)
Step 4: Configure Service Discovery
| Pattern |
Description |
| Client-side Discovery |
Client queries registry, load-balances directly |
| Server-side Discovery |
Load balancer queries registry, routes request |
| Service Registry |
DNS-based (Consul, Eureka, Kubernetes DNS) |
Recommendation: Kubernetes-native (DNS for discovery, Service for load balancing). Only use external registry if running outside K8s.
Step 5: Implement Observability
| Pillar |
Tool (Language-agnostic) |
What to Capture |
| Logging |
Structured (JSON), centralized |
Service ID, trace ID, span ID, severity, message |
| Metrics |
Prometheus + Grafana |
RED (Rate, Errors, Duration) for every endpoint |
| Tracing |
OpenTelemetry |
Request path across services, span timings |
| Health Checks |
Readiness + Liveness probes |
Can serve traffic? Is process alive? |
Step 6: Choose Deployment Strategy
| Pattern |
Strategy |
Risk |
| Blue/Green |
Two full environments, switch traffic |
Low, double resources |
| Canary |
Gradual traffic shift |
Medium, requires monitoring |
| Rolling |
Incremental instance replacement |
Low, slow |
| Feature Flags |
Toggle features independently |
Low, requires flag infrastructure |
Step 7: Apply Resilience Patterns
| Pattern |
Description |
| Circuit Breaker |
Stop calling failing service, fail fast |
| Bulkhead |
Isolate resources per service client |
| Retry with Backoff |
Exponential backoff + jitter |
| Timeout |
Hard timeout per external call |
| Fallback |
Degraded response when service unavailable |
| Rate Limiter |
Protect services from overload |
Step 8: Implement Security
| Pattern |
Description |
| API Gateway Auth |
Centralized authentication at gateway |
| JWT / OAuth2 |
Token-based service-to-service auth |
| mTLS |
Mutual TLS for service mesh |
| Service Mesh |
Istio / Linkerd for transparent mTLS, policy |
Step 9: Plan Migration from Monolith
- Identify seams — areas of code that change independently
- Extract read model first — read-only microservice serving cached/pre-computed data
- Extract write model — feature flag to route writes to new service, dual-write during transition
- Strangler — incrementally replace monolith endpoints with service endpoints
- Monolith retirement — when all features migrated, decommission monolith
Step 10: API Versioning and Contracts
// Contract-first approach: define OpenAPI / protobuf before implementation
// Use consumer-driven contracts (CDC) to detect breaking changes
// API versioning strategies:
// 1. URL path: /v1/orders, /v2/orders
// 2. Header: Accept: application/vnd.myapp.v1+json
// 3. Query param: /orders?version=1
// Recommendation: URL path for major versions, additive field expansion for minor
Step 11: Cross-Cutting Concerns
| Concern |
Implementation |
| Configuration |
Centralized (Consul, etcd, K8s ConfigMap + secrets). Never env-specific in code |
| Shared libraries |
Minimize. Prefer duplication over coupling via shared libs |
| Error handling |
Standard error envelope across all services |
| Health checks |
Readiness (can serve?) + Liveness (is alive?) exposed on management port |
| Graceful shutdown |
Drain connections, complete in-flight, then exit |
| Rate limiting |
Per service + global via API gateway |
Step 12: API Gateway Integration
// API Gateway (Kong / APISIX / Envoy) configuration pattern
// Route: /orders/* -> order-service:3001
// Route: /payments/* -> payment-service:3002
// Global: rate limit 1000 req/min per tenant, auth via JWT
// Gateway-level concerns:
// 1. Authentication — validate JWT before routing to service
// 2. Rate limiting — per-tenant, per-endpoint, burst allowance
// 3. Request validation — schema-based body validation at edge
// 4. Response transformation — strip internal headers, add CORS
// 5. Circuit breaking — gateway can fail fast before calling service
Step 13: Service Mesh Integration
# Istio VirtualService for traffic splitting (canary)
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: order-service
spec:
hosts:
- order-service
http:
- match:
- headers:
x-canary: { exact: "true" }
route:
- destination:
host: order-service
subset: v2
weight: 100
- route:
- destination:
host: order-service
subset: v1
weight: 90
- destination:
host: order-service
subset: v2
weight: 10
# Service mesh provides: mTLS, traffic shifting, fault injection,
# circuit breaking, observability — without changing application code
Step 14: Database per Service Implementation
// Order Service — owns its PostgreSQL database
// Schema: orders, order_items, order_events
// No other service has direct DB access
// Payment Service — owns its PostgreSQL database
// Schema: payments, refunds, payment_methods
// Cross-service data access: via API calls only
// Example: Order Service needs payment status
// Solution: call `GET /payments/v1/orders/:orderId` — never query payment DB
Step 15: Contract Testing with Pact
// Consumer-driven contract test (Order Service -> Payment Service)
describe('Order Service - Payment API contract', () => {
const provider = new Pact({
consumer: 'OrderService',
provider: 'PaymentService',
port: 4000,
});
beforeAll(() => provider.setup());
afterEach(() => provider.verify());
afterAll(() => provider.finalize());
it('should process payment and return transaction ID', async () => {
await provider.addInteraction({
state: 'payment can be processed',
uponReceiving: 'a request to process payment',
withRequest: {
method: 'POST',
path: '/v1/payments',
headers: { 'Content-Type': 'application/json' },
body: { orderId: '123', amount: 99.99, currency: 'USD' },
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: { transactionId: like('txn_abc123'), status: 'completed' },
},
});
const result = await paymentClient.processPayment({ orderId: '123', amount: 99.99, currency: 'USD' });
expect(result.transactionId).toBeDefined();
});
});
API Composition Pattern
// API Gateway / BFF that aggregates multiple downstream services
// Instead of client making 4 requests, gateway does it server-side
interface ProductDetailsResponse {
product: Product;
inventory: Inventory;
reviews: Review[];
recommendations: Product[];
}
async function getProductDetails(productId: string): Promise<ProductDetailsResponse> {
const [product, inventory, reviews, recommendations] = await Promise.all([
fetchProductService(productId),
fetchInventoryService(productId),
fetchReviewService(productId),
fetchRecommendationService(productId),
]);
return { product, inventory, reviews, recommendations };
}
// Circuit breaker for resilient inter-service calls
class CircuitBreaker {
private failures = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private readonly threshold = 5;
private readonly timeout = 30000; // 30s
async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime > this.timeout) {
this.state = 'HALF_OPEN';
} else {
throw new Error('Circuit breaker is OPEN');
}
}
try {
const result = await fn();
this.failures = 0;
this.state = 'CLOSED';
return result;
} catch (err) {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.threshold) {
this.state = 'OPEN';
}
throw err;
}
}
}
Service Discovery
# Kubernetes Headless Service for DNS-based discovery
# Services discover peers via SRV DNS lookups
apiVersion: v1
kind: Service
metadata:
name: payment-service
spec:
clusterIP: None # Headless — returns pod IPs
selector:
app: payment-service
ports:
- name: grpc
port: 50051
- name: health
port: 8080
// DNS-based service discovery with retry
import * as dns from 'dns/promises';
async function resolveService(name: string): Promise<string> {
const records = await dns.resolveSrv(`_grpc._tcp.${name}.svc.cluster.local`);
// Round-robin across healthy instances
const target = records[Math.floor(Math.random() * records.length)];
return `${target.name}:${target.port}`;
}
Production Considerations
| Concern |
Practice |
| Service boundaries wrong |
Expect to merge/split services as understanding grows. Plan for refactoring |
| Network latency |
Inter-service calls add 1-10ms. Batch queries where possible |
| Data consistency |
Eventual consistency is default. Accept it or use saga with compensating actions |
| Team autonomy vs consistency |
Balance: shared infrastructure (monitoring, CI) without coupling service decisions |
| Testing |
Unit (service-local) + Integration (per service) + Contract (per API pair) + E2E (minimal) |
| Observability |
Must be in place before going live. Debugging without it is guesswork |
| Cold start latency |
Serverless services (Lambda) add 200ms-5s cold start — keep latency-critical services on persistent compute |
| Inter-service auth overhead |
mTLS handshake adds ~10-50ms per connection — use connection pooling and keepalive |
| Schema changes |
Independent DB migrations per service — coordination needed for cross-service schema changes |
| Backup and restore |
Each service has independent backup strategy — test restore procedure quarterly |
Security
| Layer |
Threat |
Mitigation |
| API Gateway |
DDoS, brute force |
Rate limiting, WAF, IP allowlisting, request size limits |
| Inter-service |
Man-in-the-middle |
mTLS with short-lived certs (Istio/Linkerd auto-rotation) |
| Data at rest |
Data breach |
Encrypt DB volumes (AES-256), encrypt S3 buckets (SSE-KMS) |
| Secrets |
Credential leak |
Vault/AWS Secrets Manager, never in env files or config repos |
| Auth tokens |
Token theft |
Short-lived JWT (15 min), refresh tokens with rotation, httpOnly cookies |
| Supply chain |
Compromised dependency |
Dependency scanning (Snyk/Dependabot), signed artifacts, SBOM generation |
| API contracts |
Breaking changes |
Consumer-driven contracts, CI-validated, versioned APIs |
// Service-to-service authentication with mTLS
import { credentials, Metadata } from '@grpc/grpc-js';
import { readFileSync } from 'fs';
const rootCert = readFileSync('/etc/ssl/certs/ca-cert.pem');
const clientCert = readFileSync('/etc/ssl/certs/service-cert.pem');
const clientKey = readFileSync('/etc/ssl/certs/service-key.pem');
const channelCredentials = credentials.createSsl(rootCert, clientKey, clientCert);
const client = new PaymentServiceClient('payment-service:443', channelCredentials);
Anti-Patterns
| Anti-Pattern |
Problem |
Fix |
| Distributed Monolith |
Services tightly coupled, deployed together |
Enforce strict API contracts, independent deploy |
| Shared Database |
Multiple services same DB schema |
Extract shared data into dedicated service |
| Too Fine-grained |
Excessive network calls, latency |
Merge related services |
| God Service |
One service does everything |
Decompose by capability |
| No Monitoring |
Cannot debug production issues |
Add OpenTelemetry before going live |
| Synchronous Chains |
A calls B calls C — high latency, fragile |
Async where possible, parallel calls |
| Leaky Abstractions |
Service exposes internal DB schema in API |
API is contract — hide implementation |
| Golden Hammer |
All problems solved with microservices |
Consider monolith first, extract when needed |
| Chatty Services |
Many small calls instead of one batched call |
Use GraphQL or API composition layer |
| Shared Model Classes |
Same DTO class in multiple services |
Each service has its own view of data (duplication is OK) |
| No Schema Governance |
Inconsistent API designs across services |
API style guide + linting in CI (Spectral for OpenAPI) |
| Synchronous Event Publishing |
Services send events inline in request path |
Use transactional outbox pattern |
Performance Benchmarks
| Pattern |
Latency (p50) |
Latency (p99) |
Throughput |
| In-process method call |
< 1µs |
< 1µs |
Unlimited |
| HTTP inter-service (same AZ) |
2-5ms |
10-20ms |
~10K req/s per instance |
| gRPC inter-service (same AZ) |
0.5-2ms |
5-10ms |
~50K req/s per instance |
| Message queue (async) |
5-50ms |
100-500ms |
~100K msg/s per broker |
| Saga (orchestration, 3 steps) |
15-50ms |
100-200ms |
~5K saga/s |
| Service mesh sidecar overhead |
+1-3ms |
+5-10ms |
~5% throughput reduction |
Rules
- No shared databases between services — ever.
- Each service independently deployable with its own CI/CD.
- 3+ services in a saga? Use orchestration.
- Kubernetes-native discovery preferred over external registries.
- OpenTelemetry for all observability pillars.
- No service calls another service's database directly.
- Strangler Fig for monolith migration — no big-bang rewrites.
- Communication patterns documented per service pair with rationale.
- Always define API contracts before implementation (contract-first).
- Each service has at most one DB (shared-nothing).
- Prefer eventual consistency unless strong consistency is legally required.
- gRPC for high-throughput internal calls (< 2ms latency). HTTP for external or low-throughput internal.
- Rate limit at the gateway AND per-service for defense in depth.
- Use consumer-driven contracts to detect breaking API changes in CI.
- Every service must have independent backup and restore tested quarterly.
References
- references/communication-patterns.md — Communication Patterns
- references/data-patterns.md — Data Patterns
- references/decomposition-patterns.md — Decomposition Patterns
- references/microservices-communication.md — Microservices Communication
- references/microservices-observability.md — Microservices Observability
- references/microservices-testing.md — Microservices Testing
Handoff
Hand off to devops/containerization/SKILL.md for container orchestration setup. Hand off to backend/universal/event-driven/SKILL.md for detailed event-driven patterns. Hand off to backend/universal/database-patterns/SKILL.md for data consistency strategies.
Implementation Patterns
Observer Pattern for Event Handling
`
interface EventObserver {
onEvent(event: T): Promise;
}
class EventBus {
private observers: Set<EventObserver> = new Set();
subscribe(observer: EventObserver): void {
this.observers.add(observer);
}
unsubscribe(observer: EventObserver): void {
this.observers.delete(observer);
}
async emit(event: T): Promise {
const results = Array.from(this.observers).map(o => o.onEvent(event));
await Promise.allSettled(results);
}
}
`
Configuration-Driven Approach
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
Production Considerations
Deployment Checklist
Monitoring and Alerting
| Metric |
Threshold |
Severity |
Action |
| Error rate |
> 1% over 5min |
Critical |
Page on-call |
| p99 latency |
> 2s over 5min |
Warning |
Investigate |
| Throughput drop |
> 50% over 1min |
Critical |
Check upstream |
| Queue depth |
> 1000 over 1min |
Warning |
Scale consumers |
| Disk usage |
> 85% |
Warning |
Clean or expand |
| Memory usage |
> 90% heap |
Critical |
Restart or scale |
Anti-Patterns
| Anti-Pattern |
Symptom |
Root Cause |
Solution |
| Premature optimization |
Complex code for no measured benefit |
Guessing instead of profiling |
Measure first, optimize based on data |
| Copy-paste reuse |
Duplicate code across codebase |
Lack of abstraction |
Extract shared logic into libraries |
| Gold-plating |
Features with no current requirement |
Over-engineering |
YAGNI — build what's needed now |
| Magical thinking |
Assumptions without validation |
Skipping error handling |
Handle all failure modes explicitly |
Performance Optimization
Caching Strategy
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge).
Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).
Resource Pooling
- Database connections: Pool of reusable connections (HikariCP, pgBouncer)
- HTTP connections: Keep-alive + connection pooling for external calls
- Thread pool: Bounded thread pools for async task execution
Profiling Methodology
- Establish baseline with production traffic profile
- Profile CPU with sampling profiler (pprof, perf, async-profiler)
- Profile memory with heap dumps and allocation tracking
- Profile I/O with strace/perf trace for syscall analysis
- Profile latency with distributed tracing (OpenTelemetry)
- Identify bottleneck, formulate hypothesis, implement fix
- Re-profile to verify improvement, repeat
Security Considerations
Threat Modeling (STRIDE)
- Spoofing: Identity validation, authentication
- Tampering: Integrity checks, digital signatures
- Repudiation: Audit logs, non-repudiation
- Information disclosure: Encryption, access control
- Denial of service: Rate limiting, resource quotas
- Elevation of privilege: Principle of least privilege
Supply Chain Security
- Dependency scanning: Snyk, Dependabot, Trivy
- SBOM generation: CycloneDX or SPDX format
- Signed commits: GPG or SSH commit signing
- Artifact verification: Checksum validation, signature verification
Secrets Management
- Secrets never in code — always in secrets manager (Vault, AWS Secrets Manager)
- Rotation policy: Rotate database credentials every 90 days
- Access audit: Log every secrets access, alert on anomalies
- Encryption at rest and in transit for all secrets
- Principle of least privilege: each service gets only its own secrets
Rules
- Default-deny security posture — allow only explicitly required access.
- All inputs validated, all outputs encoded, all errors handled.
- Defend in depth — multiple layers of security controls.
- Fail securely — errors default to safe behavior.
- Log security-relevant events for audit and investigation.
- Keep dependencies updated — automate vulnerability scanning.
- Design for observability from day one, not as an afterthought.
- Document all architectural decisions with rationale.
- Review code for security, performance, and correctness before merging.
1---2name: microservices3description: Use this skill when designing microservices architecture — decomposition, communication, data, discovery, observability, deployment. This skill enforces: bounded context decomposition, database-per-service ownership, saga patterns for distributed transactions, strangler fig migration. Do NOT use for: monolith application design, frontend architecture, single-service API design.4license: MIT5---67# Microservices Architecture89## Purpose10Guide microservices decomposition, communication patterns, data ownership, and migration strategies.1112## Agent Protocol1314### Trigger15User request includes: `microservice`, `micro-services`, `service decomposition`, `distributed system`, `saga`, `cqrs`, `event sourcing`, `service mesh`.1617### Input Context18- Business domain description / bounded context map19- Current monolith architecture (if migrating)20- Team topology (Conway's Law)21- Non-functional requirements (latency, throughput, consistency, availability)22- Technology preferences (message broker, container platform, language)2324### Output Artifact25A markdown document containing:26- Service decomposition model (bounded contexts with responsibilities)27- Communication pattern selection (sync/async/event) per service pair28- Data ownership strategy (database per service, shared nothing)29- Infrastructure recommendations (service mesh, API gateway, message broker)30- Migration strategy (strangler fig, parallel run)3132### Response Format33Produce the artifact directly. No preamble, no postamble, no explanations. No filler, no hedging, no transitions. Strip articles a/an/the where unambiguous. Compress output — why use many token when few do trick. If monolith is appropriate, output `Monolith recommended. Reason: [reason].` and stop.3435### Completion Criteria36- Decomposition bounded contexts explicitly mapped to business capabilities37- Each service pair has communication pattern documented with rationale38- Data consistency strategy for each transaction spanning services39- Infrastructure recommendations with concrete tools/versions40- Migration path with ordered phases4142### Max Response Length434096 tokens4445## Decision Tree4647### Should You Use Microservices?4849```50What is your situation?51 ├── Small team (<10 devs), early stage product, uncertain domain52 │ └── Monolith recommended. Reason: microservices add accidental complexity before you understand the domain.53 ├── Medium team, well-understood domain, scaling issues in monolith54 │ └── Decompose selectively: extract hot paths first (bounded contexts)55 ├── Large organization, multiple teams, clear domain boundaries56 │ └── Microservices aligned to team topology (Conway's Law)57 └── Migrating from monolith with growing complexity58 └── Strangler Fig: extract one bounded context at a time59```6061### How to Decompose?6263```64What boundary defines this service?65 ├── Business capability (orders, payments, shipping)66 │ └── Standard approach: map to business functions67 ├── DDD subdomain (core, supporting, generic)68 │ └── Follow bounded contexts from domain modeling69 ├── Team topology (one team = one or more services)70 │ └── Conway's Law: services mirror communication structure71 └── Data ownership (candidate for extraction)72 └── If a data domain changes independently, it's a decomposition candidate73```7475### Communication Pattern Selection7677```78What does the caller need?79 ├── Immediate response, strong consistency, low latency needed80 │ └── Synchronous (HTTP/gRPC) — but beware cascading failures81 ├── Fire-and-forget, eventual consistency OK, decouple in time82 │ └── Async (message queue / event) — resilient, scalable83 ├── Distributed transaction spanning 3+ services84 │ └── Saga orchestration — central coordinator85 ├── Guaranteed event publication DB → broker86 │ └── Transactional outbox — write event in same DB transaction87 └── Need to rebuild state from events or audit trail88 └── Event sourcing — store events as source of truth89```9091## Workflow9293### Step 1: Decompose by Bounded Context9495| Pattern | Approach | When |96|---|---|---|97| **Business Capability** | Map to business functions (orders, payments, shipping) | Clear organizational boundaries |98| **Subdomain** | Follow DDD bounded contexts | Complex domain, multiple subdomains |99| **Strangler Fig** | Incrementally replace monolith features | Brownfield migration |100| **Self-contained Service** | Service owns its data, API, and logic | Least coupling, maximum autonomy |101102**Decomposition rules**:103- Service must be independently deployable104- Service must own its data exclusively (no shared databases)105- Service must be team-sizable (2-pizza team)106- Service communication must be via network calls (no in-process)107108### Step 2: Select Communication Patterns109110| Pattern | Type | Consistency | Latency | Use Case |111|---|---|---|---|---|112| **Synchronous (HTTP/gRPC)** | Request-response | Strong (if transactional) | Higher | Queries, commands needing immediate response |113| **Asynchronous (Message Queue)** | Event-driven | Eventual | Lower | Cross-service notifications, long-running processes |114| **Saga** | Choreography / Orchestration | Eventual | Medium | Distributed transaction spanning services |115| **Transactional Outbox** | Reliable event publishing | Strong | Low + async | Guaranteed event delivery with DB transaction |116117**Saga variants**:118- **Choreography**: Each service publishes events after local transaction. Lower complexity, harder to trace.119- **Orchestration**: Central coordinator tells each service what to do. Higher complexity, easier to trace, single point of failure.120- **Selection rule**: 3+ services in saga? Use orchestration. <3? Choreography is acceptable.121122### Step 3: Define Data Ownership123124| Pattern | Description | Trade-off |125|---|---|---|126| **Database per Service** | Each service owns its database | No shared schema, data duplication, eventual consistency |127| **CQRS** | Separate read and write models | Optimized queries, eventual consistency, higher complexity |128| **Event Sourcing** | Store events, derive state | Complete audit trail, complex querying, storage growth |129| **Saga (data)** | Compensating transactions | No distributed lock, eventual consistency, compensating logic needed |130131**Data ownership rules**:132- No service ever accesses another service's database directly133- No shared database between services (exception: read-only reference data)134- Data duplication is acceptable and expected (each service owns its view)135136### Step 4: Configure Service Discovery137138| Pattern | Description |139|---|---|140| **Client-side Discovery** | Client queries registry, load-balances directly |141| **Server-side Discovery** | Load balancer queries registry, routes request |142| **Service Registry** | DNS-based (Consul, Eureka, Kubernetes DNS) |143144**Recommendation**: Kubernetes-native (DNS for discovery, Service for load balancing). Only use external registry if running outside K8s.145146### Step 5: Implement Observability147148| Pillar | Tool (Language-agnostic) | What to Capture |149|---|---|---|150| **Logging** | Structured (JSON), centralized | Service ID, trace ID, span ID, severity, message |151| **Metrics** | Prometheus + Grafana | RED (Rate, Errors, Duration) for every endpoint |152| **Tracing** | OpenTelemetry | Request path across services, span timings |153| **Health Checks** | Readiness + Liveness probes | Can serve traffic? Is process alive? |154155### Step 6: Choose Deployment Strategy156157| Pattern | Strategy | Risk |158|---|---|---|159| **Blue/Green** | Two full environments, switch traffic | Low, double resources |160| **Canary** | Gradual traffic shift | Medium, requires monitoring |161| **Rolling** | Incremental instance replacement | Low, slow |162| **Feature Flags** | Toggle features independently | Low, requires flag infrastructure |163164### Step 7: Apply Resilience Patterns165166| Pattern | Description |167|---|---|168| **Circuit Breaker** | Stop calling failing service, fail fast |169| **Bulkhead** | Isolate resources per service client |170| **Retry with Backoff** | Exponential backoff + jitter |171| **Timeout** | Hard timeout per external call |172| **Fallback** | Degraded response when service unavailable |173| **Rate Limiter** | Protect services from overload |174175### Step 8: Implement Security176177| Pattern | Description |178|---|---|179| **API Gateway Auth** | Centralized authentication at gateway |180| **JWT / OAuth2** | Token-based service-to-service auth |181| **mTLS** | Mutual TLS for service mesh |182| **Service Mesh** | Istio / Linkerd for transparent mTLS, policy |183184### Step 9: Plan Migration from Monolith1851861. **Identify seams** — areas of code that change independently1872. **Extract read model first** — read-only microservice serving cached/pre-computed data1883. **Extract write model** — feature flag to route writes to new service, dual-write during transition1894. **Strangler** — incrementally replace monolith endpoints with service endpoints1905. **Monolith retirement** — when all features migrated, decommission monolith191192### Step 10: API Versioning and Contracts193194```typescript195// Contract-first approach: define OpenAPI / protobuf before implementation196// Use consumer-driven contracts (CDC) to detect breaking changes197198// API versioning strategies:199// 1. URL path: /v1/orders, /v2/orders200// 2. Header: Accept: application/vnd.myapp.v1+json201// 3. Query param: /orders?version=1202// Recommendation: URL path for major versions, additive field expansion for minor203```204205### Step 11: Cross-Cutting Concerns206207| Concern | Implementation |208|---------|---------------|209| Configuration | Centralized (Consul, etcd, K8s ConfigMap + secrets). Never env-specific in code |210| Shared libraries | Minimize. Prefer duplication over coupling via shared libs |211| Error handling | Standard error envelope across all services |212| Health checks | Readiness (can serve?) + Liveness (is alive?) exposed on management port |213| Graceful shutdown | Drain connections, complete in-flight, then exit |214| Rate limiting | Per service + global via API gateway |215216### Step 12: API Gateway Integration217218```typescript219// API Gateway (Kong / APISIX / Envoy) configuration pattern220// Route: /orders/* -> order-service:3001221// Route: /payments/* -> payment-service:3002222// Global: rate limit 1000 req/min per tenant, auth via JWT223224// Gateway-level concerns:225// 1. Authentication — validate JWT before routing to service226// 2. Rate limiting — per-tenant, per-endpoint, burst allowance227// 3. Request validation — schema-based body validation at edge228// 4. Response transformation — strip internal headers, add CORS229// 5. Circuit breaking — gateway can fail fast before calling service230```231232### Step 13: Service Mesh Integration233234```yaml235# Istio VirtualService for traffic splitting (canary)236apiVersion: networking.istio.io/v1beta1237kind: VirtualService238metadata:239 name: order-service240spec:241 hosts:242 - order-service243 http:244 - match:245 - headers:246 x-canary: { exact: "true" }247 route:248 - destination:249 host: order-service250 subset: v2251 weight: 100252 - route:253 - destination:254 host: order-service255 subset: v1256 weight: 90257 - destination:258 host: order-service259 subset: v2260 weight: 10261# Service mesh provides: mTLS, traffic shifting, fault injection,262# circuit breaking, observability — without changing application code263```264265### Step 14: Database per Service Implementation266267```typescript268// Order Service — owns its PostgreSQL database269// Schema: orders, order_items, order_events270// No other service has direct DB access271272// Payment Service — owns its PostgreSQL database273// Schema: payments, refunds, payment_methods274275// Cross-service data access: via API calls only276// Example: Order Service needs payment status277// Solution: call `GET /payments/v1/orders/:orderId` — never query payment DB278```279280### Step 15: Contract Testing with Pact281282```typescript283// Consumer-driven contract test (Order Service -> Payment Service)284describe('Order Service - Payment API contract', () => {285 const provider = new Pact({286 consumer: 'OrderService',287 provider: 'PaymentService',288 port: 4000,289 });290291 beforeAll(() => provider.setup());292 afterEach(() => provider.verify());293 afterAll(() => provider.finalize());294295 it('should process payment and return transaction ID', async () => {296 await provider.addInteraction({297 state: 'payment can be processed',298 uponReceiving: 'a request to process payment',299 withRequest: {300 method: 'POST',301 path: '/v1/payments',302 headers: { 'Content-Type': 'application/json' },303 body: { orderId: '123', amount: 99.99, currency: 'USD' },304 },305 willRespondWith: {306 status: 200,307 headers: { 'Content-Type': 'application/json' },308 body: { transactionId: like('txn_abc123'), status: 'completed' },309 },310 });311 const result = await paymentClient.processPayment({ orderId: '123', amount: 99.99, currency: 'USD' });312 expect(result.transactionId).toBeDefined();313 });314});315```316317## API Composition Pattern318```typescript319// API Gateway / BFF that aggregates multiple downstream services320// Instead of client making 4 requests, gateway does it server-side321322interface ProductDetailsResponse {323 product: Product;324 inventory: Inventory;325 reviews: Review[];326 recommendations: Product[];327}328329async function getProductDetails(productId: string): Promise<ProductDetailsResponse> {330 const [product, inventory, reviews, recommendations] = await Promise.all([331 fetchProductService(productId),332 fetchInventoryService(productId),333 fetchReviewService(productId),334 fetchRecommendationService(productId),335 ]);336337 return { product, inventory, reviews, recommendations };338}339340// Circuit breaker for resilient inter-service calls341class CircuitBreaker {342 private failures = 0;343 private lastFailureTime = 0;344 private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';345 private readonly threshold = 5;346 private readonly timeout = 30000; // 30s347348 async call<T>(fn: () => Promise<T>): Promise<T> {349 if (this.state === 'OPEN') {350 if (Date.now() - this.lastFailureTime > this.timeout) {351 this.state = 'HALF_OPEN';352 } else {353 throw new Error('Circuit breaker is OPEN');354 }355 }356357 try {358 const result = await fn();359 this.failures = 0;360 this.state = 'CLOSED';361 return result;362 } catch (err) {363 this.failures++;364 this.lastFailureTime = Date.now();365 if (this.failures >= this.threshold) {366 this.state = 'OPEN';367 }368 throw err;369 }370 }371}372```373374## Service Discovery375```yaml376# Kubernetes Headless Service for DNS-based discovery377# Services discover peers via SRV DNS lookups378apiVersion: v1379kind: Service380metadata:381 name: payment-service382spec:383 clusterIP: None # Headless — returns pod IPs384 selector:385 app: payment-service386 ports:387 - name: grpc388 port: 50051389 - name: health390 port: 8080391```392393```typescript394// DNS-based service discovery with retry395import * as dns from 'dns/promises';396397async function resolveService(name: string): Promise<string> {398 const records = await dns.resolveSrv(`_grpc._tcp.${name}.svc.cluster.local`);399 // Round-robin across healthy instances400 const target = records[Math.floor(Math.random() * records.length)];401 return `${target.name}:${target.port}`;402}403```404405## Production Considerations406407| Concern | Practice |408|---------|----------|409| Service boundaries wrong | Expect to merge/split services as understanding grows. Plan for refactoring |410| Network latency | Inter-service calls add 1-10ms. Batch queries where possible |411| Data consistency | Eventual consistency is default. Accept it or use saga with compensating actions |412| Team autonomy vs consistency | Balance: shared infrastructure (monitoring, CI) without coupling service decisions |413| Testing | Unit (service-local) + Integration (per service) + Contract (per API pair) + E2E (minimal) |414| Observability | Must be in place before going live. Debugging without it is guesswork |415| Cold start latency | Serverless services (Lambda) add 200ms-5s cold start — keep latency-critical services on persistent compute |416| Inter-service auth overhead | mTLS handshake adds ~10-50ms per connection — use connection pooling and keepalive |417| Schema changes | Independent DB migrations per service — coordination needed for cross-service schema changes |418| Backup and restore | Each service has independent backup strategy — test restore procedure quarterly |419420## Security421422| Layer | Threat | Mitigation |423|-------|--------|------------|424| API Gateway | DDoS, brute force | Rate limiting, WAF, IP allowlisting, request size limits |425| Inter-service | Man-in-the-middle | mTLS with short-lived certs (Istio/Linkerd auto-rotation) |426| Data at rest | Data breach | Encrypt DB volumes (AES-256), encrypt S3 buckets (SSE-KMS) |427| Secrets | Credential leak | Vault/AWS Secrets Manager, never in env files or config repos |428| Auth tokens | Token theft | Short-lived JWT (15 min), refresh tokens with rotation, httpOnly cookies |429| Supply chain | Compromised dependency | Dependency scanning (Snyk/Dependabot), signed artifacts, SBOM generation |430| API contracts | Breaking changes | Consumer-driven contracts, CI-validated, versioned APIs |431432```typescript433// Service-to-service authentication with mTLS434import { credentials, Metadata } from '@grpc/grpc-js';435import { readFileSync } from 'fs';436437const rootCert = readFileSync('/etc/ssl/certs/ca-cert.pem');438const clientCert = readFileSync('/etc/ssl/certs/service-cert.pem');439const clientKey = readFileSync('/etc/ssl/certs/service-key.pem');440441const channelCredentials = credentials.createSsl(rootCert, clientKey, clientCert);442const client = new PaymentServiceClient('payment-service:443', channelCredentials);443```444445## Anti-Patterns446447| Anti-Pattern | Problem | Fix |448|---|---|---|449| **Distributed Monolith** | Services tightly coupled, deployed together | Enforce strict API contracts, independent deploy |450| **Shared Database** | Multiple services same DB schema | Extract shared data into dedicated service |451| **Too Fine-grained** | Excessive network calls, latency | Merge related services |452| **God Service** | One service does everything | Decompose by capability |453| **No Monitoring** | Cannot debug production issues | Add OpenTelemetry before going live |454| **Synchronous Chains** | A calls B calls C — high latency, fragile | Async where possible, parallel calls |455| **Leaky Abstractions** | Service exposes internal DB schema in API | API is contract — hide implementation |456| **Golden Hammer** | All problems solved with microservices | Consider monolith first, extract when needed |457| **Chatty Services** | Many small calls instead of one batched call | Use GraphQL or API composition layer |458| **Shared Model Classes** | Same DTO class in multiple services | Each service has its own view of data (duplication is OK) |459| **No Schema Governance** | Inconsistent API designs across services | API style guide + linting in CI (Spectral for OpenAPI) |460| **Synchronous Event Publishing** | Services send events inline in request path | Use transactional outbox pattern |461462## Performance Benchmarks463464| Pattern | Latency (p50) | Latency (p99) | Throughput |465|---------|---------------|---------------|------------|466| In-process method call | < 1µs | < 1µs | Unlimited |467| HTTP inter-service (same AZ) | 2-5ms | 10-20ms | ~10K req/s per instance |468| gRPC inter-service (same AZ) | 0.5-2ms | 5-10ms | ~50K req/s per instance |469| Message queue (async) | 5-50ms | 100-500ms | ~100K msg/s per broker |470| Saga (orchestration, 3 steps) | 15-50ms | 100-200ms | ~5K saga/s |471| Service mesh sidecar overhead | +1-3ms | +5-10ms | ~5% throughput reduction |472473## Rules474- No shared databases between services — ever.475- Each service independently deployable with its own CI/CD.476- 3+ services in a saga? Use orchestration.477- Kubernetes-native discovery preferred over external registries.478- OpenTelemetry for all observability pillars.479- No service calls another service's database directly.480- Strangler Fig for monolith migration — no big-bang rewrites.481- Communication patterns documented per service pair with rationale.482- Always define API contracts before implementation (contract-first).483- Each service has at most one DB (shared-nothing).484- Prefer eventual consistency unless strong consistency is legally required.485- gRPC for high-throughput internal calls (< 2ms latency). HTTP for external or low-throughput internal.486- Rate limit at the gateway AND per-service for defense in depth.487- Use consumer-driven contracts to detect breaking API changes in CI.488- Every service must have independent backup and restore tested quarterly.489490## References491 - references/communication-patterns.md — Communication Patterns492 - references/data-patterns.md — Data Patterns493 - references/decomposition-patterns.md — Decomposition Patterns494 - references/microservices-communication.md — Microservices Communication495 - references/microservices-observability.md — Microservices Observability496 - references/microservices-testing.md — Microservices Testing497## Handoff498Hand off to `devops/containerization/SKILL.md` for container orchestration setup. Hand off to `backend/universal/event-driven/SKILL.md` for detailed event-driven patterns. Hand off to `backend/universal/database-patterns/SKILL.md` for data consistency strategies.499## Implementation Patterns500501### Observer Pattern for Event Handling502`503interface EventObserver<T> {504 onEvent(event: T): Promise<void>;505}506507class EventBus<T> {508 private observers: Set<EventObserver<T>> = new Set();509 subscribe(observer: EventObserver<T>): void {510 this.observers.add(observer);511 }512 unsubscribe(observer: EventObserver<T>): void {513 this.observers.delete(observer);514 }515 async emit(event: T): Promise<void> {516 const results = Array.from(this.observers).map(o => o.onEvent(event));517 await Promise.allSettled(results);518 }519}520`521522### Configuration-Driven Approach523`524config:525 defaults:526 timeout: 30s527 retryCount: 3528 overrides:529 production:530 timeout: 60s531 retryCount: 5532 development:533 timeout: 300s534 retryCount: 1535`536537## Production Considerations538539### Deployment Checklist540- [ ] Configuration validated against schema before startup541- [ ] Health check endpoints registered and monitored542- [ ] Graceful shutdown with draining period (30s timeout)543- [ ] Resource limits configured (CPU, memory, file descriptors)544- [ ] Log level set appropriate for environment545- [ ] Metrics endpoint secured and exposed546- [ ] Rate limiting configured per-tier547- [ ] TLS certificates valid and auto-renewing548- [ ] Database migrations run as separate deployment step549- [ ] Feature flags ready for gradual rollout550551### Monitoring and Alerting552| Metric | Threshold | Severity | Action |553|--------|-----------|----------|--------|554| Error rate | > 1% over 5min | Critical | Page on-call |555| p99 latency | > 2s over 5min | Warning | Investigate |556| Throughput drop | > 50% over 1min | Critical | Check upstream |557| Queue depth | > 1000 over 1min | Warning | Scale consumers |558| Disk usage | > 85% | Warning | Clean or expand |559| Memory usage | > 90% heap | Critical | Restart or scale |560561## Anti-Patterns562563| Anti-Pattern | Symptom | Root Cause | Solution |564|-------------|---------|------------|----------|565| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |566| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |567| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |568| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |569570## Performance Optimization571572### Caching Strategy573Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge).574Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).575576### Resource Pooling577- Database connections: Pool of reusable connections (HikariCP, pgBouncer)578- HTTP connections: Keep-alive + connection pooling for external calls579- Thread pool: Bounded thread pools for async task execution580581### Profiling Methodology5821. Establish baseline with production traffic profile5832. Profile CPU with sampling profiler (pprof, perf, async-profiler)5843. Profile memory with heap dumps and allocation tracking5854. Profile I/O with strace/perf trace for syscall analysis5865. Profile latency with distributed tracing (OpenTelemetry)5876. Identify bottleneck, formulate hypothesis, implement fix5887. Re-profile to verify improvement, repeat589590## Security Considerations591592### Threat Modeling (STRIDE)593- Spoofing: Identity validation, authentication594- Tampering: Integrity checks, digital signatures595- Repudiation: Audit logs, non-repudiation596- Information disclosure: Encryption, access control597- Denial of service: Rate limiting, resource quotas598- Elevation of privilege: Principle of least privilege599600### Supply Chain Security601- Dependency scanning: Snyk, Dependabot, Trivy602- SBOM generation: CycloneDX or SPDX format603- Signed commits: GPG or SSH commit signing604- Artifact verification: Checksum validation, signature verification605606### Secrets Management607- Secrets never in code — always in secrets manager (Vault, AWS Secrets Manager)608- Rotation policy: Rotate database credentials every 90 days609- Access audit: Log every secrets access, alert on anomalies610- Encryption at rest and in transit for all secrets611- Principle of least privilege: each service gets only its own secrets612613## Rules614- Default-deny security posture — allow only explicitly required access.615- All inputs validated, all outputs encoded, all errors handled.616- Defend in depth — multiple layers of security controls.617- Fail securely — errors default to safe behavior.618- Log security-relevant events for audit and investigation.619- Keep dependencies updated — automate vulnerability scanning.620- Design for observability from day one, not as an afterthought.621- Document all architectural decisions with rationale.622- Review code for security, performance, and correctness before merging.