Question 2 · Section 17

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

Language versions: English Russian Ukrainian

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

  1. 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]]