Patterns
advancedorchestration Featured

Saga with Compensating Transactions

Coordinate a multi-step business transaction that spans several systems by pairing each step with a rollback action. If a later step fails, run the rollbacks in reverse to restore a consistent state.

Views48
BPMN 2.0
On this page

Visual Flow

Rendering diagram…

When to Use This Pattern

Use a saga when a business action spans two or more systems that can't share a database transaction:

  • Order placement touching inventory, payments, and shipping
  • User provisioning across auth, billing, and CRM
  • Contract creation updating CLM, finance, and access control
  • Any "all-or-nothing" flow where you can't hold a distributed lock for minutes

Sagas are the pragmatic alternative to two-phase commit. You give up strict atomicity in exchange for resilience and you explicitly design the "undo" path instead of pretending failures can't happen.

Important

A saga doesn't give you rollback — it gives you semantic compensation. If step 2 fires a welcome email, the compensating step has to acknowledge the email was sent. You can't unsend an email.

How It Works

Break the transaction into an ordered list of local transactions. For each forward step, write a paired compensating step that semantically reverses it. If step N fails, execute compensations for steps N-1, N-2, … 1 in reverse.

Two orchestration flavours exist:

  • Orchestrated — a central workflow engine calls each step and knows the compensation map. Easier to reason about and observe. The common choice.
  • Choreographed — each service listens for events and decides what to do. Scales better but debugging is brutal.

Start orchestrated. Move to choreographed only when you've outgrown a single workflow engine.

Implementation Guide

Step 1: Define every pair

For each step, write down what it does and what the reverse looks like. If you can't describe the reverse, you don't have a saga — you have wishful thinking.

StepForward actionCompensation
1Reserve 3 units of SKU-42Release 3 units of SKU-42
2Charge card $120Refund $120 to original card
3Create shipment recordVoid shipment + notify warehouse
Step 2: Persist saga state durably

Every step transition must be written to durable storage before the next step runs. On process crash, the saga has to be resumable. Lose state once and you'll be reconciling manually.

Step 3: Make compensations idempotent

Compensations will retry. A refund that runs twice must not refund twice. Use idempotency keys (refund:order-12345) and let downstream systems dedupe.

Step 4: Handle partial compensation failures

What happens when the refund step itself fails? You need a dead-letter path that pages a human. Some compensations will always require manual intervention — accept it and design for it.

Step 5: Observe the full lifecycle

Emit events at each forward step and each compensation. Build a dashboard that shows sagas in flight, sagas rolled back, and sagas stuck awaiting manual fix. The stuck bucket is where operational reality lives.

Tips & Best Practices

  • Design compensations first. If you can't write the rollback, rethink the step.
  • Keep steps coarse-grained. Ten steps is a lot; five is better. Each step is a failure surface.
  • Compensation is not rollback. Document the observable side effects that can't be undone.
  • Version your saga definitions. In-flight sagas need to complete on the version they started with.

Related patterns