Choreography vs Orchestration: Coordinating Microservices Workflows

Philip Rehberger Aug 7, 2026 5 min read

Pick between centralized orchestration and event-driven choreography. Includes failure handling and visibility tradeoffs.

Choreography vs Orchestration: Coordinating Microservices Workflows

When a business process spans multiple services — placing an order, provisioning a user, fulfilling a request — something has to decide what happens in what order. The two answers are orchestration and choreography. They produce the same end state, but they look completely different at runtime, fail differently, and are observable in completely different ways.

Picking between them is one of the early decisions that compounds. Orchestrated systems and choreographed systems are hard to refactor into one another, so the choice deserves more attention than it usually gets.

The Two Patterns

Orchestration. A central coordinator — a workflow engine, a state machine, or a "saga orchestrator" — calls each service in turn and tracks the result. The workflow lives in the orchestrator.

Orchestrator → Inventory: reserve
Inventory → Orchestrator: reserved
Orchestrator → Payment: charge
Payment → Orchestrator: charged
Orchestrator → Shipping: schedule
Shipping → Orchestrator: scheduled
Orchestrator → Notifications: send

Choreography. No coordinator. Each service emits events when it completes, and other services listen for the events they care about.

Order Service     → OrderPlaced event
Inventory Service ← OrderPlaced; emits InventoryReserved
Payment Service   ← InventoryReserved; emits PaymentCharged
Shipping Service  ← PaymentCharged; emits ShipmentScheduled
Notifications     ← ShipmentScheduled

Same flow. Same outcome. Very different runtime shape.

How They Compare in Practice

Visibility. Orchestration wins. The workflow is in one place — usually a state machine you can read and a UI you can watch. Tracing a stuck order means looking at the orchestrator. Choreography spreads the workflow across event subscriptions, and answering "where is order 1234 stuck?" requires correlating traces across every service.

Coupling. Choreography wins. Services do not know about each other; they know about events. Adding a new service that reacts to OrderPlaced is a deploy of one service. Orchestration requires updating the orchestrator's workflow definition.

Failure handling. Orchestration wins. The orchestrator knows what step it is on, what to retry, and when to give up. In choreography, retries and compensations are owned by each individual subscriber, and the "is this workflow still alive?" question has no central answer.

Team independence. Choreography wins. Each team owns its service and its event handlers. With orchestration, the orchestrator becomes a coordination point — changes to the workflow require coordination across teams.

Operational complexity. Mixed. Choreography needs a reliable broker (Kafka, NATS, RabbitMQ) and discipline about event schemas. Orchestration needs a workflow engine or a state machine you maintain. Neither is free.

Concrete Example: Order Placement

Orchestrated version with Temporal:

export async function placeOrderWorkflow(order: Order): Promise<void> {
  await activities.reserveInventory(order);
  try {
    await activities.chargePayment(order);
  } catch (err) {
    await activities.releaseInventory(order);
    throw err;
  }
  try {
    await activities.scheduleShipment(order);
  } catch (err) {
    await activities.refundPayment(order);
    await activities.releaseInventory(order);
    throw err;
  }
  await activities.sendConfirmation(order);
}

You can read the workflow top to bottom. Failure handling is explicit. State is durable: if Temporal restarts, the workflow continues from where it left off.

Choreographed version with events:

// In Inventory Service
class HandleOrderPlaced
{
    public function handle(OrderPlaced $event): void
    {
        $this->inventory->reserve($event->items);
        event(new InventoryReserved($event->orderId));
    }
}

// In Payment Service
class HandleInventoryReserved
{
    public function handle(InventoryReserved $event): void
    {
        try {
            $charge = $this->payments->charge($event->orderId);
            event(new PaymentCharged($event->orderId, $charge->id));
        } catch (PaymentDeclined $e) {
            event(new PaymentFailed($event->orderId, $e->getMessage()));
        }
    }
}

// In Inventory Service — compensation
class HandlePaymentFailed
{
    public function handle(PaymentFailed $event): void
    {
        $this->inventory->release($event->orderId);
    }
}

No file describes the whole workflow. The order of operations is the order of event subscriptions. Adding a new step is a new subscriber; you do not edit a central definition.

Common Mixed Pattern

Most large systems are not purely one or the other. A common pattern: orchestration at the workflow level, choreography for downstream side effects.

PlaceOrderSaga (orchestrated)
  → Inventory.reserve
  → Payment.charge
  → Shipping.schedule
    emits OrderShipped event (choreographed)
      → Notifications listens
      → Analytics listens
      → Recommendations listens

Critical-path business transactions are orchestrated for visibility and atomicity. Reactions that do not need to be coordinated — analytics, notifications, recommendation training — are choreographed.

When to Choose Each

Orchestration fits when:

  • The workflow has compensating actions that must run in a specific order on failure
  • You need a single answer to "where is this transaction now?"
  • The workflow is long-running and you need to recover from process restarts
  • Compliance or auditing requires a clear, persistent record of every step

Choreography fits when:

  • Steps are largely independent and parallelizable
  • Teams need to add reactions without coordinating with the workflow owner
  • The number of downstream effects is large or growing
  • You already have a strong event backbone and operational comfort with brokers

If you are unsure, orchestration is the safer default for new systems. It surfaces problems earlier and gives operations a single place to look during incidents. You can always extract pieces into choreographed reactions later. Going the other way — collapsing a choreographed mess back into an orchestrator — is much harder.

Anti-Patterns

  • Distributed orchestration through chained synchronous calls. Service A calls service B calls service C calls service D, each waiting on the next. This has the visibility problems of choreography and the coupling problems of orchestration.
  • Implicit choreography through database polling. Service B watches service A's tables for changes. This couples services through the database and breaks the moment A's schema changes.
  • Event ordering as workflow. Choreography that depends on events arriving in a specific order across services. Brokers do not guarantee global ordering. If the workflow needs ordering, you actually want orchestration.

Designing a multi-service workflow and not sure which way to lean? We help teams pick coordination models that match their team structure and operational maturity. scopeforged.com

Share this article

Related Articles

Need help with your project?

Let's discuss how we can help you build reliable software.