Saga pattern: 7 decisions for recoverable business transactions
The saga pattern coordinates a business process across several local transactions when one shared database transaction is unsuitable or unavailable. Each step commits its own work, and later failures can trigger defined recovery actions. A distributed transaction saga needs explicit states and business rules because it cannot simply erase every earlier effect.
This article explains seven design decisions before adopting a saga. It covers step boundaries, compensation, orchestration, durable state, retries, concurrency, and manual recovery. An order workflow involving stock, payment, and fulfillment provides a practical example.
Why a saga is different from one database transaction
A local database transaction can provide atomicity within its supported scope. A process spanning independent services may not share that boundary. Once one service commits, another service’s failure does not automatically undo it.
Microsoft’s saga pattern guidance explains coordination through local transactions and compensating actions. The approach can support recovery, but it introduces state, timing, and consistency work that the team must operate.
First, ask whether the process needs to be distributed this way. A simpler service boundary or local transaction may solve the problem with less complexity. Sagas should address a real coordination need rather than serve as a default architecture label.
For the order example, the business may reserve stock, authorize a payment, and request fulfillment. Each action has its own state and limits. A failure after one step needs a defined response that the business can explain.
1. Define local steps and their business states
Choose steps that represent meaningful committed outcomes. “Call inventory” is too vague. A useful step might establish that stock has been reserved for one order under a defined reference.
For each step, state its input, success condition, failure conditions, and resulting state. Also define how the coordinator or other participants learn the outcome. A transport acknowledgment may not mean the business step completed.
Next, distinguish rejected, pending, timed out, and unknown results. A payment rejection differs from a connection loss after the payment service may have accepted the request. Recovery cannot treat both as the same failure.
Keep a shared vocabulary with business owners. Operators should understand whether an order is awaiting stock, awaiting confirmation, or held for review. A generic “processing” status can hide important work.
2. Design compensation as business action
A compensation is a defined action that addresses an earlier committed step. Releasing a reservation or requesting a supported refund can be examples. It is not necessarily a reversal that restores the world exactly as it was.
For instance, an external message cannot be unsent, and a shipped item cannot return through a database rollback. Some effects need a new business process, while others may not be reversible at all.
Define compensation ownership, preconditions, and failure handling. A compensation can fail too. The saga needs to record that state and route the case rather than claim the original process has cleanly disappeared.
Also identify any commitment after which the process should normally move forward to resolution. The business may prefer completing a supported next step instead of trying to reverse every earlier action. That choice must be explicit.
3. Choose orchestration or choreography deliberately
An orchestrated saga uses a coordinator to direct steps and track progress. A choreographed saga lets participants react to events according to their own rules. Both can work, and both need clear ownership and evidence.
AWS’s saga orchestration guidance describes a coordinator-based approach. Evaluate it against the complexity and operating needs of your own workflow rather than choose solely by diagram style.
Orchestration can make the sequence and recovery path easier to inspect, but the coordinator becomes an important service to operate. Choreography can keep reactions with participants, while making the full process harder to follow if events and dependencies grow.
Oxen’s article on recoverable business workflows explains choices around long-running coordination. Keep the process definition and its runtime responsibilities clear regardless of the product used.
4. Persist progress and deadlines
A saga may outlive one request or process instance. Store enough state to resume after a restart and explain what already happened. Memory alone is not a dependable record for a multi-step business transaction.
Record the saga identity, current state, completed steps, pending work, references, and relevant deadlines. Preserve the version of the workflow rules where needed. A later deployment should not silently reinterpret an old in-flight case.
Next, define timeout behavior for each wait. A timeout can mean stop waiting, inspect current state, retry, compensate, or ask for review. The right action depends on the step’s business semantics.
Test a coordinator restart at several points. It should recover its place without repeating completed effects or forgetting a pending compensation. The record should support both runtime recovery and human investigation.
5. Make messages and actions safe to repeat
Distributed work can involve repeated messages and uncertain request outcomes. Each participant needs a suitable way to recognize the same business operation and avoid unintended repeated effects.
Use stable operation identities and supported idempotent behavior where appropriate. A retry of the same reservation should not reserve another quantity simply because it arrived through a new connection.
Oxen’s guide to a message queue explains delivery and background-work choices. A queue can help move messages, but its delivery semantics do not automatically make every downstream action safe.
Also address the gap between committing local data and publishing an event. Use a supported design that preserves the required relationship, such as an appropriate transactional messaging or outbox approach. Test the actual failure window rather than assume two separate writes always occur together.
6. Handle concurrent changes and partial visibility
Other work can change an account, item, or order while the saga runs. Define how the process detects conflicts and which operations remain valid. A reservation can expire, and a customer can request cancellation at an awkward point.
Do not assume global isolation across independent steps. The application may need versions, reservations, or other domain controls. Choose them around the business invariant rather than copy one generic locking strategy.
Oxen’s article on enterprise integration patterns explains boundaries between systems. Each participant should own its local rules while the wider process coordinates their outcomes.
Next, define what users see during partial completion. A temporary state should be honest about what remains. Avoid showing an order as fully complete when payment or fulfillment is still uncertain.
7. Give manual recovery a controlled route
Some cases will need human judgement or a correction outside the automatic path. Build an exception view with the current state, completed effects, uncertain steps, and available authorized actions.
Operators should not repair a saga by editing several databases from memory. A supported recovery action should validate the current state, record authority, and preserve history. Some actions may need another role’s review.
Oxen’s guide to audit logging explains meaningful action records. Connect automated and manual decisions to the same business case so the final result remains traceable.
Finally, keep compensation failures and overdue cases visible. A saga should reach a known terminal outcome or an explicitly owned exception. Leaving it in an unexplained pending state is not recovery.
A practical order saga
Begin with an accepted source order. The process requests a stock reservation, confirms the relevant payment step, and asks fulfillment to proceed under the business rules. Each participant returns a durable reference and state.
If stock is unavailable, the process can reject or hold the order before later commitments. If a later step fails, it follows the defined compensation or forward-recovery route. The exact choice belongs to the business and supported service behavior.
Next, test an unknown payment outcome. The process should inspect or reconcile the existing operation before creating another. A timeout alone should not establish that no payment action occurred.
Then test a failed reservation release. The saga should preserve the outstanding action and route it to an owner. It should not report a clean cancellation while stock remains held.
Testing the failure matrix
List failures before, during, and after each consequential step. Include duplicate messages, delayed responses, service restarts, expired reservations, and workflow-version changes. Use a suitable controlled environment and safe test dependencies.
For every case, define the expected final state and evidence. The test should verify business records across participants, not only the coordinator’s status.
Also test out-of-order information. A delayed success message can arrive after the coordinator started recovery. The workflow needs a rule for interpreting that event in its current state.
Finally, ask a colleague to recover a deliberately stuck case through the operator route. Their questions reveal missing context and unsafe assumptions in the runbook.
Observability and reconciliation
Use a common correlation reference across steps, messages, and participant records. Operators should follow one case without searching unrelated logs. Show queue age, pending deadlines, compensation status, and unresolved outcomes.
Reconcile the coordinator’s view with participant facts where appropriate. A local transaction may succeed while its outcome message is delayed or lost. The process needs a way to discover and resolve that gap.
Keep technical errors and business rejections distinct. They have different owners and recovery paths. A valid business rejection should not trigger endless infrastructure retries.
When a saga may be unnecessary
A single service with one local transaction can be simpler when it fits the business boundary. Splitting that transaction into several services can create coordination work without a corresponding benefit.
Some workflows also need stronger immediate consistency than the proposed saga can provide. Review the invariant and architecture with the responsible technical and business owners rather than assume compensation makes every split acceptable.
Start with the simplest design that meets the requirement. A saga is a tool for managing a distributed reality, not a reason to create one.
Operating and maintenance costs
Include durable state, messaging, timers, retries, observability, reconciliation, operator tools, and workflow migration in the estimate. These are core parts of the design rather than optional polish.
Assign owners for each participant and the end-to-end process. A team that owns only the coordinator cannot fix every domain rejection. Clear routes make recovery faster and reduce repeated investigation.
Keep completed and canceled cases under suitable retention rules. Historical evidence can help explain an order, while sensitive payloads should not remain broadly available without a purpose.
Plan for a new workflow version while old cases remain open
A saga can outlive the deployment that started it. An order may wait for approval while the team releases a new payment step or changes its compensation policy. The coordinator must still interpret the saved state and outstanding messages of that older case.
Choose a supported versioning strategy for the workflow technology. Options can include retaining the old path for existing instances or using an explicit, tested migration. Avoid silently reinterpreting saved progress under a new sequence of steps.
Test deployment with cases paused at meaningful boundaries, including before an irreversible action and during compensation. Then verify deadlines, operation references, and operator views after the update. Business owners should approve any migration that changes what a customer has already authorized. This keeps routine delivery from becoming an unplanned recovery event.
Frequently Asked Questions
Does a saga roll back all earlier work?
No. It uses defined compensating or forward-recovery actions. Some effects are irreversible or require a separate business process.
Is choreography always more scalable than orchestration?
No universal choice applies. Compare coordination, visibility, ownership, and operating complexity for the actual workflow.
Why does a saga need idempotent actions?
Retries and repeated messages can occur. Participants need to recognize the same operation so recovery does not create unintended duplicate effects.
Make partial completion understandable and recoverable
The saga pattern needs clear local steps, business recovery rules, durable state, and safe repeated actions. Test the failure paths and operator workflow before a broad rollout. The goal is a process the team can explain even when it stops halfway through.