Home/Learn/System Design/Event-Driven Architecture

Event-Driven Architecture

Intermediate
Communication Patterns

Event-driven architecture (EDA) structures communication around events — immutable facts that something happened. Services produce and consume events asynchronously, enabling loose coupling, scalability, and real-time responsiveness.

Overview

In event-driven architecture, services communicate by producing and consuming events rather than making direct synchronous calls. An event is an immutable record that something happened ("OrderPlaced", "PaymentReceived", "UserRegistered"). Producers emit events without knowing which services will consume them. Consumers subscribe to events they care about and react independently. This creates loose coupling — adding a new consumer requires no changes to the producer. EDA enables real-time processing, natural audit trails (event log), and temporal decoupling (consumers process at their own pace). The two main patterns are event notification (lightweight event triggers consumers to fetch data) and event-carried state transfer (event contains all necessary data). Event sourcing takes EDA further by storing all state changes as events.

Event-Driven vs Request-Driven

In request-driven architecture, Service A calls Service B synchronously and waits. In event-driven architecture, Service A emits an event and moves on — Service B processes it asynchronously.

Conceptual + Java — event-driven order flow
// Request-driven (synchronous coupling)
// OrderService → PaymentService.charge()  → wait → InventoryService.reserve() → wait
// Problem: if PaymentService is down, OrderService is blocked

// Event-driven (asynchronous coupling)
// OrderService emits "OrderPlaced" event → message broker
//   ├→ PaymentService consumes → charges payment → emits "PaymentCompleted"
//   ├→ InventoryService consumes → reserves stock
//   └→ NotificationService consumes → sends confirmation email
//
// Benefits:
// - OrderService is not blocked
// - Adding EmailService = just subscribe to event (zero changes to OrderService)
// - Each service scales independently
// - Natural retry: failed consumers re-process from queue

// Spring Boot event publisher
@Service
public class OrderService {
    private final KafkaTemplate<String, OrderEvent> kafka;

    public Order placeOrder(OrderRequest req) {
        Order order = orderRepo.save(createOrder(req));
        kafka.send("order-events", order.getId(),
            new OrderPlacedEvent(order.getId(), order.getUserId(), order.getTotal()));
        return order;
    }
}

Event Notification vs Event-Carried State Transfer

Event notification sends a thin event ("OrderPlaced with id=123") — consumers call back for details. Event-carried state transfer sends a fat event with all data — consumers are self-sufficient but events are larger.

JSON — thin vs fat events
// Event notification (thin event)
{
  "type": "OrderPlaced",
  "orderId": "123",
  "timestamp": "2025-03-29T10:00:00Z"
}
// Consumer must call OrderService API to get full order details
// Pros: small events, single source of truth
// Cons: callback coupling, more network calls

// Event-carried state transfer (fat event)
{
  "type": "OrderPlaced",
  "orderId": "123",
  "userId": "u-42",
  "items": [{ "productId": "p-1", "qty": 2, "price": 49.99 }],
  "total": 99.98,
  "shippingAddress": { "city": "Mumbai", "zip": "400001" },
  "timestamp": "2025-03-29T10:00:00Z"
}
// Consumer has all data — no callback needed
// Pros: fully decoupled, consumer autonomy
// Cons: larger events, data duplication, schema evolution complexity

Challenges & Best Practices

EDA introduces challenges: eventual consistency, event ordering, idempotent consumers, and debugging distributed event flows. Schema evolution and event versioning are critical for long-term maintenance.

Java + Conceptual — EDA best practices
// Challenge 1: Ordering — use partition keys
// Kafka: same order_id → same partition → ordered processing

// Challenge 2: Idempotency — deduplicate by event ID
@KafkaListener(topics = "order-events")
public void onEvent(OrderEvent event) {
    if (processedEvents.contains(event.getEventId())) return; // dedupe
    processOrder(event);
    processedEvents.add(event.getEventId());
}

// Challenge 3: Schema evolution — use Avro + Schema Registry
// Version 1: { orderId, total }
// Version 2: { orderId, total, currency }  ← backward compatible (new field optional)

// Challenge 4: Debugging — correlation IDs
// Every event carries a correlationId linking the entire flow
// OrderPlaced(correlationId=abc) → PaymentCharged(correlationId=abc)
// → Distributed tracing (Jaeger, Zipkin) visualises the event chain

// Best practice: outbox pattern for reliable event publishing
// 1. Save entity + event to DB in one transaction
// 2. Background job reads outbox table → publishes to Kafka
// 3. Guarantees event is published if and only if entity is saved

Key Points to Remember

  • 1Events are immutable facts ("OrderPlaced") — producers emit, consumers react independently.
  • 2EDA enables loose coupling — adding consumers requires no producer changes.
  • 3Thin events (notification) require callbacks; fat events (state transfer) make consumers self-sufficient.
  • 4Consumers must be idempotent — events may be delivered more than once.
  • 5Use the outbox pattern for reliable event publishing with database consistency.

Interview Questions

Sign in to ask Aria
1

What is event-driven architecture and how does it differ from request-driven?

EasyTCS
2

What is the difference between event notification and event-carried state transfer?

MediumAmazon
3

How do you ensure event ordering in a distributed event-driven system?

MediumGoogle
4

Explain the outbox pattern and why it is needed.

HardFlipkart
5

Design an event-driven order processing system handling 50K events/second.

HardUber

Ask Aria about Event-Driven Architecture

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…