What is the difference between choreography and orchestration in Saga
// How the orchestrator differs from a regular method: // 1. Persists state (survives restarts) // 2. Handles asynchronous events // 3. Has built-in retry and compensation
Junior Level
Choreography and orchestration are two ways to implement the Saga pattern.
Choreography — there is no central controller. Each service listens to events from other services and reacts.
Orchestration — there is a central orchestrator that tells each service what to do.
// How the orchestrator differs from a regular method: // 1. Persists state (survives restarts) // 2. Handles asynchronous events // 3. Has built-in retry and compensation
Choreography:
Order Service -> "OrderCreated" -> Payment Service -> "PaymentDone" -> Shipping Service
Orchestration:
Orchestrator
/ | \
Order Payment Shipping
↓ ↓ ↓
"create" "charge" "ship"
Middle Level
Choreography — Event-driven
// Order Service
@EventListener
public void on(OrderCreatedEvent event) {
paymentService.reserve(event.orderId());
eventPublisher.publish(new PaymentReservedEvent(event.orderId()));
}
// Payment Service
@EventListener
public void on(PaymentReservedEvent event) {
shippingService.ship(event.orderId());
eventPublisher.publish(new ShippingCompletedEvent(event.orderId()));
}
// Compensation
@EventListener
public void on(PaymentFailedEvent event) {
orderService.cancel(event.orderId());
eventPublisher.publish(new OrderCancelledEvent(event.orderId()));
}
Orchestration — Central coordinator
@Component
public class OrderOrchestrator {
public void processOrder(Order order) {
try {
orderService.create(order);
paymentService.charge(order);
inventoryService.reserve(order);
shippingService.ship(order);
} catch (PaymentException e) {
orderService.cancel(order);
// throw e — exception propagates to the HTTP controller,
// which returns 500 with a description of the compensation error.
throw e;
} catch (InventoryException e) {
paymentService.refund(order);
orderService.cancel(order);
// throw e — exception propagates to the HTTP controller,
// which returns 500 with a description of the compensation error.
throw e;
}
}
}
Comparison
| Choreography | Orchestration |
|---|---|
| No central control | Central orchestrator |
| Event-driven | Command-driven |
| Hard to trace the flow | Easy to understand the flow |
| Cyclic dependencies | No cyclic dependencies |
| With 2-3 services the event flow is still manageable. | With 5+ services connections grow quadratically, and without an orchestrator it’s impossible to understand who calls whom. |
Event-driven = services react to events (someone published a fact). Command-driven = orchestrator explicitly tells each service “do X”.
Common mistakes
- Cyclic dependencies in choreography:
Service A -> event -> Service B -> event -> Service A -> infinite loop!
Senior Level
Architectural Trade-offs
Choreography:
Pros:
- No single point of failure
- Loose coupling between services
- Natural event-driven style
Cons:
- Hard to debug (flow is not obvious)
- Risk of cyclic dependencies
- Difficult to add a new step
- No single place for monitoring
Orchestration:
Pros:
- Clear execution flow
- Easy to add a step
- Centralized monitoring
- No cyclic dependencies
Cons:
- Orchestrator is a bottleneck
- Orchestrator is a single point of failure
- Orchestrator knows too much about other services
Production Experience
When to choose choreography:
- 2-3 services
- Simple logic
- Event-driven infrastructure already exists
- Maximum fault tolerance is required
When to choose orchestration:
- 5+ services
- Complex logic with conditions
- Monitoring and tracing are needed
- Different teams for each service
Real-world orchestrator (Axon Framework):
@Saga
public class OrderSaga {
@Autowired
private transient CommandGateway commandGateway;
@StartSaga
@SagaEventHandler(associationProperty = "orderId")
public void handle(OrderCreatedEvent event) {
commandGateway.send(new ReserveInventoryCommand(event.getOrderId()));
}
@SagaEventHandler(associationProperty = "orderId")
public void handle(InventoryReservedEvent event) {
commandGateway.send(new ProcessPaymentCommand(event.getOrderId()));
}
@SagaEventHandler(associationProperty = "orderId")
public void handle(PaymentCompletedEvent event) {
commandGateway.send(new CompleteOrderCommand(event.getOrderId()));
end();
}
@SagaEventHandler(associationProperty = "orderId")
public void handle(PaymentFailedEvent event) {
commandGateway.send(new CancelOrderCommand(event.getOrderId()));
end();
}
}
Best Practices
✅ Choreography for simple Sagas (2-3 steps)
✅ Orchestration for complex Sagas (5+ steps)
✅ Monitor Sagas in both approaches
✅ Implement idempotency
❌ Don't mix approaches within one Saga
❌ Don't create cyclic dependencies in choreography
❌ Don't make the orchestrator too "smart"
Interview Cheat Sheet
Must know:
- Choreography — event-driven, no central control, services react to events
- Orchestration — command-driven, central coordinator manages the flow
- Choreography: no SPOF, but hard to debug, risk of cyclic dependencies
- Orchestration: clear flow, easy to monitor, but orchestrator is a bottleneck
- Choreography for simple Sagas (2-3 steps), orchestration for complex (5+)
- In choreography each service only knows about neighbors; in orchestration the orchestrator knows about all
- Both approaches require idempotency and compensations
Common follow-up questions:
- How to avoid cyclic dependencies in choreography? Design a directed event graph, use orchestration for complex logic.
- What if the orchestrator crashes? State is persisted — after restart it continues from the point of failure.
- Can the approaches be combined? Not within a single Saga, but different Sagas in the system can use different approaches.
- How to test an orchestrator? Unit tests for logic + integration tests with mocked services.
Red flags (DO NOT say):
- “Choreography is simpler for 10 services” — no, the flow becomes unreadable
- “The orchestrator cannot fail” — it can, state persistence is needed
- “There is no coupling in choreography” — there is, through events
- “Orchestrator = anti-pattern, violates loose coupling” — it’s a trade-off, not an absolute
Related topics:
- [[1. What is Saga pattern and when to use it]]
- [[3. How to implement distributed transactions in microservices]]
- [[4. What are compensating transactions]]
- [[15. How to organize communication between microservices]]
- [[26. What tools are used for microservice orchestration]]