← Back to courses

Microservices Patterns

Decomposition · communication · data · reliability · observability · security · deployment · transactions

01Decomposition Patterns- How to split a system into microservices
Decompose by Business Capabilitydecompose
Split services around stable business capabilities such as users, orders, billing, and shipping.
Business Capabilities ├─ User Management → UserService ├─ Order Processing → OrderService ├─ Billing → BillingService └─ Shipping → ShippingService Teams own capabilities end-to-end
e.g.: Amazon teams for Cart, Search, Checkout
Decompose by Subdomain (DDD)DDD
Use Domain-Driven Design bounded contexts to create services with their own language and model.
DDD Subdomains Core Domain → OrderService (competitive edge) Supporting → InventoryService (needed but not core) Generic → AuthService (buy/use off-shelf) Each has its own model & language
e.g.: DDD by Eric Evans, Vaughn Vernon
Strangler Fig Patternmigration
Incrementally extract services from a monolith while routing selected traffic to the new system.
Monolith → Microservices migration Step 1: Monolith handles everything Step 2: Extract UserService, route user traffic Monolith → UserService Step 3: Extract OrderService next Step N: Monolith retires completely
e.g.: Real-world monolith to microservice migrations
Self-Contained Servicedesign
Design services to answer requests from local ownership data instead of chaining synchronous calls.
Anti-pattern (chatty): OrderSvc → UserSvc → InventorySvc all in one request = cascading failures Self-Contained (correct): OrderSvc → handles locally syncs data async via events
e.g.: Event-driven microservices with local projections
02Communication Patterns- How services talk to each other
Synchronous REST / gRPCsync
Direct request-response communication where the caller waits for a downstream service.
REST (HTTP/JSON) ServiceA → GET /users/123 → UserService ← { id:123, name:"Alice" } ← waits synchronously gRPC (Protobuf / HTTP2) ServiceA → GetUser(id:123) → UserService ← User{id,name} ← strongly typed, faster than REST
e.g.: Express REST, gRPC with Protobuf, GraphQL
Asynchronous Messagingasync
Services communicate through a broker so producers and consumers can scale independently.
Message Broker Pattern OrderSvc → publish(OrderPlaced) → [Broker] ↓ ↓ EmailSvc InventorySvc processes processes async async Broker: Kafka, RabbitMQ, SQS, NATS
e.g.: Kafka, RabbitMQ, SQS, NATS
Event-Driven Communicationevents
Services publish domain events when important business facts happen.
Domain Events OrderService emits: → OrderPlaced { orderId, items, userId } → OrderCancelled { orderId, reason } → OrderShipped { orderId, trackingId } Subscribers react: EmailSvc listens → OrderPlaced AnalyticsSvc listens → all events InventorySvc listens → OrderPlaced, OrderCancelled
e.g.: Kafka events, EventBridge, Node EventEmitter
API Gateway Patterngateway
A single entry point routes, authenticates, rate-limits, and protects backend services.
Request Flow through Gateway Client → [API Gateway] │ 1. Authenticate (JWT/OAuth) │ 2. Rate limit check │ 3. Route to service │ 4. Load balance ↓ GET /users → UserService POST /orders → OrderService GET /products → ProductService
e.g.: Kong, AWS API Gateway, Nginx, Traefik
Service Discoverydiscovery
Services register and discover dynamic endpoints instead of hardcoding hostnames or IPs.
Client-Side Discovery ServiceA → query [Service Registry] ← "UserSvc is at 10.0.1.5:8080" → call 10.0.1.5:8080 directly Server-Side Discovery ServiceA → [Load Balancer] LB queries registry internally → routes to correct instance Registry: Consul, Eureka, K8s DNS
e.g.: Consul, Kubernetes DNS, Eureka
Microservices Communication Deep Divecommunication
Interactive deep dive on service discovery, load balancing, resilience, and live communication behavior.
Client -> Discovery -> Load Balancer -> Service Instance Failures -> Retry / Circuit Breaker Health checks -> dynamic routing
e.g.: Service discovery + round robin + circuit breaker simulation
03Service Design Patterns- How individual services are structured internally
Sidecar Patterninfra
Run a helper container beside the app to handle cross-cutting infrastructure concerns.
Kubernetes Pod ┌─────────────────────────────────┐ │ Pod │ │ ┌────────────┐ ┌─────────────┐ │ │ │ Main App │ │ Sidecar │ │ │ │ :8080 │ │ Envoy Proxy│ │ │ │ │ │ Log Agent │ │ │ └────────────┘ └─────────────┘ │ │ shared network + storage │ └─────────────────────────────────┘
e.g.: Istio Envoy sidecar, Datadog agent, Fluentd
Backends for Frontends (BFF)design
Create a tailored backend/API layer per client type such as web, mobile, or TV.
Per-Client Backends 📱 Mobile → [BFF-Mobile] → lean payload 🌐 Web → [BFF-Web] → rich payload 📺 TV → [BFF-TV] → media-optimized ↓ all call same microservices [UserSvc][OrderSvc][ProductSvc]
e.g.: Netflix, SoundCloud BFF architecture
Ambassador Patternproxy
Use a local proxy to handle network concerns for a service.
Ambassador as Proxy YourService → [Ambassador] │ handles: ├─ retry logic ├─ circuit breaking ├─ request logging ├─ rate limiting └─ TLS termination ↓ Remote Service / External API
e.g.: Envoy, Ambassador API Gateway, Linkerd
Service Meshinfra
A mesh of sidecar proxies manages service-to-service traffic transparently.
Service Mesh Topology ServiceA ServiceB [App] ←→ [Proxy] ←────→ [Proxy] ←→ [App] ↕ ↕ [Control Plane: Istio] configures all proxies handles: mTLS, tracing, retries, load balance
e.g.: Istio, Linkerd, Consul Connect
Anti-Corruption Layer (ACL)DDD
Translate between your clean domain model and an external or legacy model.
ACL protects your domain External Legacy System uses: customer_no, acct_ref ↓ [Anti-Corruption Layer] translates: customer_no → userId acct_ref → accountId ↓ Your Clean Domain uses: userId, accountId
e.g.: Legacy adapters, SDK wrappers, domain mappers
04Data Patterns- How services manage and share data
Database per Servicedata
Each service owns its database and no other service reads or writes it directly.
Data Ownership Model UserService → [PostgreSQL db_users] OrderService → [MongoDB db_orders] ProductService → [Redis db_products] SearchService → [Elastic db_search] ✗ OrderService cannot query db_users directly ✓ OrderService calls UserService API instead
e.g.: Microservices data ownership best practice
CQRSdata
Separate write models from read models so each can optimize for its own workload.
CQRS inside a service WRITE SIDE (Commands) POST /orders → CommandHandler → validates business rules → updates Write DB (normalized) → emits OrderPlaced event READ SIDE (Queries) GET /orders → QueryHandler → Read DB (denormalized, fast) → populated from events
e.g.: Event-sourced systems, denormalized read stores
Event Sourcingevents
Store every state change as an immutable event and rebuild state by replaying events.
Event Store (append-only log) [1] AccountCreated { userId: 1, balance: 0 } [2] MoneyDeposited { userId: 1, amount: 500 } [3] MoneyWithdrawn { userId: 1, amount: 200 } [4] MoneyDeposited { userId: 1, amount: 100 } Replay 1→4: balance = 0+500-200+100 = 400 Time travel: replay 1→2 = balance = 500
e.g.: EventStoreDB, Axon, custom event logs
Transactional Outboxreliable events
Write business data and outgoing event records in one local database transaction.
Outbox Pattern Flow Begin Transaction: INSERT INTO orders (order data) INSERT INTO outbox (event data) ← same tx Commit Transaction ✓ [Outbox Poller / CDC] reads outbox table → publishes to Kafka/RabbitMQ → marks outbox record as sent Atomic: either both saved or neither
e.g.: Debezium outbox transform, custom outbox poller
API Compositionquery
Aggregate data from multiple services into one response.
Composition Flow Client: GET /order-summary/456 ↓ [OrderComposer] ↓ parallel calls: → UserService.getUser(orderId.userId) → OrderService.getOrder(456) → ProductService.getProducts(orderItems) ← merge all responses return combined OrderSummary to client
e.g.: GraphQL resolvers, BFF aggregators
Change Data Capture (CDC)CDC
Capture database changes and publish them as events.
CDC via DB Transaction Log Application → writes to [PostgreSQL] PostgreSQL → WAL (Write-Ahead Log) [Debezium] → reads WAL changes → publishes to Kafka: orders.created { ... } orders.updated { ... } Consumers → react to DB changes
e.g.: Debezium, Kafka Connect, database logs
05Reliability Patterns- Keep services resilient under failure
Circuit Breakerresilience
Stop calling a failing dependency to prevent cascading failures.
Circuit Breaker State Machine CLOSED (normal) requests flow → 5 failures in 10s ↓ OPEN (tripped) all requests → instant fail + fallback wait 30s timeout ↓ HALF-OPEN (testing) let 1 request through success → back to CLOSED fail → back to OPEN
e.g.: opossum, Resilience4j, Hystrix
Bulkhead Patternisolation
Isolate resources so one failing area does not take down the whole system.
Resource Pools (Bulkheads) Thread Pool A (10 threads) → Payment calls Thread Pool B (10 threads) → Inventory calls Thread Pool C (10 threads) → Email calls Payment service hangs → Pool A exhausted Pool B, C still available Inventory, Email still work ✓
e.g.: Thread pools, connection pools, K8s namespaces
Retry with Exponential Backoffretry
Retry transient failures with progressively longer waits and jitter.
Retry Strategy Attempt 1 → fails (wait 100ms) Attempt 2 → fails (wait 200ms) Attempt 3 → fails (wait 400ms) Attempt 4 → success ✓ With Jitter (avoids thundering herd) Wait = min(cap, base * 2^attempt) + random(0..100ms) Idempotency required for safe retries!
e.g.: axios-retry, got retry, AWS SDK retry
Timeout Patterntimeout
Never wait forever for an outgoing call.
Timeout Configuration connect_timeout: 2s (TCP handshake) request_timeout: 5s (full response) read_timeout: 3s (each read chunk) ServiceA → calls ServiceB → no response in 5s → TimeoutException thrown → return fallback / 503 Without timeout: thread hangs forever
e.g.: fetch AbortController, axios timeout, gRPC deadline
Health Check / Readiness Probeops
Expose endpoints that tell orchestrators whether the service is alive and ready.
Health Probe Types Liveness: GET /health/live Is the process alive? (not deadlocked?) fail → K8s kills + restarts pod Readiness: GET /health/ready Is service ready to receive traffic? fail → removed from load balancer (e.g. still warming up DB connections) Startup: GET /health/startup Did the app finish starting? (slow start)
e.g.: Kubernetes livenessProbe, readinessProbe, Consul health
06Observability Patterns- See what happens inside distributed services
Distributed Tracingobserve
Follow one request across services using trace IDs and spans.
Trace: traceId = abc-123 [Gateway] span 1: 0ms → 120ms [UserSvc] span 2: 10ms → 40ms [OrderSvc] span 3: 45ms → 110ms [DB call] span 4: 50ms → 105ms ← slow! [EmailSvc] span 5:115ms → 118ms Identify bottleneck: DB call at span 4
e.g.: OpenTelemetry, Jaeger, Zipkin, AWS X-Ray
Centralized Logginglogs
Aggregate structured logs from all services into one searchable place.
Log Aggregation Pipeline ServiceA → { traceId, level, msg, ts } ServiceB → { traceId, level, msg, ts } ServiceC → { traceId, level, msg, ts } ↓ log shipper (Fluentd / Filebeat) [Central Store: Elasticsearch / Loki] ↓ [Dashboard: Kibana / Grafana] search: traceId=abc-123 → all logs
e.g.: ELK, Loki + Grafana, Datadog logs, CloudWatch
Metrics & Monitoringmetrics
Emit numeric service and resource measurements for dashboards and alerts.
RED Method (per service) Rate → requests per second Errors → error rate % Duration → p50, p95, p99 latency USE Method (per resource) Utilization → CPU/Memory % Saturation → queue depth Errors → error count
e.g.: Prometheus + Grafana, Datadog, New Relic
Correlation IDobserve
Thread a unique request ID through headers, logs, and downstream calls.
Correlation ID Flow Client → Gateway generates: X-Request-ID: req-789xyz passes header to all services UserSvc: log { reqId: "req-789xyz", ... } OrderSvc: log { reqId: "req-789xyz", ... } PaymentSvc:log { reqId: "req-789xyz", ... } Search logs: reqId=req-789xyz → full trace
e.g.: X-Request-ID, express-request-id, Morgan logger
Log Aggregation + Alertingalerts
Trigger alerts from logs and metrics before users report problems.
Alert Rules (examples) error_rate > 5% → PagerDuty alert p99_latency > 2000ms → Slack warning pod restarts > 3 → immediate page disk usage > 85% → warning email SLO / Error Budget SLO: 99.9% uptime = 43.8 min/month Budget: consumed 40 min this month → freeze deployments, focus on stability
e.g.: PagerDuty, OpsGenie, Grafana Alerts, CloudWatch Alarms
07Security Patterns- Secure service-to-service and client-to-service communication
Mutual TLS (mTLS)security
Both client and server services verify each other with certificates.
mTLS Handshake ServiceA → presents cert to ServiceB ServiceB → presents cert to ServiceA Both verify against trusted CA Encrypted + mutually authenticated channel Service Mesh handles this automatically
e.g.: Istio mTLS, Linkerd, SPIFFE/SPIRE
Token-Based Auth (JWT)auth
Use signed tokens to propagate identity and authorization claims.
JWT Flow Client → POST /auth/login AuthSvc → signs JWT { userId, roles, exp } Client → sends JWT in Authorization header ServiceA → verify signature locally ✓ ServiceA → extract userId, roles from payload Stateless — no session store needed
e.g.: jsonwebtoken, jose, Auth0, Keycloak, Cognito
Zero Trust Architecturesecurity
Authenticate and authorize every request, even inside the private network.
Zero Trust per request Old model: inside network = trusted ServiceA calls ServiceB → just works Zero Trust model: verify everything ServiceA calls ServiceB → present identity (mTLS cert / JWT) → B verifies identity → B checks authorization policy → B logs the access → allow or deny
e.g.: BeyondCorp, SPIFFE/SPIRE, Istio AuthorizationPolicy
Secrets Managementsecrets
Store and rotate credentials in a vault instead of hardcoding them.
Secrets Vault Pattern ✗ Bad: DB_PASS=secret123 in code / env ✓ Good: HashiCorp Vault Service → authenticate to Vault Vault → return secret (time-limited) Service → use secret, auto-renew Rotation: new secret → services pick up automatically without redeploy
e.g.: HashiCorp Vault, AWS Secrets Manager, encrypted K8s Secrets
08Deployment Patterns- How microservices are deployed and released safely
Blue-Green Deploymentdeploy
Run two identical environments and switch traffic after the new one passes checks.
Blue [v1.0] ← 100% live traffic Green [v2.0] ← deploy + test here Switch: Blue → standby Green → 100% live traffic Rollback: flip switch back instantly
e.g.: AWS CodeDeploy, Kubernetes service switching
Canary Deploymentdeploy
Release to a small percentage of traffic, monitor, then gradually increase.
100 users total: 5% → v2 (canary 🐦) — monitor closely 95% → v1 (stable) metrics OK? → increase to 25% → 50% → 100% metrics bad? → rollback 5% → 0% instantly
e.g.: Argo Rollouts, Flagger, Spinnaker, Istio weights
Rolling Updatedeploy
Replace old instances with new instances gradually.
Rolling Update Progress Start: [v1][v1][v1][v1][v1] Step 1: [v2][v1][v1][v1][v1] Step 2: [v2][v2][v1][v1][v1] Step 3: [v2][v2][v2][v1][v1] Done: [v2][v2][v2][v2][v2] API must be backward-compatible!
e.g.: Kubernetes Deployment default rollout
Shadow / Mirror Deploymentdeploy
Copy real traffic to the new version and compare its answers - no user ever sees them.
Duplicate live traffic to new version silently Users only receive response from old version Compare latency, errors, and results of both versions Best for: DB migrations, ML models, critical rewrites
e.g.: NGINX mirror module, Envoy traffic shadowing, Istio mirror
Recreate Deploymentdeploy
Stop old version first, then start the new version.
Terminate ALL current instances, then deploy new version Guarantees no version mixing - clean state Has intentional downtime window Best for: dev/staging, breaking schema changes
e.g.: Kubernetes strategy: Recreate, controlled maintenance windows
A/B Testing Rolloutexperiment
Show two versions to two groups of users and measure which one works better.
Route users by attributes (region, plan, ID) Measure business outcomes: conversions, engagement, revenue Not only release safety - this is product experimentation Best for: feature flags, pricing tests, UX experiments
e.g.: LaunchDarkly, Statsig, Optimizely, custom feature-flag routers
Immutable Infrastructuredeploy
Never patch running instances; build a new image and replace them.
Mutable (old way) Server → SSH in → apt update → patch (drift: servers become snowflakes) Immutable (correct) Code change → build new Docker image → push to registry → deploy new pods → terminate old pods Every deployment is predictable
e.g.: Docker, Kubernetes, Packer AMIs, GitOps with ArgoCD
🎯Deployment Interview Cardinterview
Layer-level interview focus across deployment strategies.
Q: Blue-Green vs Canary - when to choose each? A: Blue-Green for instant rollback and strict cutover. A: Canary for gradual risk control with live metrics.
Tip: mention rollback speed, observability, and infra cost tradeoffs.
09Distributed Transaction Patterns- How to maintain data consistency across services
Saga Pattern - Choreographytransactions
Services publish and react to events without a central coordinator.
Choreography Saga OrderSvc → emit: OrderCreated PaymentSvc → hear: OrderCreated → process payment → emit: PaymentProcessed StockSvc → hear: PaymentProcessed → reserve stock → emit: StockReserved ShipSvc → hear: StockReserved → ship On failure: compensating events PaymentSvc → emit: PaymentFailed OrderSvc → hear: PaymentFailed → cancel order
e.g.: Kafka choreography, EventBridge rules
Saga Pattern - Orchestrationtransactions
A central orchestrator commands each service step and handles compensation.
Orchestration Saga [Saga Orchestrator] drives the flow: Step 1: call PaymentSvc.charge() → success → go to step 2 → fail → compensate Step 2: call StockSvc.reserve() → success → go to step 3 → fail → call PaymentSvc.refund() Step 3: call ShipSvc.schedule() Full visibility: orchestrator knows state
e.g.: Temporal.io, AWS Step Functions, Conductor, Camunda
Outbox + Inbox Patternconsistency
Use outbox records for reliable sends and inbox records for idempotent receives.
Outbox (sender side) BEGIN TX: INSERT orders → order data INSERT outbox → { msgId, event, status:pending } COMMIT Poller: read outbox → publish to Kafka → mark sent Inbox (receiver side) Receive event { msgId: "m-456" } CHECK inbox table: msgId=m-456 exists? → YES: already processed, skip (idempotent) → NO: process + INSERT inbox(m-456)
e.g.: Debezium outbox, custom outbox poller, inbox table
Two-Phase Commit (2PC)consistency
A coordinator asks participants to prepare, then commits or rolls back all participants.
2PC Protocol Phase 1 — PREPARE: Coordinator → "prepare?" → DB-1, DB-2, DB-3 DB-1 → "ready ✓" DB-2 → "ready ✓" DB-3 → "ready ✓" Phase 2 — COMMIT: Coordinator → "commit!" → all DBs all commit atomically ✓ If DB-2 says "not ready": Coordinator → "rollback!" → all DBs
e.g.: XA transactions; rarely preferred in microservices
Production microservices use patterns from many categories together: boundaries, communication, data ownership, failure handling, observability, security, release safety, and consistency all matter.