Hello! Welcome back to your course on designing high-load distributed systems.
In our last lesson, we implemented the Transactional Outbox pattern, ensuring that a service can atomically update its own database and guarantee the eventual delivery of an event. This reliable eventing is a critical building block for the pattern we'll explore today: the Saga.
When a business process spans multiple microservices—like placing an order, processing a payment, and updating inventory—we can't use a traditional ACID transaction. The Saga pattern provides a way to maintain data consistency across these services through a sequence of local transactions.
Today's learning outcome is to compare choreography vs. orchestration for implementing Sagas and design a Saga for a given scenario. We will cover:
- The core concepts of the Saga pattern, including local transactions and compensations.
- The two primary coordination strategies: event-driven Choreography and command-driven Orchestration.
- The practical trade-offs between these two approaches in terms of coupling, complexity, and observability.
- A design exercise to apply these concepts to a real-world scenario.
1. The Saga Pattern: A Sequence of Local Transactions
A Saga is a sequence of local transactions where each transaction updates the database within a single service and triggers the next step in the process. If a step fails, the Saga executes a series of compensating transactions to undo the preceding work, thus maintaining overall data consistency.
This diagram illustrates the general flow, showing both the successful path and the rollback path using compensating transactions.

To get a formal definition, let's turn to a foundational resource on microservice patterns.
The article 'Pattern: Saga' by Chris Richardson on microservices.io provides a concise and authoritative definition of the Saga pattern and its two coordination models. This will establish our core vocabulary.
Please read the 'Context', 'Problem', and 'Solution' sections. Focus on how a Saga is defined as a sequence of local transactions and the introduction of Choreography and Orchestration.
As you've read, the two coordination strategies are the heart of implementing a Saga. Let's explore each in detail.
2. Choreography: A Decentralized, Event-Driven Dance
In a choreography-based Saga, there is no central coordinator. Each service participates in the Saga by publishing and subscribing to events. When a service completes its local transaction, it publishes an event, which then triggers the next service(s) in the workflow.
This is where our previous lesson on the Transactional Outbox becomes critical. For a choreographed Saga to be reliable, each service must be able to atomically update its state and publish its event. The outbox pattern is the standard way to achieve this.
How it works:
- Service A performs a local transaction and publishes an event (e.g.,
OrderCreated). - Service B listens for this event, performs its local transaction, and publishes its own event (e.g.,
PaymentProcessed). - Service C listens for
PaymentProcessedand continues the chain. - If a service fails, it publishes a failure event (e.g.,
PaymentFailed). Other services that have already completed their work must listen for this failure event and execute a compensating transaction (e.g., the Order service cancels the order).
This approach is highly decentralized. Each service is only aware of the events it needs to consume and produce, promoting loose coupling.
To see a more detailed breakdown and a practical example, let's look at the following resource.
Saga pattern: Choreography and Orchestration - Medium
The Medium article 'Saga pattern: Choreography and Orchestration' provides a great walkthrough of a choreographed Saga using an e-commerce example with Spring Boot and Kafka. We'll focus on the conceptual overview and the flow.
Please read the 'Choreography' section, including the 'Overview' and 'How Choreography Works' subsections. You can skim the code snippets; the key is to understand the event flow between the services via the event broker.
3. Orchestration: A Centralized, Command-Driven Workflow
In an orchestration-based Saga, a central component, the Saga Orchestrator, is responsible for managing the entire transaction. The orchestrator tells the participant services what to do and in what order. It explicitly sends commands to services and waits for replies. The orchestrator maintains the state of the entire Saga.

How it works:
- A request comes in to create the Saga. The orchestrator is instantiated.
- The orchestrator sends a command to Service A (e.g.,
ReserveCredit). - Service A performs its local transaction and sends a reply (success or failure) to the orchestrator.
- Based on the reply, the orchestrator decides the next step: either send a command to Service B or, if Service A failed, start sending compensating commands to undo previous steps.
- This continues until the Saga completes or is fully compensated.
The orchestrator can be a dedicated service or part of the service that initiates the Saga. It effectively becomes a state machine that tracks the progress of the distributed transaction.
Let's return to the Medium article to see how this compares to the choreographed approach.
Saga pattern: Choreography and Orchestration - Medium
We'll now examine the orchestration part of the same article. It explains the centralized model and walks through the same e-commerce scenario from a command-driven perspective.
Please read the 'Orchestration' section, focusing on the 'Overview' and the explanation of the detailed example diagram. Notice how the orchestrator communicates with the services, often using a mix of commands (via HTTP or messages) and events.
4. Choreography vs. Orchestration: A Head-to-Head Comparison
Choosing between these two patterns involves significant architectural trade-offs. Your experience in building high-load systems has likely shown that there is no single "best" solution; the choice depends on the specific context of the business process and the team structure.
Let's formalize the comparison.
| Aspect | Choreography (Decentralized Events) | Orchestration (Centralized Commands) |
|---|---|---|
| Coupling | Loosely Coupled: Services don't need to know about each other, only about events. This simplifies adding/removing participants. | Tightly Coupled (to Orchestrator): Participant services have a direct dependency on the orchestrator's API. The orchestrator is coupled to all participants. |
| Complexity | Simple Components: Each service is simple and has a single responsibility. Complex Workflow: Understanding the end-to-end flow requires tracing events across multiple services, which can be difficult. | Complex Orchestrator: The business logic is centralized in the orchestrator, which can become a complex state machine. Simple Components: Participant services are simple, often just exposing a command/reply API. |
| Observability | Difficult: Debugging and monitoring are challenging. A failure requires correlating events across multiple service logs to find the root cause. | Easy: The orchestrator is a single place to check the status of a Saga. Failures are logged centrally, simplifying debugging. |
| Resilience | High: No single point of failure. If an event-consuming service is down, events can queue up in the broker until it recovers (assuming a durable message bus). | Low: The orchestrator is a single point of failure. If it goes down, no new Sagas can be processed, and in-flight Sagas are stalled. High availability for the orchestrator is critical. |
The final section of the Medium article provides a good summary table.
Saga pattern: Choreography and Orchestration - Medium
To conclude our comparison, please read the final wrap-up section of the article, which presents these trade-offs in a concise table.
Read the 'Wrap up : Choreography vs Orchestration' section and review the comparison table.
5. Design Exercise
Now, let's apply these concepts. Imagine you are designing the backend for a food delivery service. A user placing an order triggers a workflow that involves four services:
- Order Service: Creates the order in a
PENDINGstate. - Restaurant Service: Receives the order, confirms it can be prepared, and accepts it.
- Payment Service: Charges the user's saved credit card.
- Courier Service: Finds and assigns a nearby courier.
The order is only considered CONFIRMED after the restaurant accepts it and the payment succeeds.
Your Task (approx. 5 minutes):
- Choose between Choreography and Orchestration for this workflow.
- Justify your choice, referencing the trade-offs we discussed (coupling, observability, etc.).
- Outline the sequence of steps (events or commands) for a successful order.
- Outline the compensating transactions required if the
Payment Servicefails after theRestaurant Servicehas already accepted the order.
(Take a few minutes to think through your design.)
Discussion of a Possible Solution
Both patterns are viable, but let's consider a solution using Orchestration, as business workflows like this often benefit from explicit control.
-
Justification: An orchestration approach is often preferred for a core business process like ordering because the flow is explicit and easier to monitor. If an order fails, customer support needs a clear, centralized view of what went wrong, which an orchestrator provides. While it introduces a single point of failure, the operational benefits of clear workflow management can outweigh this risk, which can be mitigated with a highly available orchestrator service.
-
Successful Flow (Orchestrated):
- Client sends
POST /ordersto theOrder Service. Order Servicecreates anOrderinPENDINGstate and starts anOrderSagaOrchestrator.- Orchestrator sends
AcceptOrdercommand toRestaurant Service. Restaurant Servicereplies withOrderAccepted.- Orchestrator sends
ProcessPaymentcommand toPayment Service. Payment Servicereplies withPaymentSuccessful.- Orchestrator sends
AssignCouriercommand toCourier Service. - Orchestrator updates the
Orderstatus toCONFIRMED.
- Client sends
-
Compensation Flow (Payment Fails):
- ...Steps 1-4 happen as above.
- Orchestrator sends
ProcessPaymentcommand toPayment Service. Payment Servicereplies withPaymentFailed.- Orchestrator initiates compensation: it sends a
CancelRestaurantOrdercommand to theRestaurant Service. - Orchestrator updates the
Orderstatus toFAILED.
A choreographed approach would involve the Order Service emitting an OrderPlaced event, the Restaurant Service consuming it and emitting OrderAccepted, and both the Payment Service and Courier Service (potentially) reacting to OrderAccepted. This is more decoupled but harder to trace.
Conclusion
Today we've explored the Saga pattern as a crucial tool for managing consistency in distributed systems.
Key Takeaways:
- Sagas manage long-running business transactions by sequencing local transactions and using compensating transactions to handle failures.
- Choreography is a decentralized, event-driven approach that offers loose coupling and high resilience but makes the overall workflow implicit and harder to observe.
- Orchestration is a centralized, command-driven approach where an orchestrator explicitly manages the workflow, providing excellent observability at the cost of tighter coupling and a potential single point of failure.
- The choice between them is a critical architectural decision that depends on the specific requirements for coupling, visibility, and the complexity of the business process.
Preview of the Next Lesson:
In our design exercise, we outlined the need for compensating transactions. In the next lesson, we will dive into the practical details and implement compensation logic for a failed step in a choreographed Saga, building directly on the concepts from today.