How Event-Driven Architecture Works

Intermediate
8 min read· Architecture & Design

Event-Driven Architecture (EDA) is a design approach where services communicate by producing and consuming events rather than making direct API calls. When something happens — "OrderPlaced", "PaymentProcessed", "UserRegistered" — the producing service publishes an event to a broker. Any number of consumers react to it independently, without the producer knowing or caring who they are. EDA enables loose coupling, high scalability, and natural audit trails — at the cost of eventual consistency and increased operational complexity.

Think of it like a newspaper

A journalist (producer) writes an article about an event and publishes it in the newspaper (event broker). Readers (consumers) subscribe to the newspaper and read the article independently — each does their own thing with the information. The journalist doesn't know who reads the paper or what they do with the news. New subscribers can start reading tomorrow without the journalist doing anything. This is fundamentally different from the journalist calling each reader personally (REST API) — which is synchronous, tightly coupled, and breaks if a reader is unreachable.

Step by Step

1 / 6

Key Concepts

Domain Event

A record of something that happened in the business domain, named in past tense: OrderPlaced, PaymentFailed, AccountCredited. Events are immutable facts — you never edit or delete an event. They contain all data consumers need to react: IDs, timestamps, relevant values. Domain events form the language of the business.

Event Broker

The infrastructure that receives, stores, and delivers events. Kafka: high-throughput, durable, ordered within a partition, supports replay. RabbitMQ: flexible routing, lower latency, messages deleted after consumption. AWS EventBridge: serverless, fan-out to many targets with filtering rules. The broker decouples producers from consumers in both space (don't need to know about each other) and time (don't need to run simultaneously).

Choreography vs Orchestration

Choreography: each service reacts to events independently with no central coordinator — like a dance where each dancer follows the music. Decoupled but harder to visualise. Orchestration: a central saga orchestrator explicitly directs each service step-by-step — easier to reason about but creates a central dependency. Most event-driven systems use choreography for simple flows and orchestration for complex multi-step transactions.

Event Sourcing

Storing the state of an entity as a sequence of events rather than the current state. Instead of UPDATE accounts SET balance = 100, you store AccountDebited{amount: 50} and AccountCredited{amount: 150}. Current state is derived by replaying events. Benefits: full audit trail, time travel (state at any point in history), rebuild read models. Trade-off: more complex to query, eventual consistency, event schema evolution is hard.

CQRS (Command Query Responsibility Segregation)

Separating read and write models. Commands (writes) update the event store. Queries (reads) use a separate, denormalised read model (projection) optimised for query patterns. Often paired with event sourcing: domain events update both the event store and projections. A read model can be rebuilt at any time by replaying the event store.

Idempotent Consumer

Processing the same event multiple times produces the same result. Critical in EDA because brokers guarantee at-least-once delivery — consumers will receive duplicates. Design consumers to be idempotent: store processed event IDs and skip already-processed events. For financial operations, use the event ID as an idempotency key in database upserts.

Outbox Pattern

Reliably publishing events alongside database writes. Instead of writing to the DB then publishing to Kafka (two operations that can fail independently), write both to the DB in one transaction: the event goes into an "outbox" table. A separate process reads the outbox and publishes to Kafka, then deletes the row. Guarantees at-least-once delivery without distributed transactions.

Event Schema Registry

A central catalog that stores and enforces event schemas (Confluent Schema Registry, AWS Glue Schema Registry). Producers register their event schemas; consumers validate incoming events against the registry. Prevents incompatible changes from breaking consumers silently. Supports Avro and Protobuf for compact, schema-validated serialisation.

Key Facts

  • LinkedIn's founding engineer Jay Kreps built Kafka in 2010 specifically to handle LinkedIn's activity stream as a persistent, replayable event log. The same design now powers event-driven architectures at thousands of companies.
  • Amazon's entire retail platform is event-driven. Every click, search, purchase, and inventory change publishes an event. Independent services consume events to update recommendations, analytics, fulfilment, and billing without synchronous coupling.
  • Event sourcing is used in banking and financial systems precisely because it provides an immutable, auditable record of every state change — a regulatory requirement. CQRS+Event Sourcing is the foundation of many core banking systems.
  • The CAP theorem states a distributed system can guarantee at most two of: Consistency, Availability, and Partition tolerance. EDA systems typically choose AP (availability + partition tolerance) over CP — they are eventually consistent but always available.
  • Martin Fowler's "Event-Driven" article and Greg Young's work on CQRS/Event Sourcing (2010) are the canonical references. Greg Young coined the term "Event Sourcing" and popularised the combination with CQRS.
  • Debezium is a popular open-source tool for Change Data Capture (CDC) — it reads database transaction logs (PostgreSQL WAL, MySQL binlog) and publishes row-level changes as events to Kafka. Turns any database into an event source without application code changes.

Real-World Applications

Order processing fan-out

A single OrderPlaced event triggers parallel reactions: Inventory reserves stock, Payment charges the card, Email sends confirmation, Analytics records revenue, Fraud scores the transaction, Warehouse queues picking. Without EDA, the Order Service would make 6 synchronous API calls, creating a 6-service latency chain and tight coupling. With EDA, all reactions happen in parallel and the Order Service responds in <50ms.

Real-time analytics pipelines

Every user action (page view, click, purchase) is published as an event to Kafka. A stream processing layer (Apache Flink, Kafka Streams) aggregates events in real time: "revenue in the last 5 minutes", "active users right now". Results are written to a dashboard database. This replaces batch ETL jobs (nightly reports) with live metrics.

Building new features without touching existing code

Existing events already flow through Kafka. A new Loyalty Points service subscribes to OrderCompleted events and awards points with zero changes to the Order Service. A new Personalisation service subscribes to user behaviour events. EDA makes adding features additive — no modification to existing services required.

Disaster recovery via event replay

A new service's database is corrupted. Instead of restoring from a backup (potentially hours of lost data), the team replays all relevant events from the Kafka topic (retained for 30 days) into a fresh database. The service catches up to the present in minutes. This is only possible because events are persisted in the broker.

Frequently Asked Questions

When should I use events instead of REST API calls?

Use events when: multiple services need to react to the same action (fan-out), the producer doesn't need an immediate response (fire and forget), services should be independently deployable and scalable, or you need an audit trail. Use REST when: you need an immediate response (user is waiting), you're implementing CRUD operations, or the flow is simple and only involves two services. Most real systems use both — REST for synchronous user-facing flows, events for async backend reactions.

How do you handle event schema changes?

Evolve schemas carefully — you can't change a published event without potentially breaking consumers. Strategies: add new optional fields (backward compatible), never remove or rename fields without a versioned migration (EventV1 → EventV2), use a schema registry to enforce compatibility rules (backward, forward, full compatibility). Event upcasters can transform old event formats as they're consumed. Schema evolution is the biggest operational challenge in mature event-driven systems.

What is the difference between event-driven and message-driven architectures?

A message is sent to a specific recipient (point-to-point, like a queue). An event is published to a channel where any interested party can listen (pub/sub). Event-driven implies the publisher doesn't know or care who the consumers are. Message-driven often implies the producer knows who the message is for. In practice the terms are often used interchangeably, and many systems use a message broker (Kafka, RabbitMQ) for both patterns.

How do you debug failures in an event-driven system?

Distributed tracing is essential — propagate a trace ID from the originating request through every event and service call. Tools like Jaeger or Zipkin visualise the complete call graph. Dead Letter Queues (DLQs) catch events that repeatedly fail processing — inspect them to find bugs. Replay events from the broker to reproduce issues in a staging environment. Event sourcing systems have a complete audit trail — you can reconstruct exactly what happened at any point.

Related Topics