The idea: one business transaction, two ways to drive it

An order touches four services, each with its own database. Order saves the order, Payment charges 100, Inventory reserves one item and Shipping books a courier. No single transaction covers all four, so the order is a saga: a chain of local transactions, each undone by a compensating transaction if a later one fails. See Saga vs Two-Phase Commit for why a saga and not 2PC.

Left: the Order Saga orchestrator holds the saga definition and sends ChargePayment, ReserveStock and BookCourier to Payment, Inventory and Shipping, each answering with a reply. Right: Order, Payment, Inventory and Shipping each publish OrderCreated, PaymentCharged, StockReserved and CourierBooked to a broker, and each listens to the event before its own step.
Orchestration writes the flow down in one place; choreography spreads it over every service's subscriptions.

A saga can be driven in two ways. The page runs the same order with the same failure through both at once, on one clock where every hop takes one tick. On the left is orchestration. On the right is choreography. Under each side, a table shows where the flow is written down.

Orchestration: commands, replies, one saga definition

An orchestrator (here the Order Saga, often a class inside the Order service or a workflow engine such as Temporal or Camunda) holds the saga definition: the list of steps, each with its command and its compensation. It sends one command, waits for the reply, logs it, and sends the next one. Participants do not know about each other. They only answer commands.

The whole flow, including the failure path, is in one table. Reading the saga log tells you exactly where any order is. The price is a central component that every new step has to go through. It can also grow into a "god service" that holds business logic that belongs to the participants.

Choreography: events, subscriptions, no one in charge

With choreography there is no coordinator. Each service listens to some events and publishes its own: OrderCreated → Payment charges → PaymentCharged → Inventory reserves → StockReserved → Shipping books → CourierBooked → Order confirms. The broker (Kafka, RabbitMQ, a cloud event bus) only stores and delivers events.

Services stay loosely coupled: Payment doesn't know who reacts to PaymentCharged. The flow, though, is not written anywhere. It exists only as the sum of the subscription lists in the "who reacts to what" table, spread over every service's code. To answer "what happens after a payment?" you have to read all of them.

orchestrationchoreography
messagescommands and repliesevents
who knows the flowthe orchestrator (one table)nobody; it is spread over the subscriptions
where an order isin the saga logmust be pieced together from every service
couplingorchestrator → each participanteach service → the events it listens to
messages per happy order (page)84 events

Failures and compensation on both sides

When Inventory is out of stock, both sides end with a refund and a cancelled order at the same tick (Demo 2: Inventory out of stock). On the left the orchestrator reads StockFailed, looks up the compensations of the steps already done and sends RefundPayment, then RejectOrder. On the right the same result needs two more subscriptions: Payment listens to StockFailed and refunds, and Order listens to PaymentRefunded and cancels. When Payment declines, there is nothing to undo (Demo 3).

A service is down: a timeout vs a stalled order

Shipping is down for the whole run (Demo 4: Shipping is down). The orchestrator is waiting for a reply, so its silence is noticed. After 3 ticks it retries, after 3 more it gives up, releases the stock, refunds the payment and rejects the order. The order ends in a known state, and the saga log says why.

In choreography the broker holds StockReserved until Shipping comes back. No service is waiting for anything, so nothing happens: the order stays PENDING, with the customer charged and the item reserved, maybe for hours. Holding the event is correct (no event is lost), but nobody is accountable for the order. Teams add a timeout watcher (a job that looks for orders stuck in PENDING), distributed tracing and dashboards. Some add a small orchestrator just for the deadline.

Shipping is down. Orchestration: BookCourier is lost, the orchestrator times out, retries, times out again, gives up, sends ReleaseStock, RefundPayment and RejectOrder, and the order ends CANCELLED. Choreography: Inventory publishes StockReserved, the broker holds it for Shipping, nobody is waiting, and the order stays PENDING with the payment charged and the item reserved.
A missing reply is noticed by the orchestrator that waits for it; a held event is noticed by nobody.

Changing the flow: coupling and cycles

The business wants a fraud check before payment (Demo 5: add a fraud check before payment). Orchestration changes the saga definition and adds the Fraud service: 2 places. Choreography adds Fraud (it listens to OrderCreated), changes Payment (it must now wait for FraudCleared instead of OrderCreated) and changes Order (it must cancel on FraudRejected): 3 services, often owned by 3 teams, deployed in the right order. Adding a step that only reacts (send an e-mail on CourierBooked) is where choreography shines: one new subscriber, no change anywhere else.

With many services, event chains can also form cycles. A service reacts to an event that, a few hops later, causes the event it started from. Nothing in the code shows the loop. It only shows up in production, as a storm of events.

Reliable messaging underneath

Both sides assume a service never commits its local change without also sending its reply or event, and never sends one without the change. That needs the transactional outbox. Delivery is then at-least-once, so every handler must be idempotent: an inbox table, or a key per saga and step. The page draws each hop once and leaves this out. See also How Kafka Moves a Message for the broker side.

When to use which

preferwhen
orchestrationmany steps, a strict order, failure paths that matter, deadlines, someone has to answer "where is order 1?"
choreographyfew steps, independent reactions (notify, update a read model, award points), teams that must not depend on a central service
bothcommon in practice: each bounded context orchestrates its own saga, and contexts talk through events

What the page leaves out

The outbox and the idempotent handlers that every hop needs; retries inside choreography; the orchestrator's own crash and recovery from its log (see Saga vs Two-Phase Commit); parallel steps; several orders competing for the same stock; semantic locks (a PENDING state that other sagas respect); and workflow engines' durable timers and versioned definitions.