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" eventEvent 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
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env python3
import sys
import json
def main():
# Your implementation here
for line in sys.stdin:
msg = json.loads(line)
print(json.dumps(msg), flush=True)
if __name__ == "__main__":
main()