Use Cases and Real-world Examples
Microservices Patterns: Use Cases and Real-world Examples
Microservices patterns are not abstract dogma — each one exists to solve a specific failure mode that appears when you split a system into independently deployed services. The goal of this lesson is to stop treating patterns as a checklist and start treating them as answers to concrete questions: How do I keep data consistent across services? How do I stop one slow service from taking down the rest? How does a client talk to fifty services without knowing about all of them?
The core problem is this: a monolith gets consistency, a single database transaction, and one deploy for free. The moment you break it into services, you lose all three. Every microservices pattern is a way to buy back one of those lost guarantees at an acceptable price. If you can name the guarantee a pattern restores, you understand the pattern.
How the core patterns work, precisely
Four patterns cover most real interview and production scenarios:
- API Gateway — a single entry point that fronts many services. It handles cross-cutting concerns (TLS termination, authn/authz, rate limiting, request routing, response aggregation) so each service and each client stays simple. The client makes one call; the gateway fans out. Variant: Backend-for-Frontend (BFF), one gateway per client type (mobile, web, partner API).
- Saga — replaces a distributed ACID transaction with a sequence of local transactions, each in one service, linked by events. If step N fails, you run compensating transactions (semantic undos) for steps 1..N-1. Two flavors: choreography (services react to each other's events, no central brain) and orchestration (a coordinator service issues commands and tracks state).
- Circuit Breaker — wraps calls to a dependency in a state machine (
CLOSED→OPEN→HALF_OPEN). After a threshold of failures it tripsOPENand fails fast instead of piling up threads on a dead dependency, then probes recovery inHALF_OPEN. - CQRS + Event Sourcing — separate the write model from read models, and (optionally) persist state as an append-only log of events, projecting read-optimized views asynchronously.
A worked scenario: e-commerce checkout at scale
Consider a checkout flow handling 2,000 orders/sec at peak. Placing an order touches three services: Order, Payment, and Inventory, each owning its own database. There is no shared transaction, so we use an orchestration Saga:
- Order service creates the order in
PENDINGand emits a command to the orchestrator. - Orchestrator calls Inventory (reserve stock). If the item is out of stock, the reservation fails as a no-op — nothing to compensate — and the order moves to
CANCELLED. - Orchestrator calls Payment (charge card). Payment's local commit succeeds → emit
PaymentCaptured. If the charge fails, the orchestrator runs the compensating transactionReleaseStockReservation, then moves the order toCANCELLED.
Step order is deliberate: put the step most likely to fail for a mundane business reason (stock) first, while failure is still free to undo, and put the expensive-to-compensate money movement at the pivot — see the Saga in Depth page for the failure-frequency × compensation-cost rule.
Now the payment gateway is a third party with a p99 latency of ~300ms. During an outage, calls hang for a 5s timeout. At 2,000 QPS, threads pile up: by Little's Law the in-flight count climbs to 2000/s × 5s = 10,000 concurrent requests across the 5 s timeout window — thread pools exhaust and the whole checkout tier stalls (a cascading failure). A circuit breaker set to trip after, say, 50% failures over a 10s rolling window flips to OPEN and fails those calls in under 1ms, returning a "payment temporarily unavailable" response and freeing threads for healthy traffic. The read side (order history) is served by a CQRS read replica projected from events, so browsing stays fast even while writes are degraded.
Trade-offs: when to use, when not — versus alternatives
Saga vs. distributed transaction (2PC): Use a Saga when services span databases and you can tolerate eventual consistency plus writing compensating logic. Its price is that there is no isolation — other transactions can see intermediate states (an order briefly PENDING with money charged but stock unconfirmed). Prefer 2PC only inside a tightly-coupled boundary with a resource manager that supports it; at internet scale 2PC's synchronous locking kills availability, which is why almost no one uses it across microservices.
Orchestration vs. choreography Saga: Choreography (services subscribe to each other's events) is simplest for 2–3 steps and avoids a central component, but with 6+ steps the event web becomes impossible to reason about — no one place tells you "where is order 123 in the flow?" Use orchestration once you need visibility, complex branching, or timeouts; accept that the orchestrator is a new component to build and scale.
Circuit breaker vs. plain retries/timeouts: Timeouts bound a single call; retries help transient blips but amplify load during a real outage. A circuit breaker is the pattern for a sustained dependency failure. Use all three together — timeout + limited retry with backoff + breaker — not one instead of another.
API Gateway vs. direct client-to-service: Use a gateway when you have many clients or many services and need centralized auth, throttling, and aggregation. Skip it (or keep it thin) for a single internal client or a tiny system — a heavy gateway becomes a bottleneck and a single point of failure, and stuffing business logic into it recreates the monolith.
CQRS/Event Sourcing: Use when read and write loads differ wildly or you need an audit log / temporal queries. Do not reach for it by default — it adds eventual consistency between write and read models plus event-versioning pain. For simple CRUD it is pure overhead.
Pitfalls an interviewer probes
- Non-idempotent compensations and retries. Sagas retry steps after crashes, so every step and every compensation must be idempotent (keyed by an idempotency token) or you double-charge a card. Interviewers love asking "what if the orchestrator crashes right after Payment succeeds but before recording it?"
- Compensations that cannot truly undo. You can refund money, but you cannot un-send a shipping email. Real designs need semantic compensation (issue a credit) or a pivot transaction after which the saga can only go forward.
- Circuit breaker tuning. Thresholds too tight cause false trips; too loose and it never protects you. A breaker with no
HALF_OPENprobe stays open forever; a shared breaker across all endpoints trips on one bad route. Also: what does the caller show the user when OPEN? — the fallback matters. - Dual-write / outbox. Writing to a DB and publishing an event are two operations; a crash between them loses the event. The expected answer is the transactional outbox pattern (write event to an outbox table in the same local transaction, relay it via CDC).
- Gateway as a hidden monolith. Piling routing plus business logic plus aggregation into the gateway couples all teams to one deploy — the exact coupling microservices were meant to remove.
Key takeaways
- Every microservices pattern buys back a guarantee the monolith gave for free — name the guarantee (consistency, isolation, availability, simplicity of client contract) and the pattern's purpose is clear.
- Sagas trade ACID isolation for cross-service consistency; the hard parts are idempotency, compensating transactions, and reliable event publishing via the outbox pattern.
- Circuit breakers protect against sustained dependency failure by failing fast; combine them with timeouts and bounded retries rather than choosing one.
- Match the pattern to scale: gateways, CQRS, and orchestration earn their complexity only under many clients, skewed read/write loads, or long multi-step flows — for small systems they are net cost.
A complete microservices pattern map
Real systems do not use four patterns in isolation. The canon is organized by the guarantee each pattern buys back:
- Decomposition: bounded context (split by business capability, not technical layer), strangler fig (incrementally replace a monolith), and anti-corruption layer (isolate a legacy model).
- Communication: API Gateway / BFF (front-door routing, auth, aggregation), synchronous REST/gRPC (immediate request/response), and asynchronous messaging / event streaming (decoupled workflows).
- Data: database-per-service, CQRS (split read/write models), event sourcing (state as an append-only log), saga (cross-service consistency), and transactional outbox (atomic DB write + event).
- Resilience: circuit breaker, bulkhead (isolate failure domains), retry with backoff + jitter, and idempotency keys.
- Observability: distributed tracing with correlation IDs, structured logging, health checks, and SLI/SLO dashboards.
- Configuration: externalized config, secrets management, and dynamic refresh / rotation.
If you can place a problem in one of those buckets, you know which pattern family to reach for.
Decomposition: bounded contexts and Conway’s Law
The most expensive mistake in microservices is drawing boundaries around data layers (frontend service, backend service, database service) instead of business capabilities. The right split follows the problem domain: an e-commerce system decomposes into Order, Payment, Inventory, Shipment, and Customer services. Each owns a bounded context — its own language, invariants, and data.
Conway’s Law says systems mirror the communication structures of the organizations that build them. If the Order and Payment teams cannot ship independently, do not split Order and Payment services — the resulting API will be a chatty, tightly-coupled mess. Use the anti-corruption layer when a new service must consume a legacy model without being poisoned by it: translate the legacy schema at the boundary, then evolve your own model inside.
Sync versus async inter-service communication
Choosing how services talk is a decision about coupling, latency, and failure mode, not about technology fashion.
- Synchronous REST/gRPC is the right default when a caller needs an immediate answer and can tolerate the callee being temporarily down (via timeout + circuit breaker). It keeps read paths simple and predictable.
- Asynchronous messaging / events is the right default when work can be deferred, when multiple services care about the same fact, or when the caller must not block on a downstream that may fail. It decouples availability: the Order service can emit
OrderPlacedeven if the Email service is restarting.
In the checkout scenario above, Order → Payment is synchronous: the user cannot proceed until the charge succeeds or fails. But OrderPlaced → Email / Analytics / Warehouse is asynchronous: those consumers can catch up after a delay, and adding a new consumer does not change the Order service.
| Dimension | Sync REST/gRPC | Async messaging / events |
|---|---|---|
| Latency | Immediate; caller blocks | Higher base latency; eventual |
| Coupling | Tight: caller knows callee | Loose: producer knows topic/event |
| Failure mode | Timeout, retry, circuit breaker | Retry via redelivery, dead-letter queue |
| Best for | Reads, queries, commit/abort decisions | Writes, workflows, fan-out, analytics |
| Risk | Cascading stalls, retry storms | Eventual consistency, ordering complexity |
Healthy architectures mix both: sync on the query path, async on the write side, and an outbox to keep the local DB commit and the outgoing event atomic.
🤖 Don't fully get this? Learn it with Claude
Stuck on Use Cases and Real-world Examples? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **Use Cases and Real-world Examples** (System Design) and want to truly understand it. Explain Use Cases and Real-world Examples from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **Use Cases and Real-world Examples** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **Use Cases and Real-world Examples** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **Use Cases and Real-world Examples** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.