ARCHIVED from builddistributedsystem.com on 2026-04-28 — URL: https://builddistributedsystem.com/tracks/coordinator/tasks/task-19-3-2-choreography
TASK

Implementation

In a choreography-based saga, there is no central coordinator. Each service listens for events and triggers the next step by publishing its own events. Coordination happens through events, not a orchestrator.

Choreography flow:

Service A (Inventory)
  → Completes T1
  → Publishes "InventoryReserved" event
  → (if failure) Publishes "InventoryReservationFailed"

Service B (Payment)
  → Subscribes to "InventoryReserved"
  → On event, executes T2 (ChargePayment)
  → Publishes "PaymentCharged" event
  → (if failure) Publishes "PaymentFailed" event

Service C (Shipping)
  → Subscribes to "PaymentCharged"
  → On event, executes T3 (CreateShipment)
  → Publishes "ShipmentCreated" event
  → (if failure) Publishes "ShipmentFailed" event

Event structure:

// Forward path events:
{"type": "InventoryReserved", "saga_id": "saga42", "reservation_id": "r1", "timestamp": 1680123456}
{"type": "PaymentCharged", "saga_id": "saga42", "payment_id": "p1", "amount": 99.99, "timestamp": 1680123457}
{"type": "ShipmentCreated", "saga_id": "saga42", "shipment_id": "s1", "timestamp": 1680123458}

// Compensating events:
{"type": "PaymentFailed", "saga_id": "saga42", "reason": "insufficient_funds", "timestamp": 1680123457}
{"type": "InventoryReleased", "saga_id": "saga42", "reservation_id": "r1", "timestamp": 1680123458}

Example choreography execution:

// Client starts saga by calling InventoryService:
Request:  {"type": "reserve_inventory", "msg_id": 1, "saga_id": "saga42", "sku": "abc123", "quantity": 1}
Response: {"type": "reserve_inventory_ok", "in_reply_to": 1, "saga_id": "saga42", "reservation_id": "r1"}

// InventoryService publishes event, PaymentService picks it up:
{"type": "InventoryReserved", "saga_id": "saga42", "reservation_id": "r1"}

// PaymentService charges and publishes event:
{"type": "PaymentCharged", "saga_id": "saga42", "payment_id": "p1", "amount": 99.99}

// ShippingService creates shipment and publishes event:
{"type": "ShipmentCreated", "saga_id": "saga42", "shipment_id": "s1"}

Handling failures in choreography:

// If payment fails:
{"type": "PaymentFailed", "saga_id": "saga42", "reason": "insufficient_funds"}

// InventoryService subscribes to PaymentFailed and runs compensator:
{"type": "InventoryReleased", "saga_id": "saga42", "reservation_id": "r1"}

Sample Test Cases

Choreography completes successfullyTimeout: 5000ms
Input
{"src":"c0","dest":"event_bus","body":{"type":"init","msg_id":1,"services":["inventory","payment","shipping"]}}
{"src":"c1","dest":"inventory","body":{"type":"reserve_inventory","msg_id":2,"saga_id":"saga42","sku":"abc123","quantity":1}}
Expected Output
{"src": "event_bus", "dest": "c0", "body": {"type": "init_ok", "in_reply_to": 1, "msg_id": 0}}
Choreography handles payment failureTimeout: 5000ms
Input
{"src":"c0","dest":"event_bus","body":{"type":"init","msg_id":1,"services":["inventory","payment","shipping"]}}
{"src":"c1","dest":"inventory","body":{"type":"reserve_inventory","msg_id":2,"saga_id":"saga43","sku":"abc123","quantity":1,"fail_payment":true}}
Expected Output
{"src": "event_bus", "dest": "c0", "body": {"type": "init_ok", "in_reply_to": 1, "msg_id": 0}}

Hints

Hint 1
Each service publishes events when it completes its step
Hint 2
The next service subscribes to those events and reacts
Hint 3
No central coordinator: services communicate via events only
Hint 4
Example: InventoryService publishes "InventoryReserved", PaymentService subscribes and charges
Hint 5
Use a message broker or event bus to broadcast events
OVERVIEW

Theoretical Hub

Concept overview coming soon

Key Concepts

choreographyevent-driven architectureservice coordinationno central orchestratorevent publishing
main.py
python
Implement Choreography-Based Saga - The Coordinator | Build Distributed Systems