Choreography vs Orchestration
debt(d8/e8/b8/t7)
Closest to 'silent in production until users hit it' (d9), scored d8. The detection_hints note automated=no and tools like temporal/conductor/laravel-workflow only help if you've already chosen orchestration — they don't detect the wrong choice. Code patterns (scattered timeout handling, duplicated compensation logic) are only visible through careful code review. The wrong pattern choice is silent until a business flow fails or becomes undebuggable in production, hence near d9 but scored d8 because experienced reviewers can spot the structural warning signs during review.
Closest to 'cross-cutting refactor across the codebase' (e7), scored e8. The quick_fix is a conceptual guide, not a one-line fix. Switching from choreography to orchestration (or vice versa) for a complex workflow means rewriting event handlers into orchestrator steps (or vice versa), redistributing business logic across services, adding/removing distributed tracing, rewriting compensation logic, and potentially introducing new infrastructure (e.g. Temporal, Conductor). This touches multiple services and message schemas — close to architectural rework (e9) but slightly below because it's scoped to a specific workflow rather than rewriting the entire system.
Closest to 'defines the system's shape' (b9), scored b8. The why_it_matters explicitly states this choice 'defines how complex business workflows are implemented, debugged, and evolved.' The applies_to covers web, cli, and queue-worker contexts. Once chosen and implemented, the pattern shapes every subsequent workflow addition, the observability tooling selected, the failure-handling approach, and how teams reason about distributed state. It has strong gravitational pull (b7) bordering on system-defining (b9), scored b8 because it applies per-workflow rather than globally restructuring all code.
Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7). The misconception is explicit: 'Choreography is always better because it's more decoupled' — this is a widely held and reasonable-sounding belief that leads developers to choose choreography and then discover they've created an invisible debugging nightmare. The common mistakes reinforce this: choreography without distributed tracing, unhandled failures, and mixed patterns. A competent developer applying standard decoupling intuition will default to choreography and be seriously wrong in many production scenarios.
Also Known As
TL;DR
Explanation
In orchestration, a central service (the orchestrator) calls each participant service in order, handles failures, and manages state. Think of a conductor directing musicians. In choreography, each service subscribes to events and reacts independently — no central coordinator. Think of dancers reacting to music. Orchestration is easier to understand and debug; choreography is more decoupled and resilient but harder to trace. Both approaches are used together in real systems.
Diagram
flowchart TD
subgraph Orchestration
ORCH[Orchestrator] -->|1 call| INV[Inventory]
ORCH -->|2 call| PAY[Payment]
ORCH -->|3 call| SHIP[Shipping]
end
subgraph Choreography
E1[OrderPlaced event] --> INV2[Inventory<br/>listens]
INV2 -->|StockReserved event| PAY2[Payment<br/>listens]
PAY2 -->|PaymentDone event| SHIP2[Shipping<br/>listens]
end
style ORCH fill:#6e40c9,color:#fff
style BUS fill:#6e40c9,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Choreography without distributed tracing — impossible to follow a business flow across services.
- Orchestration where the orchestrator contains business logic that should live in the participant services.
- Not handling failures in choreography — if one service's reaction fails, the event is processed but the side effect is missing.
- Mixing both patterns without clear boundaries — choose one per workflow, not both for the same flow.
Code Examples
// Orchestration overloaded with business logic:
class OrderOrchestrator {
public function processOrder(Order $o): void {
// Orchestrator making domain decisions it should not:
$discount = $o->total > 100 ? 0.1 : 0; // Business rule in wrong place
$inventory->reserve($o, $discount); // Too much knowledge
$payment->charge($o->total * (1 - $discount));
}
}
// Choreography — each service reacts to events independently:
// OrderService publishes: OrderCreated
// InventoryService listens: reserves stock, publishes StockReserved
// PaymentService listens to StockReserved: charges, publishes PaymentConfirmed
// ShippingService listens to PaymentConfirmed: creates shipment
// Each service knows only its own domain — no central coordinator