Saga Pattern
Mental model
A Saga is a sequence of local transactions across services, connected by events or commands. If a later step fails, previously completed work may need a compensating action.
E-Commerce Example
flowchart TD A["Order Created"] --> B["Reserve Inventory"] B --> C{"Reserved?"} C -->|Yes| D["Process Payment"] C -->|No| X["Cancel Order"] D --> E{"Payment OK?"} E -->|Yes| F["Create Shipment"] E -->|No| G["Release Inventory"] G --> X F --> H["Order Completed"]
Why Do We Need It?
Suppose:
Order DB ✓
Inventory ✓
Payment ✗We cannot simply roll back the PostgreSQL transaction in Order Service because the inventory operation happened in another service/database.
Instead:
Reserve Inventory
↓
Payment fails
↓
Release Inventory
↓
Cancel OrderReleaseInventory is a compensating action.
Saga Is NOT a Distributed Transaction
This distinction matters.
| Saga | 2PC |
|---|---|
| Application-level workflow | Distributed transaction protocol |
| Local transactions | Coordinated transaction |
| Compensation | Rollback/prepare protocol |
| Eventual consistency is common | Stronger atomicity goal |
| Services remain autonomous | Tighter coordination |
| Usually more operationally resilient | Can involve blocking/coordination overhead |
Important
A compensating transaction is not the same thing as a database rollback.
If an email was sent, for example, you cannot literally “roll back” the email. You need a new compensating action if one exists.
Orchestration vs Choreography
Orchestration
One component coordinates the workflow:
Saga Orchestrator
│
├── Reserve Inventory
├── Process Payment
└── Create ShipmentAdvantages:
- centralized workflow
- easier to understand
- easier to monitor
- explicit failure handling
Choreography
Services react to events:
OrderCreated
↓
InventoryReserved
↓
PaymentCompleted
↓
ShipmentCreatedAdvantages:
- fewer central orchestration components
- services react independently
But complex workflows can become difficult to understand.
Project choice
For this learning project, start with orchestration because it makes the workflow and failure handling easier to reason about.
Idempotency Is Required
Imagine:
ReserveInventory
↓
Inventory reserves item
↓
response lost
↓
Saga retries
↓
ReserveInventory AGAINInventory must safely handle the duplicate.
So Saga depends heavily on:
Outbox + Saga
These patterns work together:
Local transaction
│
├── business state
└── outbox event
│
▼
Kafka
│
▼
Next Saga stepRemember
Outbox makes events reliable. Saga coordinates business workflow. Idempotency makes retries safe.
Interview Questions
Why not use 2PC everywhere?
2PC provides distributed transaction coordination but introduces coupling and coordination overhead. Many microservice architectures prefer local transactions plus asynchronous workflows and compensation.
What happens if compensation fails?
The Saga must persist its state and retry/alert the failed compensation. In a production system, this becomes an operational concern requiring monitoring, retry policies, and possibly manual intervention.
Does Saga guarantee consistency?
It provides a way to coordinate toward a consistent business outcome, but intermediate states are often visible. It commonly uses eventual consistency.