Explore Saga in Scala for coordinating distributed workflows with compensation, idempotency, timeout budgets, and durable step ownership across services.
Saga pattern: A behavioral pattern for coordinating a multi-step distributed workflow where each step commits locally and failures are handled through compensating actions rather than through one global transaction.
Saga matters when behavior crosses service boundaries and the system still needs a coherent business outcome. In Scala, the implementation might live in actors, workflow engines, queues, or effect-based orchestration code, but the core design problem stays the same: who owns step order, recovery, and compensation?
Use Saga when:
Common examples include order placement, booking, refund flows, and cross-service provisioning.
Compensation should be designed as a real business action, not as a fantasy rollback. Releasing reserved inventory, refunding a payment, or canceling a shipment request are domain operations with their own constraints and failure modes.
That is why Saga design is mostly about careful step semantics:
There are two common shapes:
Scala codebases often start with orchestration because it keeps ownership clearer. Choreography can work, but it becomes hard to reason about once too many services react to one another implicitly.
The diagram below shows a simple orchestrated failure path:
sequenceDiagram
participant API as "Order API"
participant Saga as "Order Saga"
participant Inv as "Inventory Service"
participant Pay as "Payment Service"
participant Ship as "Shipping Service"
API->>Saga: "Place order"
Saga->>Inv: "Reserve stock"
Inv-->>Saga: "Reserved"
Saga->>Pay: "Charge payment"
Pay-->>Saga: "Failed"
Saga->>Inv: "Release stock"
Inv-->>Saga: "Released"
Saga-->>API: "Order rejected"
The main thing to notice is that the saga owns the workflow state and compensation sequence. The services still do their own local work, but the business-level flow has one obvious home.
A real saga should track durable workflow state:
Without durable state, restarts and duplicate messages quickly turn a “simple” workflow into a mystery.
Every step and compensation path should be designed with repeated delivery in mind. In distributed systems, “exactly once” is usually a story told at the interface boundary, not a free implementation fact.
That means:
The system assumes every step can be reversed cleanly, but the domain reality is messier.
Several services partially coordinate the flow, and no single place makes it clear which step is next or who compensates what.
Network retries turn into duplicate charges, repeated emails, or double reservations.
Use Saga when one business action spans multiple local transactions. Prefer explicit workflow ownership, durable saga state, idempotent step contracts, and business-real compensation. If compensation is not credible or eventual consistency is unacceptable, the system probably needs a different boundary, not just a prettier saga implementation.