Concept of the day · architecture

Saga Pattern

No distributed transaction: a sequence of local commits, each with a compensating undo.

The mechanism. A business operation that spans services cannot hold one ACID transaction across them. A saga splits it into local transactions, each committing in its own service, plus a compensating transaction for each step that semantically undoes it. If step four fails, you run the compensations for three, two and one, in reverse.

Two shapes. Orchestration: a coordinator tells each service what to do and tracks progress. Easy to reason about and to observe, but the coordinator is a dependency and can become a god object. Choreography: each service listens for events and reacts. No central component, but the flow exists only as an emergent property of the subscriptions, which makes "what happens when a user checks out" unanswerable without reading six repos.

What you give up. Atomicity across the whole operation, and isolation entirely. Intermediate states are visible: an order exists while payment is still pending. You must design for that — status fields, semantic locks, a "pending" state the UI understands. Compensations are semantic, not rollbacks: you cannot un-send an email, so you send an apology; you cannot un-ship a parcel, so you issue a return label.

Why it is harder than it looks. Compensations can fail, so they must be retryable and therefore idempotent. Steps can be interleaved with other sagas on the same data. And the "can this compensation always succeed?" question has an uncomfortable answer for anything involving the physical world or a third party.

Takeaway. Reach for a saga when a single transaction truly cannot span the boundary. If the services share a database, use a transaction. Most sagas in the wild are a distributed solution to a self-inflicted distribution problem.

← edition of 2026-10-30cheatsheet