Saga Pattern: 7 Decisions for Recoverable Business Workflows
13 mins read

Saga Pattern: 7 Decisions for Recoverable Business Workflows

The saga pattern coordinates a business process that spans several services by breaking it into local steps and defining what to do when a later step fails. It can help manage an order, booking, or fulfillment journey without pretending that every system shares one database transaction. The hard part is agreeing how the business should recover from partial completion.

This article explains seven decisions behind a useful distributed saga pattern: boundaries, states, coordination, compensation, retries, visibility, and manual recovery. A parts-order example shows why undoing a technical change differs from reversing a real business action.

What a saga does

In a single database transaction, a system can often commit a group of changes together or roll them back. A process that crosses separate services may not have that shared transaction boundary.

A saga uses a sequence of local transactions. Each step commits in its own system. If the overall process cannot finish, the design may run compensating actions for steps that already completed.

Microsoft’s saga design pattern explains this approach to distributed work. Compensation is a new business action, not a magical rewind. A refund, for example, differs from a payment that never happened.

Start by asking whether the process really needs separate services. A simpler transaction or workflow inside one system may meet the need with less operating complexity.

1. Draw the true business boundaries

List the systems involved and the decisions each owns. An order service may own the order, inventory may own reservations, and a payment service may own financial attempts. Keep those responsibilities clear.

Then identify the outcome the customer expects. “Order accepted” can mean different things: details saved, stock reserved, payment authorized, or shipment committed. The saga needs a defined point for each promise.

For example, a parts supplier should not promise dispatch merely because it created an order record. If stock allocation is still pending, the customer-facing state should say so.

Our article on event driven architecture explains why facts and commands need distinct meaning. That distinction helps prevent services from interpreting a local success as proof of an entire journey.

2. Define states that reflect partial completion

A saga can spend time between steps. During that period, some local work may have finished while other work waits. The data and interface should represent those states honestly.

Use states that help staff act, such as stock pending, payment pending, confirmed, cancelling, or needs review. The exact model should follow the business, not a generic list copied from another product.

Also define which actions remain allowed in each state. Can the customer change the address while stock waits? Can support cancel after shipment starts? These rules affect every participating service.

Avoid using one broad failed flag for all problems. A rejected card, a temporary inventory outage, and an incomplete cancellation need different next steps. Clear state makes recovery safer.

3. Choose how the steps coordinate

In orchestration, a coordinator tracks the flow and tells participants what to do next. In choreography, services react to events and continue the process through their agreed interactions. Both approaches need clear contracts and failure rules.

The AWS guidance on saga orchestration describes a central coordination approach. It can make the journey easier to follow, while adding a component whose state and availability need care.

Choreography can suit some loosely connected flows, but the full process can become hard to understand as more services react. A diagram of arrows does not prove that someone owns the overall outcome.

Choose based on the process and team. Ask who can explain the current state, change the sequence, and stop unsafe work during an incident. That answer matters more than a preference for a fashionable architecture.

4. Design compensation in business terms

For every consequential step, ask what can happen if later work fails. A stock reservation may be releasable. A sent email cannot be unsent. Once an item ships, it may need a return process rather than a technical rollback.

Write a compensation policy with timing and authority. A cancelled payment authorization, a refund, and a credit note have different meanings. The finance owner should approve the relevant route rather than let developers treat them as equivalent.

Also identify irreversible or expensive steps. You may choose to place them later in the flow or require stronger checks before they begin. The correct order depends on the business and available APIs.

Compensation can fail too. A refund request may time out, or a stock release may hit an unavailable system. The saga needs to track those attempts and expose unfinished recovery rather than declaring the process neatly reversed.

5. Make repeated and out-of-order work safe

Messages and requests can repeat. A timeout may leave the caller unsure whether a participant completed its step. Each consequential action therefore needs suitable duplicate protection and a stable reference.

Do not assume that the saga coordinator prevents every duplicate on its own. Participants must handle the delivery behavior of their own boundaries. The same principle applies to compensating actions.

Ordering also matters. An old confirmation should not overwrite a newer cancellation state. Use the agreed identifiers, versions, or state checks to reject or review stale work.

Our guide to REST API design explains why retries, conflicts, and explicit outcomes belong in the interface contract. A saga depends on those local contracts to coordinate safely.

6. Give support an end-to-end view

A service can report success while the business process remains unfinished. Support needs a view that links the saga reference with each participant’s attempt and outcome.

Show what the system knows, what it waits for, and who should act. A state such as cancelling should not look final if one participant still holds a resource or financial effect.

Measure time in state as well as error counts. A stalled journey may have no new errors because no worker is making progress. Age-based alerts can reveal that gap.

Keep event times and collection times clear where delayed messages matter. Also protect sensitive data in logs. The support view should expose useful references and outcomes without copying every payment or customer detail.

7. Define manual recovery before launch

Some cases require human judgment. A customer may accept a substitute part, a shipment may have left, or a financial response may remain uncertain. The design should route these cases to an owner with a controlled set of actions.

Avoid a generic “restart saga” button that repeats every step. Recovery should respect what already happened. A safe tool may resume one pending step, reconcile an unknown result, or apply an approved business correction.

Record the actor, reason, and outcome for manual action. Our article on audit logging explains how useful evidence connects actions with accountable decisions.

Also rehearse recovery with the team that will provide support. A runbook that only the original developer understands is not a dependable operating plan.

A parts-order saga example

Imagine a customer orders a replacement component for a service visit. The process creates an order, reserves stock, requests payment authorization, and confirms fulfillment.

The order first enters a pending state. Inventory then reserves the requested quantity under a stable order reference. Payment begins only after the agreed stock result. Each step records its local outcome.

Now payment declines. The saga follows the approved policy to release the reservation and mark the order as not confirmed. If the release fails temporarily, the process shows recovery pending and alerts the owner after the agreed limit.

In another case, the payment call times out after the provider acts. The coordinator does not assume failure and immediately create another attempt. It uses the provider’s supported reference and status path to resolve the unknown outcome.

Next, suppose the customer cancels after a dispatch instruction. The process checks whether the shipment can still stop. If it cannot, the business may need a return or support process. No compensation code can make the parcel never have left.

Finally, support can inspect one journey and see the order, reservation, payment, dispatch, and any recovery attempts. That view makes the system’s current promise clear to staff and the customer.

Manage the period of inconsistency

Because steps commit locally, different services may show different progress for a time. The system should avoid presenting those intermediate states as contradictory final facts.

For example, inventory can show reserved stock while the order still waits for payment. That can be correct. The customer should see a suitable pending state rather than an unconditional confirmation.

Set rules for reads and actions during that window. A report may tolerate a short delay, while a new order decision may require a current authoritative check. Do not rely on eventual consistency as a vague excuse for unclear behavior.

Also define timeouts by business effect. A brief payment wait and a long supplier confirmation have different acceptable durations. The coordinator or event flow needs a policy for each.

Test the flow at every meaningful boundary

A clean full run proves only one path. Stop or delay each participant in a safe test setup and observe the result. Test both forward work and compensation.

Include these cases:

  • A repeated command arrives after a participant completes it.
  • A response disappears after a local commit.
  • A cancellation arrives while another step runs.
  • A compensating action fails and later resumes.
  • An older message arrives after a newer state.
  • Support must reconcile an uncertain external result.

Then inspect business totals and references after recovery. A healthy coordinator status does not prove that stock and financial records agree. Use reconciliation suited to the workflow.

Know when a simpler design is enough

A saga adds state, coordination, contracts, and recovery work. It can suit a real distributed transaction problem, but it is not required for every background task.

If one service owns all relevant data and can complete the work safely in one transaction, that may be simpler. If a later action is merely a noncritical notification, a separate background job may suffice.

Compare the value of independent services with the cost of managing partial completion. Make that tradeoff visible to the process owner, not only the architecture team.

Frequently Asked Questions

Is a saga a distributed database rollback?

No. Each step commits locally. Compensation performs a new business action, which may not erase every effect of the earlier step.

Should we use orchestration or choreography?

Choose based on flow complexity, ownership, and support needs. Both require clear contracts and a way to understand the overall outcome.

Can compensation always finish automatically?

No. It can fail or require judgment. Provide a visible pending state and a controlled manual recovery route.

Does every microservice process need a saga?

No. Use it for a genuine multi-step consistency problem. Simpler transactions or background work may meet other needs with less complexity.

Design recovery as part of the process

The saga pattern works when each step, intermediate state, and compensating action has clear business meaning. Start with one journey, prove uncertain outcomes and recovery, and give support an end-to-end view. Expand only when the team can explain what the system has promised and what remains unfinished.

Build software around how your business works

Plan multi-system workflows with clear states, safe retries, and practical recovery tools.

Talk to our team

Leave a Reply

Your email address will not be published. Required fields are marked *