Event-Driven Architecture — Events, Message Buses (2026)
In this tutorial, you'll learn event-driven architecture — how decoupled services communicate through events rather than direct requests, using message brokers like Kafka and RabbitMQ. Why does this matter? Synchronous request-response coupling creates chains of failures — if one service is down, every caller fails. Events break those chains, enabling resilient, scalable, and auditable systems. Real-world use: every major tech company (Uber, Netflix, LinkedIn) runs on event-driven architectures for critical workflows like order processing, fraud detection, and real-time analytics.
What Is Event-Driven Architecture?
Event-driven architecture (EDA) is a pattern where producers emit events that consumers react to, without the producer knowing who (if anyone) will consume them. Events are immutable records of something that happened — OrderCreated, PaymentProcessed, UserRegistered. A message broker (Kafka, RabbitMQ, AWS SQS/SNS) mediates the communication, providing durability, routing, and delivery guarantees.
graph LR
subgraph Producers
P1[Order Service]
P2[Payment Service]
P3[User Service]
end
subgraph Broker[Message Broker]
T1[Topic: orders]
T2[Topic: payments]
T3[Topic: users]
end
subgraph Consumers
C1[Inventory Service]
C2[Notification Service]
C3[Analytics Service]
C4[Audit Logger]
end
P1 -->|OrderCreated| T1
P2 -->|PaymentProcessed| T2
P3 -->|UserRegistered| T3
T1 --> C1
T1 --> C2
T2 --> C3
T3 --> C4
style Producers fill:#4A90D9,color:#fff
style Broker fill:#E67E22,color:#fff
style Consumers fill:#2ECC71,color:#fff
How Event-Driven Architecture Works
Event Types
Three distinct event types:
- Commands — a directive: "do this" (e.g.,
ReserveInventory). Always targets a specific consumer. - Events — a notification: "this happened" (e.g.,
OrderCreated). Zero or many consumers can react. - Queries — a request for data (less common in EDA, usually handled via synchronous APIs).
Message Brokers
Apache Kafka — durable, high-throughput, ordered partitions. Best for event streaming, audit logs, and replay scenarios:
from kafka import KafkaProducer, KafkaConsumer
import json
# Producer
producer = KafkaProducer(bootstrap_servers="localhost:9092")
event = {"type": "OrderCreated", "order_id": "123", "total": 99.99}
producer.send("orders", json.dumps(event).encode())
producer.flush()
# Consumer
consumer = KafkaConsumer("orders", bootstrap_servers="localhost:9092")
for message in consumer:
event = json.loads(message.value.decode())
print(f"Processing: {event['type']} — {event['order_id']}")
if event["type"] == "OrderCreated":
reserve_inventory(event["order_id"])
RabbitMQ — smart broker with flexible routing (direct, topic, fanout exchanges). Best for task queues and routing scenarios:
import pika
# Producer
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
channel = connection.channel()
channel.exchange_declare(exchange="orders", exchange_type="topic")
channel.basic_publish(
exchange="orders",
routing_key="order.created",
body=json.dumps({"order_id": "123"})
)
# Consumer
def callback(ch, method, properties, body):
event = json.loads(body)
print(f"Received: {event['order_id']}")
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(queue="inventory_queue", on_message_callback=callback)
channel.start_consuming()
Idempotency
Consumers must handle duplicate events safely. A consumer might receive the same event twice if the broker re-delivers. Use idempotency keys:
class OrderConsumer:
def __init__(self):
self._processed = set() # Track processed event IDs
def handle_order_created(self, event: dict) -> None:
event_id = event["id"]
if event_id in self._processed:
return # Already processed — skip
# Process the event
inventory.reserve(event["order_id"])
self._processed.add(event_id)
Ordering Guarantees
Kafka guarantees ordering within a partition. RabbitMQ guarantees ordering within a queue. For most systems, per-entity ordering (all events for order #123 in order) is sufficient and achievable via partition keys:
# Kafka: partition by order_id ensures ordering per order
producer.send("orders", key=b"order_123", value=event_bytes)
Data Flow
sequenceDiagram
participant OS as Order Service
participant K as Kafka
participant IS as Inventory Service
participant NS as Notification Service
OS->>K: Emit OrderCreated(order_123)
K-->>IS: Deliver OrderCreated
K-->>NS: Deliver OrderCreated
IS->>IS: Reserve inventory
IS->>K: Emit InventoryReserved(order_123)
NS->>NS: Send confirmation email
Real-World Examples
Uber
Uber's dispatch system processes ride events (ride requested, driver assigned, trip started, trip completed) through Kafka. Each microservice reacts to relevant events — payments processes on trip completion, ratings triggers after payment.
Netflix
Netflix's event processing pipeline handles billions of events daily — user interactions, playback events, infrastructure changes. Events trigger analytics, personalization, and alerting systems.
LinkedIn's "citrus" event bus processes feed updates, notifications, and analytics. Kafka originated at LinkedIn to handle this event-driven architecture.
Pros and Cons
| Pros | Cons |
|---|---|
| Loose coupling — producers don't know consumers | Eventual consistency — no ACID guarantees |
| Scalability — consumers scale independently | Debugging complexity — hard to trace event flows |
| Resilience — consumers can fail without affecting producers | Duplicate events — must handle idempotently |
| Audit trail — events are immutable logs | Schema evolution — versioning events is tricky |
| Real-time — react to events as they happen | Ordering challenges — guaranteed ordering is expensive |
When to Use Event-Driven Architecture
Use EDA when:
- Decoupling — services should not know about each other
- Multiple consumers — one event triggers many reactions (e.g., order → inventory + shipping + notifications)
- Audit compliance — every state change must be recorded
- Streaming data — real-time analytics, monitoring, fraud detection
Skip EDA for simple CRUD apps, request-response workflows where the caller needs an immediate answer, or when eventual consistency is unacceptable.
FAQ
Related Concepts
- Event Sourcing — state as event sequence
- CQRS Pattern — separate read/write with events
- Saga Pattern — distributed transactions via events
- Microservices Architecture — services communicate via events
- Observer Pattern — the design pattern basis for pub/sub
Practice Questions
What is the difference between a command and an event?
How does Kafka guarantee message ordering, and within what scope?
What is idempotency, and why is it critical in event-driven systems?
Compare Kafka and RabbitMQ — when would you choose each?
What is eventual consistency, and how does it affect the consumer experience?
Challenge
Design an event-driven system for an e-commerce checkout flow. Define the events (at least 5), the producer service, and the consumer services. Implement a simple Kafka producer that emits OrderCreated and two consumers — one that reserves inventory, one that sends a confirmation.
Real-World Task
Choose a synchronous API in your project that could become event-driven. Identify the producer, the event, and potential consumers. Implement the event emission on the producer side. Create one consumer that handles the event asynchronously. Measure the latency difference between the synchronous and asynchronous paths.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro