| name | microservices-architecture |
| description | Microservices architecture patterns and best practices. Use when designing distributed systems, breaking down monoliths, or implementing service communication. |
Microservices Architecture
Comprehensive guide for designing and implementing microservices-based systems.
Microservices Fundamentals
What are Microservices?
MICROSERVICES = Independently deployable services
that do one thing well
Characteristics:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Single responsibility โ
โ โ Own their data โ
โ โ Independently deployable โ
โ โ Communicate via APIs โ
โ โ Technology agnostic โ
โ โ Owned by small teams โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Monolith vs Microservices
MONOLITH:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Single Application โ
โ โโโโโโโโฌโโโโโโโฌโโโโโโโฌโโโโโโโ โ
โ โ UsersโOrdersโ Cart โSearchโ โ
โ โโโโโโโโดโโโโโโโดโโโโโโโดโโโโโโโ โ
โ Single Database โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MICROSERVICES:
โโโโโโโโ โโโโโโโโ โโโโโโโโ โโโโโโโโ
โUsers โ โOrdersโ โ Cart โ โSearchโ
โ DB โ โ DB โ โ DB โ โ DB โ
โโโโโโโโ โโโโโโโโ โโโโโโโโ โโโโโโโโ
โ โ โ โ
โโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโ
API Gateway
When to Use Microservices
USE WHEN:
โ Large, complex domain
โ Need independent scaling
โ Multiple teams working in parallel
โ Different technology needs per service
โ Fault isolation is critical
โ Frequent, independent deployments
DON'T USE WHEN:
โ Small team/application
โ Simple domain
โ Tight latency requirements
โ Limited DevOps maturity
โ Unclear domain boundaries
Service Design
Domain-Driven Design
BOUNDED CONTEXTS:
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Orders โ โ Shipping โ
โ Context โ โ Context โ
โ โโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโ โ
โ โ Order โ โ โ โ Shipment โ โ
โ โ LineItem โ โ โ โ Carrier โ โ
โ โ Customer(ID)โ โ โ โ Address โ โ
โ โโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
Each context has its own:
- Ubiquitous language
- Data model
- Business rules
Service Boundaries
GOOD boundaries follow:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Business Capability โ
โ - What the business does โ
โ - e.g., Payment Processing, Inventory โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Subdomain โ
โ - Area of expertise โ
โ - e.g., Pricing, Catalog, Customer โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Single Responsibility โ
โ - Does one thing well โ
โ - Can explain in one sentence โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BAD boundaries:
โ Technical layers (UI service, DB service)
โ CRUD operations (User CRUD service)
โ Too granular (EmailSender service)
Service Size Guidelines
Right-sized service:
- 2-pizza team can own it (5-8 people)
- Rewrite in 2-4 weeks if needed
- Clear, single business purpose
- Minimal external dependencies
- Own its data completely
Too big: Multiple teams needed, mixed concerns
Too small: Can't function independently
Communication Patterns
Synchronous (Request/Response)
REST:
โโโโโโโโ HTTP GET /users/123 โโโโโโโโ
โClientโ โโโโโโโโโโโโโโโโโโโโโ>โServerโ
โ โ<โโโโโโโโโโโโโโโโโโโโโ โ โ
โโโโโโโโ { "name": "John" } โโโโโโโโ
gRPC:
โโโโโโโโ Binary/Protobuf โโโโโโโโ
โClientโ โโโโโโโโโโโโโโโโโโโโโ>โServerโ
โ โ<โโโโโโโโโโโโโโโโโโโโโ โ โ
โโโโโโโโ Strongly typed โโโโโโโโ
GraphQL:
โโโโโโโโ POST /graphql โโโโโโโโ
โClientโ โโโโโโโโโโโโโโโโโโโโโ>โServerโ
โ โ<โโโโโโโโโโโโโโโโโโโโโ โ โ
โโโโโโโโ Flexible queries โโโโโโโโ
Asynchronous (Event-Driven)
MESSAGE QUEUE:
โโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโ
โProducerโ โโโ> โ Queue โ โโโ> โConsumerโ
โโโโโโโโโโ โ(RabbitMQโ โโโโโโโโโโ
โ SQS) โ
โโโโโโโโโโโ
EVENT STREAMING:
โโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโ
โProducerโ โโโ> โ Topic โ โโโ> โConsumerโ
โโโโโโโโโโ โ (Kafka) โ โโโ> โConsumerโ
โโโโโโโโโโโ โโโ> โConsumerโ
โโโโโโโโโโ
Communication Comparison
| Pattern | Use Case | Trade-offs |
|---|
| REST | CRUD, simple queries | Simple, but chatty |
| gRPC | High performance, internal | Fast, but complex |
| GraphQL | Flexible client needs | Flexible, but overhead |
| Message Queue | Task processing | Decoupled, but delay |
| Event Streaming | Event sourcing, analytics | Scalable, but complex |
Data Management
Database per Service
SEPARATE DATABASES:
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ
โUsers โ โOrders โ โProductsโ
โService โ โService โ โService โ
โโโโโโโโโโค โโโโโโโโโโค โโโโโโโโโโค
โPostgreSQLโ โMongoDB โ โMySQL โ
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ
Benefits:
โ Independent scaling
โ Technology freedom
โ No shared schema coupling
โ Fault isolation
Challenges:
- No joins across services
- Eventual consistency
- Data duplication
Data Consistency Patterns
SAGA PATTERN (Choreography):
โโโโโโโโ โโโโโโโโ โโโโโโโโ
โOrder โโโโโโ>โPaymentโโโโโโ>โShip โ
โCreateโ โProcessโ โOrder โ
โโโโโโโโ โโโโโโโโ โโโโโโโโ
โ โ โ
โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ
Each service publishes events
that trigger next step
SAGA PATTERN (Orchestration):
โโโโโโโโโโโโโโ
โOrchestratorโ
โโโโโโโโโโโโโโ
/ โ \
โ โ โ
โโโโโโโโ โโโโโโโโ โโโโโโโโ
โOrder โ โPaymentโ โShip โ
โโโโโโโโ โโโโโโโโ โโโโโโโโ
Central coordinator manages flow
Event Sourcing
Instead of storing current state:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Account: $500 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Store all events:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. AccountCreated $0 โ
โ 2. Deposited $1000 โ
โ 3. Withdrawn $300 โ
โ 4. Withdrawn $200 โ
โ Current: $500 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Benefits:
โ Complete audit trail
โ Temporal queries
โ Event replay
โ Natural fit for CQRS
API Gateway
Gateway Pattern
โโโโโโโโโโโโโโโ
โ API Gateway โ
โโโโโโโโฌโโโโโโโ
โโโโโโโโโโโโฌโโโดโโโฌโโโโโโโโโโโ
โ โ โ โ
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ
โUsers โ โOrders โ โProductsโ โAuth โ
โService โ โService โ โService โ โService โ
โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ โโโโโโโโโโ
Gateway responsibilities:
- Request routing
- Authentication/Authorization
- Rate limiting
- Load balancing
- Request/Response transformation
- Caching
- Monitoring
BFF (Backend for Frontend)
Mobile App Web App
โ โ
โ โ
โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ
โ Mobile BFF โ โ Web BFF โ
โโโโโโโฌโโโโโโโ โโโโโโโฌโโโโโโโ
โ โ
โโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโ
โ Internal APIs โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Each BFF:
- Optimized for its client
- Aggregates multiple services
- Handles client-specific logic
Service Discovery
Client-Side Discovery
โโโโโโโโ โโโโโโโโโโโโโโโโ
โClientโโโโโ>โService โโโโโ> Service A (10.0.1.10)
โ โ โRegistry โโโโโ> Service A (10.0.1.11)
โ โ<โโโโโ(Consul, etcd)โโโโโ> Service A (10.0.1.12)
โโโโโโโโ โโโโโโโโโโโโโโโโ
Client queries registry, then calls service directly.
Client handles load balancing.
Server-Side Discovery
โโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โClientโโโโโ>โLoad Balancerโโโโโ>โService โ
โ โ โ โ โRegistry โ
โโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโดโโโโโโโโโ
โ โ โ
Service Service Service
Load balancer handles discovery and routing.
Simpler for clients.
Resilience Patterns
Circuit Breaker
States:
โโโโโโโโโโ Failures โโโโโโโโโโ
โ CLOSED โโโโโโโโโโโโโโโโ>โ OPEN โ
โ(normal)โ โ(reject)โ
โโโโโโโโโโ โโโโโโโโโโ
โ โ
โ Timeout โ
โ โโโโโโโโโโโโโโ โ
โโโโโHALF-OPEN โ<โโโโโโโโโ
โ(test) โ
โโโโโโโโโโโโโโ
Implementation:
- Track failure count
- Open circuit after threshold
- Reject calls while open
- Periodically test with half-open
- Close circuit on success
Retry Pattern
async function withRetry<T>(
fn: () => Promise<T>,
maxAttempts: number = 3,
backoff: number = 1000,
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (attempt === maxAttempts) throw error;
const delay = backoff * Math.pow(2, attempt - 1);
const jitter = delay * 0.1 * Math.random();
await sleep(delay + jitter);
}
}
}
Bulkhead Pattern
Isolate resources to prevent cascade:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Service A โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ โThread โ โThread โ โ
โ โPool 1 โ โPool 2 โ โ
โ โ(Service โ โ(Service โ โ
โ โ B) โ โ C) โ โ
โ โโโโโโโโโโโ โโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
If Service C is slow, only Pool 2 is affected.
Service B calls continue normally.
Timeout Pattern
async function withTimeout<T>(fn: () => Promise<T>, ms: number): Promise<T> {
return Promise.race([
fn(),
new Promise<T>((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms),
),
]);
}
const user = await withTimeout(
() => userService.getUser(id),
5000,
);
Observability
The Three Pillars
LOGS:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2024-01-15 10:30:45 [INFO] OrderService โ
โ Order created: { id: 123, user: 456 } โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
METRICS:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ order_created_total: 1523 โ
โ order_processing_seconds: 0.234 โ
โ active_connections: 45 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
TRACES:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [Request ID: abc123] โ
โ โโ Gateway: 2ms โ
โ โโ OrderService: 150ms โ
โ โ โโ UserService: 45ms โ
โ โ โโ InventoryService: 80ms โ
โ โโ Total: 152ms โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Distributed Tracing
Trace Context Propagation:
โโโโโโโโ X-Trace-ID: abc โโโโโโโโ
โAPI โโโโโโโโโโโโโโโโโโ>โOrder โ
โGW โ โSvc โ
โโโโโโโโ โโโโโโโโ
โ
X-Trace-ID: abc โ
โ
โโโโโโโโ
โUser โ
โSvc โ
โโโโโโโโ
Tools: Jaeger, Zipkin, AWS X-Ray, Datadog
Health Checks
app.get("/health/live", (req, res) => {
res.status(200).json({ status: "alive" });
});
app.get("/health/ready", async (req, res) => {
const dbHealthy = await checkDatabase();
const cacheHealthy = await checkCache();
if (dbHealthy && cacheHealthy) {
res.status(200).json({ status: "ready" });
} else {
res.status(503).json({
status: "not ready",
checks: { database: dbHealthy, cache: cacheHealthy },
});
}
});
Deployment
Containerization
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
Kubernetes Basics
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: order-service:1.0.0
ports:
- containerPort: 3000
resources:
limits:
cpu: "500m"
memory: "256Mi"
livenessProbe:
httpGet:
path: /health/live
port: 3000
readinessProbe:
httpGet:
path: /health/ready
port: 3000
Testing Strategies
Test Pyramid for Microservices
/\
/ \ E2E Tests
/โโโโ\ (Few, slow, brittle)
/ \
/โโโโโโโโ\ Contract Tests
/ \ (Service boundaries)
/โโโโโโโโโโโโ\
/ \ Integration Tests
/โโโโโโโโโโโโโโโโ\ (With dependencies)
/ \
/โโโโโโโโโโโโโโโโโโโโ\ Unit Tests
/ \ (Many, fast, isolated)
/________________________\
Contract Testing
describe("User Service Contract", () => {
it("returns user by ID", async () => {
await provider.addInteraction({
state: "user 123 exists",
uponReceiving: "a request for user 123",
withRequest: {
method: "GET",
path: "/users/123",
},
willRespondWith: {
status: 200,
body: {
id: "123",
name: like("John"),
email: like("john@example.com"),
},
},
});
});
});
Best Practices
DO:
- Start with a monolith, extract services later
- Define clear service boundaries
- Use asynchronous communication where possible
- Implement circuit breakers
- Centralize logging and monitoring
- Automate everything
- Design for failure
- Version your APIs
DON'T:
- Create too many, too small services
- Share databases between services
- Make synchronous chains too deep
- Ignore distributed system complexities
- Couple services through shared libraries
- Skip contract testing
- Deploy without monitoring
Migration Checklist
Monolith to Microservices