Saga Pattern
Concept
A monolith with one database gets atomicity for free: "create order, charge card, reserve stock" is one local ACID transaction, and the database either commits all of it or none of it. Decompose that monolith into microservices — Order, Payment, Inventory, each owning its own database — and that single transaction disappears. There is no longer one database that can lock all three tables and roll them back together, and reaching for two-phase commit across services is generally avoided in microservices architectures because a blocking coordinator that holds locks across independently-owned, independently-scaled services is exactly the kind of tight coupling and availability risk microservices are meant to eliminate.
The saga pattern is the answer: a business operation that spans multiple services is executed as a sequence of local transactions, one per service, each committing independently. If every step succeeds, the saga completes normally. If a step fails partway through, the saga does not roll back in the database sense — instead it runs compensating transactions, one per already-completed step, in reverse order, to semantically undo the business effect of what was already committed (e.g., "reserve stock" is undone by "release stock," not by a database rollback, since that data may already be visible to other transactions and other services).
There are two ways to coordinate the steps:
- Choreography — each service publishes a domain event when its local transaction commits, and the next service(s) subscribe to that event and react by running their own local transaction, publishing their own event in turn. There is no central coordinator; the flow emerges from a chain of event producers and consumers.
- Orchestration — a single orchestrator (a saga execution coordinator) explicitly tells each participant which local transaction to run next, tracks the saga's state, and — on failure — explicitly invokes the compensating transactions for every step already completed, in reverse order.
Tradeoffs
| Aspect | Choreography | Orchestration |
|---|---|---|
| Coupling | Loose — services only know the events they emit/consume, not each other | Tighter — the orchestrator has explicit knowledge of every participant and step order |
| Visibility / traceability | Hard to see the overall flow — it's implicit in a chain of pub/sub subscriptions across services | Centralized state machine — the current step and saga status are visible in one place |
| Complexity distribution | Spread thin across every participating service (and prone to cyclic event dependencies as steps grow) | Concentrated in one orchestrator, which can itself grow into a fragile "god service" if not scoped carefully |
| Best fit | Few participants, a simple linear or lightly-branching flow | Longer, multi-branch workflows needing retries, timeouts, and a clear audit trail |
The broader tradeoff sagas make, regardless of coordination style: giving up the I (isolation) and the all-or-nothing atomicity guarantee of ACID, in exchange for keeping each service's database autonomous and each service independently available. A saga gives you eventual, not immediate, consistency — the system passes through intermediate states (order created, payment pending, inventory not yet reserved) that are visible before the saga finishes, which is the direct price of not holding cross-service locks.
When to use / when not to
- Use it once a monolith has genuinely been decomposed (strangler-fig style or otherwise) into services that each own their own datastore, and a single business operation now needs to touch more than one of them.
- Prefer choreography for a small number of participants and a simple flow, where the coupling savings matter more than centralized visibility.
- Prefer orchestration once the workflow has enough steps, branches, or timeout/retry logic that "what state is this saga in right now" becomes a real operational question you need to answer quickly.
- Don't reach for a saga if the operation can stay inside one service and one database — a local ACID transaction is strictly simpler and gives strictly stronger guarantees; sagas only earn their complexity when the data genuinely lives in more than one place.
- Don't use it where the business can't tolerate the intermediate, not-yet-consistent states a saga necessarily passes through — sagas trade strict isolation for availability, and some domains (e.g., anything requiring a hard real-time consistent balance check) need a different answer.
- Be cautious when a step's real-world side effect isn't actually reversible — the compensation is then a corrective business action standing in for a rollback, not a true undo (see Common pitfall).
Common pitfall
Treating a compensating transaction as if it were a database ROLLBACK — restoring the system to exactly the state before the failed step ran. It isn't, and it doesn't. A compensating transaction is a forward-moving business operation in its own right: "release the reserved inventory," "refund the charge," "cancel the shipment" are new transactions that logically reverse the business effect, not an erasure of the fact that the original step happened. Some steps have no clean compensation at all — a package that's already physically shipped can't be un-shipped; the "compensation" is a returns/refund workflow, a materially different and slower process than the original step.
Compounding this: sagas provide no isolation between concurrent transactions touching the same data. Because each local transaction commits independently and immediately, another saga (or an unrelated read) can observe or act on a saga's in-progress, not-yet-fully-consistent state — AWS's guidance calls this out explicitly as an anti-pattern to design around, recommending an application-level semantic lock (a status field like ORDER_PENDING) to signal "this record is mid-saga, don't act on it as final" until the saga either completes or compensates.
Principal Engineer Lens
Saga is the consistency bill that comes due the moment a single-database monolith gets decomposed into services that each own their own store — it's the direct sequel to a strangler-fig migration, not a competing pattern. The Principal-level judgment call isn't "we use sagas here," it's first confirming the operation actually needs cross-service consistency at all — a lot of "we need a saga" cases are really "we need eventual consistency plus honest UX for the pending state" (show "Payment Processing," don't force synchronous cross-service locking to fake instant consistency). Once a saga is genuinely warranted, the second judgment call is choreography vs. orchestration, made deliberately on participant count and audit/observability needs — not defaulted to whichever a past team happened to reach for.
This shows up constantly in Fintech and Capital Markets specifically: payment authorization → ledger posting → settlement, or trade execution → clearing → custody update, are textbook multi-service business transactions where synchronous cross-service locking (2PC-style) is both an availability and a scalability non-starter under real SLAs — sagas with compensations are usually the only workable answer. The strong architecture-review answer names exactly which steps are compensable, which aren't (and what corrective business action substitutes for a rollback on those), and how the isolation gap is guarded (semantic locks, optimistic versioning) for state observed mid-saga. It's also worth surfacing explicitly that every step and every compensation must be safe to retry over an unreliable network — which is why sagas and idempotency keys are twin patterns that show up together in the same systems, not two unrelated concepts.
Reel Script
Setup: In a monolith, "place an order" is one database transaction — it either all happens or none of it does. Split Order, Payment, and Inventory into separate services with separate databases, and that guarantee is gone. So what actually happens when the payment succeeds but the warehouse is out of stock?
Concept walkthrough: Walk through the mechanics: each service runs its own local transaction and commits independently — no cross-service lock, no 2PC coordinator. If a later step fails, the saga runs compensating transactions for every step that already committed, in reverse order, to undo the business effect (not the database rows). Then contrast the two ways to wire the steps together: choreography, where each service just reacts to the previous service's event with no one in charge, versus orchestration, where a central coordinator explicitly calls each step and explicitly triggers the compensations on failure.
Real example tie-in: Trace the order flow end to end — Order service creates the order as pending, Payment service charges the card, Inventory service tries to reserve stock and fails. The saga now has to compensate backward: refund the charge, mark the order cancelled. Note that "refund" is a new transaction, not a rewind — the charge genuinely happened, and the refund is a separate, auditable, forward-moving operation.
Tradeoffs & alternatives: Contrast against the road not taken — a distributed transaction with a two-phase commit protocol holding locks across all three services until every participant agrees to commit — and why that's usually rejected in microservices: it blocks, it couples services' availability together, and it doesn't scale. Then contrast choreography against orchestration directly: fewer moving parts and less coupling with choreography, versus a clear, centrally-visible state machine with orchestration once the flow gets complex enough that "what state is this business transaction in right now" is a question someone will actually ask during an incident.
Principal Engineer takeaway: The senior answer in a design review isn't "we'll use a saga" as a buzzword — it's naming which specific steps have a real compensation and which don't (and what corrective action covers the ones that don't), which coordination style fits the actual number of participants, and how you're closing the isolation gap sagas open up by default. That level of specificity is what separates having heard of the pattern from having actually designed one.
Related
- Architecture Index
- Strangler Fig Pattern — strangler fig is how you get from a monolith to decomposed services in the first place; saga is what you need once that decomposition has broken apart the single local transaction that monolith used to give you for free. One is the migration path, the other is the consistency mechanism you need on the far side of it.
- Stripe Idempotency Keys — sagas and idempotency keys are twin distributed-transaction concerns: a saga's steps and compensating transactions run over the same unreliable networks idempotency keys exist to protect, and both must be safe to retry without double-applying a side effect.
Sources: