Tale of Software Architect(ure): Part 18 (Event Driven Architecture)
Saiful Islam Rasel
Senior Engineer, SDE @ bKash | Ex: AsthaIT | Sports Programmer | Problem Solver | FinTech | Microservice | Java | Spring-boot | C# | .NET | PostgreSQL | DynamoDB | JavaScript | TypeScript | React.js | Next.js | Angular
Story:
Once upon a time there was a popular marketplace called "Cloud Bazaar." Vendors in Cloud Bazaar worked around the clock to fulfill orders and delight their customers. However, as the city grew, so did the number of orders, making it difficult for vendors to keep up. The once-efficient market was bogged down by delays and dependencies between services.
To solve this problem, the president of Cloud Bazaar introduced a new communication system called "Order & Chill". In this system, every vendor and service would operate independently but would stay connected through a network of messengers called "Brokers."
Here’s how it worked: When a customer ordered something, the "Order Desk" would place an "Order Placed" message on the Broker’s board. This board was a special place where all services could see the new events and pick up tasks that concerned them.
With this new system, each service could act as soon as they saw the relevant message on the board without waiting for each other. No one was held up by dependencies, and any new team, like a Loyalty Points service, could simply start listening for events that mattered to them without affecting anyone else.
Event Driven Architecture:
Event-driven architecture (EDA) is a software architecture pattern in which system components communicate through events instead of direct requests. It’s ideal for systems that need to respond to a series of unpredictable events in real-time or systems that not need immediate response, allowing for greater flexibility and scalability.
Key Components of Event-Driven Architecture
Example Scenario: E-commerce Order Processing
Let's consider an online retail platform to illustrate event-driven architecture:
Order Placed (Event Producer)
Event Broker (e.g., Kafka or RabbitMQ)
Event Consumers
Further Events and Reactions
Context
Modern software systems often require scalability, real-time responsiveness, and adaptability to handle diverse, unpredictable events. These systems, such as e-commerce platforms, financial trading systems, and IoT applications, need to process and respond to user actions or external triggers promptly without being bound to a strict sequence. Traditional architectures, like monolithic or request-driven designs, can struggle with such real-time demands and can become complex and rigid as they grow, especially when new features are added.
Problem
Solution
Event-Driven Architecture (EDA) introduces a model where:
Sample Pseudocode:
Step 1: Define Event Data Structure
First, define a basic structure for an event, which will contain event type and event data.
# Event structure
class Event:
def __init__(self, type, data):
self.type = type
self.data = data
Step 2: Event Broker
The broker will manage events and route them to the appropriate consumers.
# Event Broker for managing events
class EventBroker:
def __init__(self):
self.subscribers = {} # Dictionary to store event type and corresponding 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):
if event.type in self.subscribers:
for handler in self.subscribers[event.type]:
handler(event)
Step 3: Define Event Producers
The OrderService acts as the producer when an order is placed. It publishes an Order Placed event.
# Order Service that produces events
class OrderService:
def __init__(self, broker):
self.broker = broker
def place_order(self, order_data):
print("Order placed:", order_data)
event = Event("OrderPlaced", order_data)
self.broker.publish(event)
Step 4: Define Event Consumers
Each service subscribes to specific events and defines how to handle them.
# Inventory Service - subscribes to OrderPlaced event
class InventoryService:
def __init__(self, broker):
broker.subscribe("OrderPlaced", self.update_inventory)
def update_inventory(self, event):
print("Inventory updated for order:", event.data)
# Billing Service - subscribes to OrderPlaced event
class BillingService:
def __init__(self, broker):
broker.subscribe("OrderPlaced", self.process_payment)
def process_payment(self, event):
print("Payment processed for order:", event.data)
# Publish another event after billing is complete
order_billed_event = Event("OrderBilled", event.data)
broker.publish(order_billed_event)
# Shipping Service - subscribes to OrderPlaced and OrderBilled events
class ShippingService:
def __init__(self, broker):
broker.subscribe("OrderPlaced", self.prepare_shipping)
broker.subscribe("OrderBilled", self.ship_order)
def prepare_shipping(self, event):
print("Preparing shipment for order:", event.data)
def ship_order(self, event):
print("Shipping order:", event.data)
# Notification Service - subscribes to OrderBilled event
class NotificationService:
def __init__(self, broker):
broker.subscribe("OrderBilled", self.send_confirmation)
def send_confirmation(self, event):
print("Confirmation email sent for order:", event.data)
Step 5: Putting It All Together
Initialize the broker, services, and simulate an order being placed.
# Initialize the event broker
broker = EventBroker()
# Initialize services and subscribe them to events
inventory_service = InventoryService(broker)
billing_service = BillingService(broker)
shipping_service = ShippingService(broker)
notification_service = NotificationService(broker)
# Simulate placing an order
order_service = OrderService(broker)
order_data = {"order_id": 123, "items": ["item1", "item2"], "total": 100.0}
order_service.place_order(order_data)
Summary:
Event-driven architecture (EDA) is a software design pattern where components communicate through events rather than direct calls. In this architecture, event producers generate events when something significant happens (e.g., a user places an order), and event consumers listen for these events to perform relevant actions (e.g., processing payment, updating inventory). Events are routed through an event broker, which stores and delivers them to all interested consumers.
EDA is ideal for building scalable, flexible systems, as each component can operate independently and respond in real time, allowing for efficient handling of complex workflows without creating bottlenecks. New consumers can easily be added without altering existing ones, supporting system growth and flexibility.