HT
How Things Work
System Online

Saga Pattern

Managing transactions that span services without distributed locks — local transactions chained together, compensating transactions for rollback, and orchestration vs choreography.

How It Works

A saga maintains data consistency across multiple services without a distributed ACID transaction. It runs a sequence of local transactions, one per service; if any step fails, it executes compensating transactions to undo the completed ones. It trades atomic, immediate consistency for availability and eventual consistency — the realistic way to coordinate business operations across service boundaries.

1
Why Distributed Transactions Are Hard

A single business operation (place an order) often spans multiple services, each with its own database. You can't wrap them in one ACID transaction across service boundaries, and two-phase commit (2PC) is slow, locks resources, and couples services. The saga pattern provides consistency without a distributed transaction.

2
A Sequence of Local Transactions

A saga breaks the operation into a series of local transactions, one per service. Each step commits in its own database and then triggers the next. There's no global lock — each service does its part and moves on, so the system stays available.

3
Compensating Transactions

If a step fails partway through, there's no automatic rollback — instead the saga runs compensating transactions to semantically undo the already-committed steps, in reverse order (refund the payment, release the inventory). The system reaches a consistent end state through forward steps or compensations.

4
Orchestration vs Choreography

Orchestration uses a central coordinator that explicitly tells each service what to do and handles compensation — easy to follow and monitor, but a central component. Choreography has services react to each other's events with no coordinator — loosely coupled, but the overall flow is implicit and harder to trace.

Key Concepts

ðŸ“ŋSaga

A sequence of local transactions across services that achieves consistency without a distributed ACID transaction.

âŸēCompensating Transaction

An action that semantically undoes a completed step (refund, release) when a later step fails.

🎞Orchestration

A central coordinator drives the steps and compensations — explicit, observable, but a focal component.

💃Choreography

Services react to each other's events with no coordinator — decoupled, but the flow is implicit.

🔄Eventual Consistency

The system converges to a consistent state through steps or compensations, not instant atomic rollback.

â†ĐïļSemantic Rollback

Undoing the business effect (not a DB rollback) — and noting some actions can't be perfectly undone.

Orchestration vs choreography
tsx
1// ORCHESTRATION — a central coordinator drives the steps and compensations.
2class PlaceOrderSaga {
3 async execute(order) {
4 const done = [];
5 try {
6 await payments.reserve(order); done.push("payment");
7 await inventory.reserve(order); done.push("inventory");
8 await shipping.arrange(order); done.push("shipping");
9 return "committed";
10 } catch (err) {
11 // failure → undo completed steps in REVERSE order
12 if (done.includes("inventory")) await inventory.release(order);
13 if (done.includes("payment")) await payments.refund(order);
14 return "rolled back"; // eventual consistency, not ACID
15 }
16 }
17}
18
19// CHOREOGRAPHY — no coordinator; services react to each other's events.
20// payments --OrderPaid--> inventory
21// inventory --StockReserved--> shipping
22// shipping --ShipFailed--> inventory(release) --> payments(refund)
ðŸ’Ą
Why This Matters

Cross-service transactions are unavoidable in microservices, and sagas are the standard answer — making them a frequent senior design-interview topic. Understanding compensations, the orchestration/choreography trade-off, and the reality of eventual consistency is essential for building correct distributed workflows that don't leave money charged for orders that never ship.

Common Pitfalls

⚠Forgetting compensations can also fail — they need retries, idempotency, and sometimes manual intervention.
⚠Assuming everything is reversible — some actions (a sent email, a shipped package) can't be perfectly undone.
⚠Pure choreography for complex flows — the implicit, emergent workflow becomes impossible to trace or monitor.
⚠Non-idempotent steps/compensations — at-least-once messaging means they may run more than once.
⚠Ignoring intermediate inconsistency — other reads may see a half-done saga; design the UX for it.
Real-World Use Cases

1Placing an Order Across Payment, Inventory & Shipping

Scenario

Checkout must charge the card (Payments), reserve stock (Inventory), and book a courier (Shipping) — three separate services and databases. If shipping can't be arranged, the charge and stock reservation must not stand.

Problem

There's no single transaction spanning the three services, so a failure at shipping would otherwise leave the customer charged and stock wrongly reserved — partial, inconsistent state.

Solution

Model it as a saga. Each service commits its local step in order; if shipping fails, the saga runs compensations in reverse — release the inventory, then refund the payment. The order ends either fully placed or fully unwound, with no partial state.

ðŸ’Ą

Takeaway: When one operation spans multiple services, a saga gives you all-or-nothing semantics through compensating transactions instead of an impossible distributed ACID transaction — accepting eventual consistency along the way.

2Choosing Orchestration Over Choreography as It Grew

Scenario

A saga implemented with pure choreography (services reacting to events) grew to 8 steps. Nobody could answer 'what's the current state of this order?' or trace why a saga got stuck, and circular event chains crept in.

Problem

With choreography, the workflow is implicit in scattered event handlers. As steps multiplied, the emergent flow became impossible to reason about, monitor, or debug, and compensation logic was spread everywhere.

Solution

Introduce an orchestrator (e.g. a state machine / workflow engine) that owns the saga: it issues each command, tracks the current step, and triggers compensations centrally. The flow becomes explicit, observable, and testable, while services stay simple command handlers.

ðŸ’Ą

Takeaway: Choreography suits simple, few-step flows with maximum decoupling; orchestration shines as workflows grow complex, giving you one place to see state, handle compensation, and debug. Pick per saga, not globally.

Join the discussion

Comments

?

Loading comments...

Cookie preferences

We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.