What is the Difference Between Choreography and Orchestration in Saga
In distributed systems, the Saga Pattern replaces classical two-phase commit transactions with a sequence of decoupled local transactions paired with compensating actions upon f...
In distributed systems, the Saga Pattern replaces classical two-phase commit transactions with a sequence of decoupled local transactions paired with compensating actions upon failure. Coordinating these distributed execution steps is achieved through two fundamentally distinct paradigms: Choreography and Orchestration.
🟢 Junior Level
Conceptual Foundation: Two Ways to Coordinate a Saga
Choreography (Event-Driven):
[Order Service] ──OrderCreated──► [Payment Service] ──PaymentCompleted──► [Delivery Service]
▲ │
└────────PaymentFailed (Rollback)───┘
Orchestration (Command-Driven):
┌───────────────────────────┐
│ Saga Orchestrator │
│ (State Machine + Saga Log)│
└──────┬───────┬───────┬────┘
1. CreateOrder │ │ 2. Charge │ 3. Dispatch
▼ ▼ ▼
[Order Service] [Payment] [Delivery]
Core Metaphor:
- Choreography resembles a ballroom dance: there is no conductor on stage. Each dancer has memorized their steps and responds dynamically to the gestures and movements of their partners. Services exchange domain events (
OrderCreated,PaymentAuthorized) via a message broker. - Orchestration resembles a symphony orchestra with a conductor: the musicians do not watch each other; they watch the conductor (orchestrator), who explicitly commands who plays, what note to strike, and when (
ReserveInventoryCommand,ChargeCardCommand).
3 Key Distinctions:
- Coordination Authority: Choreography is decentralized (no central coordinator); Orchestration is centralized in a dedicated Saga Execution Coordinator (SEC).
- Messaging Semantics: Choreography relies on Domain Events (facts about what has happened); Orchestration relies on targeted Commands (directives on what must be done) and command replies.
- Application Scope: Choreography excels in concise pipelines (2–3 services). Orchestration is essential for complex business processes (4+ services, branching conditions, timers, manual interventions).
🟡 Middle Level
1. Choreography Architecture (Event-Driven)
Services react to domain events published by peers, execute a local transaction in their private database, and publish follow-up events via the Transactional Outbox pattern:
// Payment Service reacts to order creation event
@Component
public class PaymentEventHandler {
private final PaymentService paymentService;
private final EventPublisher eventPublisher;
@KafkaListener(topics = "order-events", groupId = "payment-group")
public void onOrderCreated(OrderCreatedEvent event) {
try {
paymentService.processPayment(event.orderId(), event.amount());
// Success: Publish event for downstream consumers (Delivery Service)
eventPublisher.publish(new PaymentSucceededEvent(event.orderId()));
} catch (InsufficientFundsException e) {
// Failure: Publish compensating event for upstream consumers
eventPublisher.publish(new PaymentFailedEvent(event.orderId(), e.getMessage()));
}
}
}
Rollback Flow: If step 3 (Delivery) encounters an error, it publishes DeliveryFailedEvent. The PaymentService must subscribe to this event and issue a refund (refund()), while OrderService must subscribe and transition the order status to CANCELLED.
2. Orchestration Architecture (Command-Driven)
A central Saga Execution Coordinator (SEC) persists state machine transitions to a durable Saga Log and dispatches point-to-point commands to participants:
@Component
public class OrderSagaOrchestrator {
private final SagaRepository sagaRepo;
private final CommandGateway commandGateway;
@Transactional
public void onEvent(SagaReplyEvent reply) {
OrderSagaData saga = sagaRepo.findById(reply.sagaId())
.orElseThrow();
switch (saga.getState()) {
case PAYMENT_PENDING -> {
if (reply.isSuccess()) {
saga.transitionTo(SagaState.DELIVERY_PENDING);
commandGateway.send(new ScheduleDeliveryCommand(saga.getOrderId()));
} else {
saga.transitionTo(SagaState.COMPENSATING_ORDER);
commandGateway.send(new CancelOrderCommand(saga.getOrderId()));
}
}
case DELIVERY_PENDING -> {
if (reply.isSuccess()) {
saga.transitionTo(SagaState.COMPLETED);
} else {
saga.transitionTo(SagaState.REFUNDING_PAYMENT);
commandGateway.send(new RefundPaymentCommand(saga.getOrderId()));
}
}
case REFUNDING_PAYMENT -> {
saga.transitionTo(SagaState.COMPENSATING_ORDER);
commandGateway.send(new CancelOrderCommand(saga.getOrderId()));
}
case COMPENSATING_ORDER -> saga.transitionTo(SagaState.REJECTED);
}
sagaRepo.save(saga);
}
}
Detailed Comparison Matrix
| Architectural Criterion | Choreography | Orchestration |
|---|---|---|
| Communication Style | Event-Driven (Pub/Sub: “Order Placed”) | Command-Driven (Point-to-Point: “Debit Funds”) |
| Coordination | Distributed; embedded in each participant’s logic | Centralized in Saga Execution Coordinator (SEC) |
| Coupling | Loose invocation coupling, but high event semantic coupling | Centralized workflow coupling; participants remain decoupled from each other |
| Observability | Low: requires aggregating logs or Distributed Tracing (Zipkin/Jaeger) | High: complete execution history visible in the central Saga Log |
| Cyclic Dependencies | High risk of unintended circular event storms ($A \to B \to C \to A$) | Eliminated: workflow is modeled as a Directed Acyclic Graph (DAG) |
| Single Point of Failure (SPOF) | No central coordinating service | Orchestrator is critical (requires clustering and replicated storage) |
| Workflow Evolution | Adding steps requires reconfiguring subscriptions across multiple services | Adding steps requires modifying the workflow definition in the orchestrator |
🔴 Senior Level
1. Architectural Hazards: Pinball Spaghetti vs. The God Orchestrator
Choreography Hazard: The “Spaghetti Architecture” Pinball Trap
As the system expands beyond 5–10 services, choreography degenerates into the “pinball machine” anti-pattern: domain events bounce unpredictably between microservices. Modifying the schema of a single event (OrderCreatedEvent) breaks multiple downstream subscribers. Circular dependency cycles emerge, turning microservices into a tangled, distributed monolith.
Orchestration Hazard: Smart Orchestrator, Anemic Microservices
The most prevalent orchestration failure is absorbing domain business logic into the orchestrator. Microservices degenerate into passive CRUD workers, while the orchestrator becomes a bloated “God Object” that must be recompiled and redeployed whenever discounting formulas or validation rules change. Rule of Thumb: The orchestrator manages workflow routing, state transitions, and timeouts only. Domain decisions (e.g., how inventory is allocated or discounts calculated) remain strictly within individual domain services.
2. Message Racing & Out-of-Order Delivery
In asynchronous choreography, message delivery order is not guaranteed:
- A downstream delivery service may receive
OrderCancelledEvent(compensation) before the delayedOrderCreatedEventarrives due to network latency or Kafka consumer partition rebalancing. - Solution — State-Based Idempotency: If a cancellation arrives for an unknown entity, the service persists a tombstone record:
Order { id: 123, status: CANCELLED_PREMATURELY }. When the delayedCreateOrdercommand subsequently arrives, the service inspects the state, recognizes the cancellation tombstone, and discards the creation.
Race Condition Sequence:
1. [Broker] ────OrderCancelled──► [Delivery] (Created with status CANCELLED_PREMATURELY)
2. [Broker] ────CreateDelivery──► [Delivery] (Cancellation detected ──► Skipped!)
3. Production Durable Workflow Engines
Writing home-grown orchestrators using Java switch-case constructs is prone to state corruption during ungraceful process termination. Production-grade systems rely on dedicated durable execution engines:
- Temporal.io / Cadence:
- Code-as-Configuration: Sagas are written as standard, deterministic Java code (
Workflow.sleep(), loops,try-catch). - Powered by Event Sourcing: The engine intercepts activity calls, logs execution history, and deterministically reconstructs thread state line-by-line upon node failure.
- Code-as-Configuration: Sagas are written as standard, deterministic Java code (
- Camunda 7 / 8 (Zeebe):
- BPMN 2.0 Workflows: Business processes are modeled visually with native support for Compensation Boundary Events, SLA timers, and human escalation tasks.
- Zeebe partitions event streams via Raft consensus, delivering high throughput.
- Axon Framework:
- Provides native
@Sagacomponents with declarative key-association indexing (associationProperty = "orderId").
- Provides native
4. Pragmatic Hybrid Architecture
Mature enterprise systems employ a hybrid model:
- Orchestration inside Bounded Contexts: Within an Order Management domain, complex multi-step checkouts (credit validation, inventory reservations, fraud analysis) are orchestrated via an internal SEC.
- Choreography across Bounded Contexts: Upon saga completion, the Order domain publishes a public domain event (
OrderConfirmedEvent), which external bounded contexts (Analytics, Notifications, Recommendations) consume independently.
4 Tricky Questions
1. What happens if the orchestrator node crashes (kill -9) midway through an executing Saga?
Answer:
- A properly designed orchestrator persists state transitions to a durable Saga Log prior to dispatching commands (or relies on an Event Sourcing engine like Temporal).
- When the failed node restarts (or a surviving cluster replica assumes partition ownership):
- It reloads the last confirmed state from the Saga Log.
- It assesses the status of in-flight commands via polling or timeout expiration.
- It re-emits pending commands. Because downstream participant services must be strictly idempotent, duplicate commands do not corrupt business data.
- Without a persistent Saga Log, a crashed orchestrator leaves the distributed transaction permanently stranded in an inconsistent state.
2. Why are step timeouts (SLAs) and escalation significantly harder to implement in Choreography than in Orchestration?
Answer:
- In Orchestration, timeout management is centralized: the orchestrator arms a durable timer (e.g., Temporal’s
Workflow.await(Duration.ofMinutes(5))or a database scheduler). If a response is not received before deadline expiration, it automatically triggers compensating transactions. - In Choreography, there is no centralized owner of workflow time:
- Either every participating service must implement complex custom timeout schedulers and emit
StepTimeoutEvents. - Or an auxiliary monitoring service must be built to track SLAs across topics, which essentially recreates a fragile, decentralized orchestrator.
- Either every participating service must implement complex custom timeout schedulers and emit
3. What is a “Phantom Compensation” and how do you protect against it?
Answer:
- A Phantom Compensation occurs when a compensating action executes for an operation that was never actually applied on the target service.
- Example: An orchestrator issues
ReserveInventory, but the request drops in network transit. The orchestrator times out and sendsReleaseInventory. The inventory service receivesReleaseInventory, increments stock by +1, and later receives the delayed originalReserveInventorycommand. - Protection:
- Enforce globally unique
SagaIdandTransactionIdtokens across all steps. - Verify state preconditions: a compensation can only execute if the forward action was applied. If received prior to the forward action, the record is flagged as
CANCELLED, suppressing subsequent execution of the forward action.
- Enforce globally unique
4. How does network latency compare between Choreography and Orchestration across a 4-step linear saga?
Answer:
- In Choreography, messages travel point-to-point in a direct chain: $A \to B \to C \to D$. Total latency equals: \(T_{total} = T_{A} + RTT(A \to B) + T_{B} + RTT(B \to C) + T_{C} + RTT(C \to D) + T_{D}\)
- In Orchestration, each step requires two-way communication with the coordinator: $O \to A \to O \to B \to O \to C \to O \to D \to O$. Round-trip hops double: \(RTT_{total} = 2 \times N \times RTT\)
- However, Orchestration enables parallel step execution (e.g., running fraud detection and inventory reservation concurrently), whereas Choreography requires complex Fan-Out/Fan-In event aggregation topologies to synchronize parallel steps.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
“Choreography and Orchestration are the two coordination strategies for Sagas. Choreography is decentralized and event-driven: services publish and consume domain events over Kafka without a central coordinator, offering loose invocation coupling for simple 2–3 service flows. Orchestration is centralized and command-driven: a dedicated coordinator or durable workflow engine (Temporal, Camunda) manages an explicit state machine and Saga Log. Orchestration is the industry standard for enterprise business processes (4+ services) because it avoids ‘spaghetti’ event loops, provides end-to-end visibility, handles timeouts cleanly, and enables parallel step execution.”
Empirical Decision Rule
- Up to 3 services with simple linear flow $\to$ Choreography.
- 4+ services, complex branching, parallel steps, strict SLA timeouts, or regulatory audit requirements $\to$ Orchestration (Temporal, Camunda, Zeebe).
- Enterprise Multi-Domain Systems $\to$ Hybrid (Orchestration inside Bounded Contexts, Choreography between Bounded Contexts).
Red Flags (What NOT to Say)
- ❌ “An orchestrator is just a Spring
@Servicewith atry-catchwrapping synchronous REST calls.” — A synchronous REST orchestrator without a persistent Saga Log loses all transaction state upon crashing. - ❌ “In choreography, services are completely decoupled and have zero knowledge of each other.” — Services are tightly coupled to the semantics, payload schemas, and timing of peer events.
- ❌ “Orchestration is an anti-pattern because microservices must always be fully decentralized.” — Extreme decentralization in complex workflows causes catastrophic event pinballing; orchestration is the established enterprise standard for multi-step sagas.
Related Topics
- What is Saga Pattern and When to Use It — Saga pattern fundamentals
- How to Implement Distributed Transactions in Microservices — Distributed transaction design
- What are Compensating Transactions — Designing compensations & idempotency
- What tools are used for microservices orchestration — Temporal, Camunda, and Zeebe deep-dive