Let’s walk through a simple example of a choreography pattern using a hypothetical e-commerce system. We’ll use a combination of Python classes to simulate microservices and their event-driven interactions.

Scenario

In this example, we’ll have four services:

  1. OrderService: Manages customer orders.
  2. InventoryService: Checks and updates product inventory.
  3. PaymentService: Processes payments.
  4. ShippingService: Arranges shipping for the order.

Each service will emit and listen to specific events to perform its tasks. We’ll use a simple event bus to simulate the event-driven communication.

Implementation

Event Bus

First, we need an event bus to handle event subscriptions and publishing.

class EventBus:
    def __init__(self):
        self.subscribers = {}
 
    def subscribe(self, event_type, handler):
        if event_type not in self.subscribers:
            self.subscribers[event_type] = []
        self.subscribers[event_type].append(handler)
 
    def publish(self, event_type, data):
        if event_type in self.subscribers:
            for handler in self.subscribers[event_type]:
                handler(data)
 
event_bus = EventBus()

Services

Next, we implement the services, each with its own event handling methods.

class OrderService:
    def __init__(self, event_bus):
        self.event_bus = event_bus
        self.event_bus.subscribe("OrderCreated", self.handle_order_created)
 
    def create_order(self, order_data):
        print(f"OrderService: Creating order {order_data['order_id']}")
        self.event_bus.publish("OrderCreated", order_data)
 
    def handle_order_created(self, order_data):
        print(f"OrderService: Order {order_data['order_id']} created")
 
class InventoryService:
    def __init__(self, event_bus):
        self.event_bus = event_bus
        self.event_bus.subscribe("OrderCreated", self.handle_order_created)
        self.event_bus.subscribe("InventoryChecked", self.handle_inventory_checked)
 
    def handle_order_created(self, order_data):
        print(f"InventoryService: Checking inventory for order {order_data['order_id']}")
        self.event_bus.publish("InventoryChecked", order_data)
 
    def handle_inventory_checked(self, order_data):
        print(f"InventoryService: Inventory checked for order {order_data['order_id']}")
 
class PaymentService:
    def __init__(self, event_bus):
        self.event_bus = event_bus
        self.event_bus.subscribe("InventoryChecked", self.handle_inventory_checked)
        self.event_bus.subscribe("PaymentProcessed", self.handle_payment_processed)
 
    def handle_inventory_checked(self, order_data):
        print(f"PaymentService: Processing payment for order {order_data['order_id']}")
        self.event_bus.publish("PaymentProcessed", order_data)
 
    def handle_payment_processed(self, order_data):
        print(f"PaymentService: Payment processed for order {order_data['order_id']}")
 
class ShippingService:
    def __init__(self, event_bus):
        self.event_bus = event_bus
        self.event_bus.subscribe("PaymentProcessed", self.handle_payment_processed)
        self.event_bus.subscribe("ShippingArranged", self.handle_shipping_arranged)
 
    def handle_payment_processed(self, order_data):
        print(f"ShippingService: Arranging shipping for order {order_data['order_id']}")
        self.event_bus.publish("ShippingArranged", order_data)
 
    def handle_shipping_arranged(self, order_data):
        print(f"ShippingService: Shipping arranged for order {order_data['order_id']}")

Running the Choreography

Now we create instances of the services and simulate placing an order.

# Instantiate services
order_service = OrderService(event_bus)
inventory_service = InventoryService(event_bus)
payment_service = PaymentService(event_bus)
shipping_service = ShippingService(event_bus)
 
# Simulate placing an order
order_data = {"order_id": 1, "product_id": 101, "quantity": 2}
order_service.create_order(order_data)

Output

When you run the above code, you should see the following output, showing the flow of events and how each service reacts:

OrderService: Creating order 1
OrderService: Order 1 created
InventoryService: Checking inventory for order 1
InventoryService: Inventory checked for order 1
PaymentService: Processing payment for order 1
PaymentService: Payment processed for order 1
ShippingService: Arranging shipping for order 1
ShippingService: Shipping arranged for order 1

This example illustrates the choreography model, where each service listens for specific events, processes them, and potentially triggers new events, all without a central coordinator.