Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
Chaturmind
← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
HomeLearnSpring BootSpring Boot REST API DevelopmentDistributed Data & Consistency Patterns
✓ FreeAdvanced· 9 min read

Saga Pattern

Breaking a business transaction into local transactions per service, choreography vs orchestration, compensating transactions, persisting saga state to survive a crash, and the availability-for-consistency trade.

Published September 23, 2026


Saga Pattern

Two-Phase Commit's blocking, coordinator-dependent model doesn't fit independently-deployed services. Saga is the standard alternative: no distributed lock held across services, no single blocking coordinator — instead, a sequence of independent local transactions, each committing immediately in its own service.

The core idea: a sequence of local transactions

Order Service:     create order (LOCAL commit, immediate)
Payment Service:    charge card (LOCAL commit, immediate)
Inventory Service:  reserve stock (LOCAL commit, immediate)
Shipping Service:   schedule delivery (LOCAL commit, immediate)

Each step commits immediately and independently — no service holds a lock waiting for the others, unlike 2PC. The tradeoff this immediately introduces: if step 3 fails after steps 1 and 2 already committed, the system is temporarily in a partially-completed state that needs explicit correction, not an automatic rollback the way a single ACID transaction would provide.

Choreography-based saga: no central coordinator

Order Service:  creates order → publishes OrderCreated event
Payment Service: listens for OrderCreated → charges card → publishes PaymentCompleted event
Inventory Service: listens for PaymentCompleted → reserves stock → publishes StockReserved event

Each service reacts to the previous service's event and publishes its own — no service explicitly knows the full saga sequence, each just knows "when I see event X, I do Y and publish Z." This keeps services maximally decoupled (no shared central definition of the flow), but makes the overall saga flow hard to see from any single place — understanding "what happens when an order is placed" requires tracing event chains across every participating service's code.

Orchestration-based saga: a central orchestrator

class OrderSagaOrchestrator {
    void execute(OrderRequest request) {
        try {
            orderService.createOrder(request);
            paymentService.chargeCard(request);
            inventoryService.reserveStock(request);
            shippingService.scheduleDelivery(request);
        } catch (Exception e) {
            compensate(request); // explicit, centrally-visible failure handling
        }
    }
}

A dedicated orchestrator explicitly sequences and tracks each step — the full saga flow is visible in one place (this class), easier to reason about and modify than tracing choreography's distributed event chains, at the cost of the orchestrator itself becoming a coupling point every participating service depends on. Most teams beyond a small number of saga steps lean orchestration specifically for this visibility benefit, reserving choreography for simpler, shorter sagas where the coupling-reduction benefit outweighs the lost central visibility.

Compensating transactions: the "undo" for each step

void compensate(OrderRequest request) {
    // run compensations in REVERSE order of the steps that actually completed
    if (stockReserved) inventoryService.releaseStock(request);
    if (paymentCharged) paymentService.refund(request);
    if (orderCreated) orderService.cancelOrder(request);
}

Since there's no automatic rollback (unlike a single ACID transaction), each step needs an explicit compensating action — refunding a charge, releasing a stock reservation, cancelling an order — run in reverse order for whichever steps actually completed before the failure. This is the real design cost of sagas: every forward step needs a corresponding, correctly-implemented compensation, and getting a compensation wrong (e.g. a refund that doesn't fully reverse the original charge) is a genuine, hard-to-catch bug class specific to this pattern.

Saga state tracking: surviving a crash

class SagaState {
    String sagaId;
    List<String> completedSteps; // persisted — so a crash mid-saga can resume, not restart from scratch or lose track
    SagaStatus status;
}

An orchestrator (or a choreography participant) that loses track of which steps already completed, after a crash and restart, risks either re-running an already-completed step (a duplicate charge, if not idempotent — see Payment — Idempotency Implementation) or failing to run compensations for steps that DID complete. Persisting saga progress durably (not just in-memory) is what makes crash recovery correct rather than a guessing game.

The trade-off: availability and loose coupling, eventual consistency only

Sagas explicitly give up the strong, immediate consistency 2PC (attempts to) provide — during a saga's execution, the system is observably in an intermediate state (an order exists but payment hasn't completed yet), visible to anyone querying it at that moment. In exchange, no service is ever blocked holding locks waiting on another, and services can fail/deploy/scale independently without a shared coordinator dependency — the same fundamental trade Eventual Consistency Design covers generally, applied specifically to multi-step business transactions.

Follow-up questions this topic invites — and their answers

Q: What happens if a compensating transaction itself fails? A: This is a genuinely hard, often under-addressed problem — compensations need their own retry logic (see Retry & Backoff Strategies) and, in the worst case, a path to manual/human intervention (an alert routing to an on-call engineer) if automated compensation can't complete, since a failed compensation leaves the system in an inconsistent state with no further automatic recovery path.

Q: How would you choose between choreography and orchestration for a specific saga? A: Choreography fits short sagas (2-3 steps) where the coupling-avoidance benefit is worth the reduced visibility; orchestration fits longer or more complex sagas where centralized visibility, easier debugging, and simpler reasoning about the full flow outweigh the orchestrator becoming a shared dependency.

Q: Is a saga's intermediate state ever a problem for users, not just internally? A: Yes directly — this is exactly the 'designing UI/UX around eventual consistency' concern Eventual Consistency Design names (an 'order placed, processing payment...' UI state), since a user querying order status mid-saga needs to see an accurate, explicitly-intermediate state rather than either a premature 'complete' or a confusing error.

Q: How does the Outbox Pattern relate to making choreography-based sagas reliable? A: Directly — a choreography step publishing its triggering event needs that publish to be reliably tied to its own local transaction commit (the dual-write problem), which is exactly what Outbox Pattern solves; without it, a service could commit its local step but fail to publish the event that triggers the next saga step, silently stalling the whole saga.

Previous

Two-Phase Commit

Next

Outbox Pattern

AI Tutor

Lesson: Saga Pattern

Quick actions

AI responses can be inaccurate. Verify critical information.