Saga Design Pattern in Microservices

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 OrderCreated event.
Customer
    │
    ▼
Order Service

Step 2: Payment Service

The Payment Service receives the event.

It:

  • Validates the payment.
  • Charges the customer.
  • Publishes a PaymentCompleted event.
OrderCreated
      │
      ▼
Payment Service

Step 3: Inventory Service

The Inventory Service:

  • Reserves stock.
  • Publishes an InventoryReserved event.
PaymentCompleted
        │
        ▼
Inventory Service

Step 4: Shipping Service

The Shipping Service:

  • Creates the shipment.
  • Publishes a ShipmentCreated event.

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:

  1. Create Order
  2. Charge Payment
  3. Reserve Inventory
  4. 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.

FeatureTwo-Phase Commit (2PC)Saga
Transaction typeGlobal distributed transactionSequence of local transactions
ConsistencyImmediate (strong consistency)Eventual consistency
RollbackDatabase rollbackBusiness compensation
Database locksYesNo
ScalabilityLowerHigher
Failure handlingCoordinator decides commit/rollbackCompensation actions
Best suited forSmall, tightly controlled distributed systemsModern 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.

Share Button

Leave a Reply

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