About Us Contact Us Write for Us Advertise
Home > System Design > Fundamentals > Distributed Transactions & the Saga Pattern (Explained)
System Design › Fundamentals

Distributed Transactions & the Saga Pattern (Explained)

How do you keep an operation all-or-nothing across many microservices when each has its own database? The Saga pattern, compensating transactions, orchestration vs choreography, and the outbox pattern — fresher to senior.

Shiv Pandey
Shiv Pandey
Oct 01, 2026 | 81 views
Distributed Transactions & the Saga Pattern (Explained)

🔵 Fundamentals · Fresher-friendly → 🔴 goes deep

In a single database, a transaction is a superpower: several changes either all happen or none do, thanks to ACID. But the moment your data lives across several microservices — each with its own database — that superpower vanishes. How do you keep "charge the card, reserve the item, create the order" all-or-nothing when three different services own three different databases? That's the distributed transaction problem, and the Saga pattern is the answer interviewers want to hear. Let's build it up.

The one idea to hold onto: you can't have one big transaction across many services, so a Saga breaks the work into a sequence of local transactions — and if any step fails, it runs compensating transactions to undo the earlier ones. Not a rollback, but a deliberate "undo" step.

Why not just use one transaction?

Because there's no shared database to wrap them in. Each service — Payments, Inventory, Orders — has its own DB. A classic fix from the past is two-phase commit (2PC): a coordinator asks every service "are you ready?", and only if all say yes does it tell them all to commit.

🟠 2PC gives you correctness, but at a steep price: every participant must lock its resources and wait for the coordinator. If the coordinator or any node is slow or crashes mid-way, everyone stays locked — the whole thing stalls. It's synchronous, blocking, and fragile at scale, which is why modern distributed systems mostly avoid it. We trade 2PC's strict guarantee for something more available.

The Saga idea

A Saga replaces one big transaction with a chain of small local ones, each in a single service. Every step has a matching compensating action that undoes it. If step 3 fails, you run the compensators for steps 2 and 1 — walking backwards to clean up.

Happy path (all steps succeed) → 1. Create Order (pending) 2. Charge Card 3. Reserve Item 4. Confirm Step 3 fails → compensate backwards ← 3. Reserve FAILS (out of stock) ↩ Refund Card ↩ Cancel Order compensating transactions = deliberate "undo" for each completed step

Note the compensators aren't a database rollback — the local transactions already committed. They're new actions that reverse the effect: refund the charge, release the reservation, cancel the order. You design each one deliberately.

Two ways to coordinate a Saga

🟠 There are two styles, and knowing both by name is a strong signal:

Style How it works Trade-off
Choreography Each service emits an event; the next service listens and reacts. No central boss. Decoupled & simple for short flows; hard to follow & debug as steps grow
Orchestration A central orchestrator tells each service what to do next and triggers compensation on failure. Clear, easy to monitor; the orchestrator is one more thing to build & keep available

Rule of thumb: choreography for simple, few-step flows where you want loose coupling; orchestration once the flow is long or complex and you need one place to see and control it. Most large systems lean orchestration for anything non-trivial because "where is this order stuck?" becomes answerable.

The trade-off you must name: no isolation

🔴 Sagas buy availability by giving up the I in ACID — isolation. Because each step commits immediately, other transactions can see intermediate states. Between "charge card" and "reserve item," the money is gone but the item isn't yours yet. The system is only eventually consistent (see consistency models): correct once the Saga finishes, but briefly inconsistent in the middle.

That's the deal Sagas make: atomicity via compensation, but no isolation. You design around it — e.g. an order sits in a "pending" state until confirmed, so nobody treats it as final too early. Saying this out loud in an interview shows you understand the cost, not just the mechanism.

Two patterns that make Sagas safe

🔴 Two supporting patterns almost always come up with Sagas:

  • Idempotency: messages can be delivered more than once (at-least-once delivery), so every step and every compensator must be safe to run twice. "Refund payment 900" should refund once even if the message arrives twice — check "did I already do this?" first.
  • The outbox pattern: a service must update its DB and publish an event — but those are two systems, so one can succeed and the other fail. The fix: write the event into an outbox table in the same local transaction as the data change, then a separate process reads the outbox and publishes. Now the DB write and the "intent to publish" are atomic. This closes the nastiest gap in event-driven Sagas.

When to use a Saga (and when not to)

Reach for a Saga when a business process spans multiple services and must be all-or-nothing-ish — order checkout, travel booking (flight + hotel + car), account signup with several downstream setups. Don't reach for one if all the data lives in a single service/database — then a plain local ACID transaction is simpler and strictly better. Distributed transactions are a cost you take on only because the data is genuinely distributed.

The interview-ready summary

"You can't wrap a transaction around multiple microservices, and 2PC blocks and doesn't scale, so I'd use a Saga: a sequence of local transactions, each with a compensating transaction to undo it on failure. I'd coordinate it with an orchestrator for a complex flow, make every step idempotent because messages can repeat, and use the outbox pattern to publish events reliably. The trade-off is losing isolation — the system is eventually consistent — so I'd keep the order in a pending state until the Saga completes." That answer hits every beat a senior interviewer is listening for.

What to read next

← Rate limiting · Capacity estimation →

Related Articles

Resilience: Timeouts, Retries & Circuit Breakers Explained
System Design › Fundamentals

Resilience: Timeouts, Retries & Circuit Breakers Explained

Bloom Filters Explained: The Space-Saving Magic Trick
System Design › Fundamentals

Bloom Filters Explained: The Space-Saving Magic Trick

ACID & Database Isolation Levels Explained (with Anomalies)
System Design › Fundamentals

ACID & Database Isolation Levels Explained (with Anomalies)

CDN Explained: How Content Delivery Networks Make Sites Fast
System Design › Fundamentals

CDN Explained: How Content Delivery Networks Make Sites Fast