High-Level Design Tutorial 0/42 lessons ~6 min read Lesson 8

    Event-Driven Architecture

    Event-driven architecture (EDA) centers on producing, routing, and consuming events — immutable records that something happened (OrderPlaced, PaymentCaptured, DriverLocationUpda…

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Event-driven architecture (EDA) centers on producing, routing, and consuming events — immutable records that something happened (OrderPlaced, PaymentCaptured, DriverLocationUpdated). Producers don't know consumers; the event broker decouples teams and absorbs traffic spikes.

    EDA excels at fan-out (one order triggers email, analytics, warehouse, loyalty), temporal decoupling, and audit trails. Trade-offs include eventual consistency, duplicate delivery handling, and debugging complexity across async flows.

    This lesson teaches event naming, broker selection, consumer design, and how EDA complements request-response APIs in HLD interviews.

    Understanding the topic

    Key concepts

    • Event: past-tense fact with schema (Avro/JSON Schema/Protobuf) and metadata (timestamp, correlationId).
    • Event notification vs event-carried state transfer — choose based on consumer autonomy.
    • Broker: Kafka for log-based replay; RabbitMQ/SQS for task queues; Redis Streams for lighter cases.
    • At-least-once delivery typical — consumers must be idempotent.
    • CQRS often pairs with EDA: write model emits events; read models project asynchronously.
    • Choreography (consumers react) vs orchestration (central saga coordinator).
    text
    flowchart LR
    Producer -->|event| Broker
    Broker --> ConsumerA
    Broker --> ConsumerB

    Internal architecture

    Architecture overview

    text
    flowchart LR
    Producer -->|event| Broker
    Broker --> ConsumerA
    Broker --> ConsumerB

    Step-by-step explanation

    1. Command API accepts user action → persists aggregate → publishes domain event to broker.
    2. Topic partitioned by aggregate ID (orderId) preserving per-entity ordering.
    3. Multiple consumer groups read same topic independently (notifications vs BI).
    4. Dead-letter queue (DLQ) captures poison messages after retry exhaustion.
    5. Schema registry enforces compatible event evolution.
    6. Observability: trace context propagated in event headers (OpenTelemetry baggage).

    Informative example

    Publish inventory adjustment events after stock update — downstream search index updates asynchronously:

    java
    @Service
    public class InventoryService {
    private final InventoryRepository repo;
    private final KafkaTemplate<String, StockAdjustedEvent> kafka;
    public InventoryService(InventoryRepository repo, KafkaTemplate<String, StockAdjustedEvent> kafka) {
    this.repo = repo;
    this.kafka = kafka;
    }
    @Transactional
    public void adjust(String sku, int delta) {
    Inventory inv = repo.findBySku(sku).orElseThrow();
    inv.apply(delta);
    repo.save(inv);
    kafka.send("inventory.stock-adjusted", sku,
    new StockAdjustedEvent(sku, inv.quantity(), Instant.now()));
    }
    }
    public record StockAdjustedEvent(String sku, int quantity, Instant at) {}
    @Component
    class SearchIndexConsumer {
    @KafkaListener(topics = "inventory.stock-adjusted", groupId = "search-index")
    public void project(StockAdjustedEvent e) {
    // upsert Elasticsearch document — idempotent by sku
    }
    }

    Name events in past tense. Include correlationId from HTTP request in Kafka headers for end-to-end traces.

    Real-world use

    Real-world use cases

    • E-commerce order pipeline fan-out to warehouse, email, fraud, analytics.
    • Banking ledger posting triggering compliance audit and customer notification.
    • Social graph: new post event updates follower feeds and search index.
    • Healthcare HL7/FHIR change events syncing EHR modules.

    Best practices

    • Design idempotent consumers with natural keys or idempotency store.
    • Version event schemas with backward-compatible rules.
    • Use outbox pattern to atomically DB-write + publish.
    • Monitor consumer lag — primary EDA health metric.
    • Keep events small; reference large blobs by URL/id.
    • Document event catalog — who publishes, who subscribes, SLA.

    Common mistakes

    • Using events for synchronous query/response — wrong tool.
    • No DLQ — poison messages block partition processing.
    • Assuming exactly-once without transactional outbox or idempotent design.
    • God topics with unrelated events — hard to scale and govern.
    • Missing ordering guarantees where business requires per-entity sequence.

    Advanced interview questions

    Q1BeginnerWhat is event-driven architecture?
    Services communicate by producing and consuming events through a broker, decoupling producers from consumers.
    Q2BeginnerBenefit of EDA over direct HTTP calls?
    Decoupling, fan-out, peak buffering, and independent consumer scaling.
    Q3IntermediateHow handle duplicate events?
    Idempotent consumers keyed by event ID or business idempotency key; store processed IDs in Redis or DB.
    Q4IntermediateOutbox pattern purpose?
    Atomically persist business data and outbound event record in same DB transaction; separate publisher reads outbox.
    Q5AdvancedDesign EDA for food order lifecycle.
    Events: OrderCreated, RestaurantAccepted, DriverAssigned, Delivered — Kafka topics by type, partition by orderId, saga for payment capture on Delivered, DLQ and schema registry.

    Summary

    EDA decouples services through immutable domain events. Kafka enables replay, fan-out, and high-throughput logs. Design for at-least-once with idempotent consumers. Outbox pattern bridges transactional writes and publishing. Pair events with CQRS for read model projections. Layered and scaling lessons build on these integration patterns.

    Ready to mark this lesson complete?Track your journey across the entire course.