Distributed systems
Orchestration versus choreography for sagas
A saga can be driven by a central orchestrator, or by services reacting to each other's events. Learn the trade-offs, and why durable workflow engines make orchestration practical.
Once you have a saga, someone has to drive it. There are two ways to do that. In orchestration, a central coordinator tells each service what to do and tracks the saga's state. In choreography, there is no coordinator. Each service listens for events and publishes its own.
Concept
An orchestrator is a state machine for one saga: "payment succeeded, call inventory next; inventory failed, run the refund."
The two styles
Orchestration. The orchestrator calls each participant in order, records each result, and decides the next step. It is one place to read the whole flow.
Choreography. Orders publishes OrderCreated. Payments hears it, charges, and publishes PaymentSucceeded. Inventory hears that, reserves stock, and publishes InventoryReserved or InventoryFailed. Payments hears InventoryFailed and refunds. The flow exists only as the sum of the handlers.
| Orchestration | Choreography | |
|---|---|---|
| Where the flow lives | One place, easy to read | Spread across services' event handlers |
| Coupling | The orchestrator knows every participant | Services know only events |
| Visibility | Query the orchestrator for saga state | Rebuild the state from events and traces |
| Adding a step | Change the orchestrator | Add a listener, and hope nothing assumed the old flow |
| Good for | Flows with more than about three steps, or branching logic | Short, stable flows |
Durable workflow engines
Most teams with non-trivial sagas end up with orchestration on a durable workflow engine, such as Temporal, AWS Step Functions or Azure Durable Functions. These engines persist each step's result. The orchestrator survives crashes and resumes exactly where it stopped. They also handle retries and timers.
Temporal lets you write the workflow as ordinary code. This sketch shows the shape. Activities are retried by policy, and each one must be idempotent:
// A sketch of a Temporal workflow (TypeScript SDK).
export async function placeOrder(order: Order): Promise<void> {
await reserveInventory(order);
try {
await chargeCard(order); // idempotent activity, keyed by order.id
} catch (err) {
await releaseInventory(order); // compensation
throw err;
}
await createShipment(order);
await sendConfirmationEmail(order);
}Choreography is elegant for two or three steps. Beyond that, it becomes very hard to reason about. The broader trade-off between events and direct calls is in event-driven architecture.
Common mistakes
- Choreographing a long flow. Nobody can see the whole process, and changes break hidden assumptions.
- An orchestrator with no durable state. A crash loses progress and can repeat steps.
- Non-idempotent activities. The engine retries them, so a charge can run twice.
Check yourself
1. An order is stuck in a choreographed saga. Where do you look? Across several services' event logs and traces. The flow exists nowhere in one place.
2. Why does a durable workflow engine help an orchestrator? It saves each step's result, so after a crash the orchestrator resumes where it stopped instead of starting over.
Key takeaways
- Orchestration keeps the flow in one place. Choreography spreads it across event handlers.
- Choose choreography for short, stable flows, and orchestration for anything with branching or compensation.
- Durable workflow engines make orchestrators survive crashes.
- Every activity in the workflow must be idempotent, because retries will repeat it.
Further learning
The Temporal documentation covers workflows, activities, retries and timers in detail.
Temporal documentationTemporal Platform Documentationdocs.temporal.ioNext, see how a service updates its database and publishes an event without a dual write, in the transactional outbox.
Related
Sagas and compensating actions
A saga runs a business operation as local transactions, each with an undo step. Learn how compensations work, how to order the steps, and why sagas are atomic eventually but not isolated.
Event-driven architecture, CQRS and event sourcing
Building systems around events, from change data capture to event sourcing and read models, and knowing when it simplifies a system versus creating a distributed mess.
The transactional outbox
Update a database and publish an event without a dual write. Learn how the outbox commits the event with the change, how a relay publishes it, and how an inbox removes duplicates.