Definition
Picture two ways to coordinate a wedding. In the first, there’s a wedding planner who calls the caterer, then calls the florist, then calls the DJ - each instruction depends on the previous step completing. The planner is in charge and knows the entire sequence. In the second approach, everyone just watches for signals: when the ceremony ends, the caterer automatically starts serving, the DJ sees people sitting and starts dinner music, the photographer notices food being served and moves to candid shots. No one’s in charge; everyone just knows their cue. That’s orchestration versus choreography.
In orchestration, a central service (the orchestrator) explicitly controls the workflow. It knows all the steps, calls each service in order, handles errors, and manages the overall process. If you need to change the process, you change the orchestrator. The upside is visibility - you can see the entire flow in one place. The downside is coupling - that orchestrator becomes critical infrastructure, and it needs to know about every service it coordinates.
Choreography flips this around. Instead of a central controller, services communicate through events. “Order placed!” says the Order service, and it doesn’t care who’s listening. Payment service hears it and charges the card, emitting “Payment completed!” Inventory service hears that and reserves stock. Each service minds its own business and reacts to events it cares about. This is more resilient (no single point of failure) and more flexible (add new services by just subscribing to events), but harder to debug because the workflow is implicit, spread across many services rather than visible in one place.
Example
These patterns shape how modern systems handle complex workflows:
E-commerce Order Processing (Orchestration): Amazon’s order fulfillment likely uses orchestration internally. An orchestrator service manages the sequence: validate cart β process payment β reserve inventory β create shipping label β notify warehouse β send confirmation email. Each step must succeed before the next begins, and the orchestrator handles retries, timeouts, and compensation if something fails. It’s complex, but Amazon needs deterministic, traceable order processing.
E-commerce Order Processing (Choreography): A startup might use choreography instead. The Order service emits “OrderCreated.” The Payment service listens, charges the card, emits “PaymentSucceeded.” Inventory listens to that, reserves stock, emits “InventoryReserved.” Email service listens to multiple events and sends appropriate notifications. Adding a loyalty points service is easy - just subscribe to “PaymentSucceeded” events. No central service knows the full flow.
Uber Ride Coordination (Hybrid): Uber uses both. Finding a driver might be choreography - events flow through the system as drivers respond to ride requests. But the actual trip lifecycle (pickup β ride β dropoff β payment) is likely orchestrated because it’s a strict sequence with many edge cases (driver cancels, passenger doesn’t show, route changes) that need centralized handling.
Healthcare Insurance Claims: Insurance claim processing is typically orchestrated. A claim must go through validation β coverage check β prior authorization β provider verification β payment calculation β disbursement in a specific order with regulatory requirements at each step. The orchestrator ensures compliance and provides audit trails.
Social Media Engagement: When you post on Instagram, choreography kicks in. The post service emits “PostCreated.” The feed service adds it to followers’ feeds. The notification service alerts mentioned users. The analytics service counts impressions. The recommendation service updates its models. These all happen independently, in parallel, without any central coordinator.
Analogy
The Orchestra vs. Jazz Band: An orchestra has a conductor (orchestrator) who explicitly cues each section: violins start, then brass joins, then percussion. Everyone follows the conductor’s baton. A jazz band has no conductor - each musician listens to the others and improvises, reacting to what they hear. Both make beautiful music, but the coordination model is completely different.
The Assembly Line vs. the Beehive: An assembly line is orchestrated - a foreman schedules each station’s work, and cars move through in sequence. A beehive is choreographed - no queen bee tells individual workers where to go. Bees smell pheromones (events) and independently decide to gather nectar, build comb, or defend the hive based on what they detect.
The Military Operation vs. the Marketplace: A military operation is orchestrated - commanders issue specific orders, each unit reports back, and the next phase begins only when objectives are confirmed. A marketplace is choreographed - vendors set up, customers browse, transactions happen, all based on participants reacting to each other without anyone controlling the whole thing.
The Relay Race vs. the Marathon: In a relay race, handoffs are orchestrated - runner 1 must pass to runner 2, who must pass to runner 3, in sequence. In a marathon, everyone runs independently - you start when the gun fires (event), run your own race, and finish when you finish. Adding another runner to a marathon is trivial; adding to a relay changes the whole team structure.
Code Example
// ORCHESTRATION - Central controller
class OrderOrchestrator {
async processOrder(order) {
try {
// Orchestrator explicitly calls each step
const payment = await paymentService.charge(order);
const inventory = await inventoryService.reserve(order.items);
const shipping = await shippingService.schedule(order);
return { payment, inventory, shipping };
} catch (error) {
// Orchestrator handles rollback
await this.compensate(order, error);
}
}
}
// CHOREOGRAPHY - Event-driven
class OrderService {
async createOrder(order) {
// Just emit event, don't orchestrate
await eventBus.publish('OrderPlaced', order);
}
}
class PaymentService {
constructor() {
// Each service listens independently
eventBus.subscribe('OrderPlaced', this.handleOrder);
}
async handleOrder(order) {
await this.charge(order);
await eventBus.publish('PaymentCompleted', order);
}
}
class InventoryService {
constructor() {
eventBus.subscribe('PaymentCompleted', this.handlePayment);
}
async handlePayment(order) {
await this.reserve(order.items);
await eventBus.publish('InventoryReserved', order);
}
}