Introduction
As applications evolve from monolithic architectures to microservices, one of the biggest challenges developers face is maintaining data consistency across multiple services.
In a monolithic application, a single database transaction ensures that all operations either succeed or fail together. However, in a microservices architecture, each service owns its own database, making traditional distributed transactions (such as Two-Phase Commit or 2PC) impractical due to scalability, availability, and performance concerns.
This is where the Saga Design Pattern becomes an essential architectural pattern.
Rather than relying on a single distributed transaction, a Saga breaks a business process into multiple local transactions. Each service performs its own transaction and communicates with other services through events or commands. If one of the later steps fails, the Saga executes compensating transactions to undo the effects of previously completed steps.
What is the Saga Pattern?
A Saga is a sequence of local transactions that together complete a single business process.
Each microservice:
- Executes its own database transaction.
- Publishes an event (or sends a response).
- Triggers the next step in the workflow.
If every step succeeds, the business transaction completes successfully.
If any step fails, the Saga initiates compensating actions to bring the system back to a consistent business state.
Unlike a database rollback, a Saga performs logical rollback using business operations.
Why Do We Need Saga?
Consider an e-commerce application with the following services:
- Order Service
- Payment Service
- Inventory Service
- Shipping Service
A customer places an order.
The workflow looks like this:
Create Order
↓
Charge Payment
↓
Reserve Inventory
↓
Create Shipment
Now imagine the shipment service is unavailable.
At this point:
- The order has already been created.
- The payment has already been charged.
- Inventory has already been reserved.
Without a Saga, these completed operations remain, leaving the system in an inconsistent business state.
With a Saga, compensating actions are executed:
Cancel Shipment
↓
Release Inventory
↓
Refund Payment
↓
Cancel Order
The customer experiences either a fully successful order or a properly cancelled order.
How Saga Works in Microservices
Let’s walk through a typical workflow.
Step 1: Customer Places an Order
The Order Service:
- Saves the order.
- Publishes an
OrderCreatedevent.
Customer
│
▼
Order Service
Step 2: Payment Service
The Payment Service receives the event.
It:
- Validates the payment.
- Charges the customer.
- Publishes a
PaymentCompletedevent.
OrderCreated
│
▼
Payment Service
Step 3: Inventory Service
The Inventory Service:
- Reserves stock.
- Publishes an
InventoryReservedevent.
PaymentCompleted
│
▼
Inventory Service
Step 4: Shipping Service
The Shipping Service:
- Creates the shipment.
- Publishes a
ShipmentCreatedevent.
The Saga is now complete.
What Happens If Something Fails?
Suppose the Inventory Service discovers that the product is out of stock.
Instead of continuing, it publishes an InventoryReservationFailed event.
The Saga now starts compensation.
Inventory Reservation Failed
│
▼
Refund Payment
│
▼
Cancel Order
Notice that we are not rolling back the database transaction.
Instead, we execute new business transactions that reverse the earlier actions.
Types of Saga
1. Choreography-Based Saga (Event-Driven)
In choreography, there is no central coordinator.
Each service listens to events and decides what to do next.
Example:
Order Created
↓
Payment Completed
↓
Inventory Reserved
↓
Shipment Created
Each service simply reacts to events.
Advantages
- No central controller.
- Loosely coupled services.
- Easy to start with.
Disadvantages
- Difficult to understand as the number of services grows.
- Event flow becomes hard to trace.
- Debugging production issues becomes challenging.
2. Orchestration-Based Saga (Centralized)Design Pattern
In orchestration, a dedicated Saga Orchestrator controls the workflow.
Saga Orchestrator
│
┌───────────┼───────────┐
│ │ │
Order Service Payment Inventory
│
Shipping Service
Instead of waiting for events, each service receives commands from the orchestrator.
Example:
- Create Order
- Charge Payment
- Reserve Inventory
- Create Shipment
If any step fails, the orchestrator decides which compensating actions to execute.
Advantages
- Easier to understand.
- Centralized workflow.
- Better monitoring and debugging.
- Suitable for complex business processes.
Disadvantages
- Requires an additional orchestration service.
- Adds another component to maintain.
Saga Is Not a Distributed Database Transaction
One common misconception is that Saga behaves like a database transaction. It does not.
Database Transaction:
Begin Transaction
Update A
Update B
Update C
Commit
If something fails:
Rollback
Everything is undone automatically.
Saga:
Create Order
Charge Payment
Reserve Inventory
If Reserve Inventory fails:
Refund Payment
Cancel Order
Instead of a database rollback, the Saga performs business compensation.
Saga Alone Is Not Enough
A Saga coordinates the business workflow, but it doesn’t solve every reliability problem.
In production systems, it is usually combined with other patterns:
- Idempotency – Makes APIs and event handlers safe to retry without producing duplicate business effects.
- Outbox Pattern – Ensures events are reliably published to the message broker after a successful database transaction.
- Inbox Pattern – Prevents duplicate event processing when brokers deliver messages more than once.
Together, these patterns provide a resilient event-driven architecture.
Production Architecture
A typical production solution looks like this:
Client
│
▼
Order Service
│
Local Transaction + Outbox
│
Outbox Publisher
│
Kafka / RabbitMQ
│
┌───────────────┴───────────────┐
▼ ▼
Payment Service Inventory Service
Inbox + Idempotency Inbox + Idempotency
│ │
└───────────────┬───────────────┘
▼
Saga (Orchestrator)
│
Compensation if needed
Architecture
Client
│
▼
Order Service
│
OrderCreated Event
▼
+---------------------------+
| Saga Orchestrator | <-- Separate .NET Application
|---------------------------|
| • Workflow State |
| • Business Flow |
| • Compensation Logic |
| • Timeouts |
+---------------------------+
│ │ │
▼ ▼ ▼
Payment Inventory Shipping
Service Service Service
The orchestrator is just another ASP.NET Core Worker Service or Web API that:
- Consumes events
- Sends commands
- Maintains Saga state
- Executes compensation when needed
For enterprise systems with many microservices, the common architecture is:
- Order Service → Owns orders.
- Payment Service → Owns payments.
- Inventory Service → Owns inventory.
- Shipping Service → Owns shipping.
- Saga Orchestrator (.NET Worker/Web API) → Owns the workflow and compensation logic.
When Should You Use Saga?
Use the Saga Pattern when:
- A business process spans multiple microservices.
- Each service owns its own database.
- Distributed transactions (2PC) are not feasible.
- Event-driven communication is used.
- Business consistency is more important than immediate consistency.
Examples include:
- E-commerce order processing
- Flight and hotel booking
- Banking transfers
- Insurance claim processing
- Food delivery applications
- Supply chain management
Two-Phase Commit (2PC)
Two-Phase Commit (2PC) is a protocol used to ensure that multiple databases or services either all commit a transaction or all roll it back. It was designed for distributed transactions where consistency is more important than performance.
| Feature | Two-Phase Commit (2PC) | Saga |
|---|---|---|
| Transaction type | Global distributed transaction | Sequence of local transactions |
| Consistency | Immediate (strong consistency) | Eventual consistency |
| Rollback | Database rollback | Business compensation |
| Database locks | Yes | No |
| Scalability | Lower | Higher |
| Failure handling | Coordinator decides commit/rollback | Compensation actions |
| Best suited for | Small, tightly controlled distributed systems | Modern microservices |
Key Takeaways
- A Saga manages long-running business transactions across multiple microservices.
- Each service executes its own local transaction instead of participating in a distributed database transaction.
- If a later step fails, compensating transactions restore business consistency.
- Sagas can be implemented using Choreography (event-driven) or Orchestration (central coordinator).
- A production-ready Saga typically works alongside the Outbox, Inbox, and Idempotency patterns to ensure reliable messaging, duplicate protection, and safe retries.
By combining these patterns, microservices can achieve high scalability, resilience, and eventual consistency without relying on heavyweight distributed transactions.
