Back to Library
system_design

Event-Driven Architecture

Event-driven systems communicate via asynchronous events, enabling loose coupling and independent scaling of producers and consumers.

architecturepatternsasync

What is event-driven architecture?

In event-driven architecture (EDA), services communicate by producing and consuming events rather than making direct synchronous calls. A producer publishes an event when something happens; consumers react to those events independently.

Core Patterns

Producer-Consumer

One producer writes to a queue; one consumer reads and processes.

  • Decouples producer from consumer
  • Consumer processes at its own pace (load levelling)
  • If consumer is down, messages queue up and are processed when it recovers

Pub/Sub (Publish-Subscribe)

One producer publishes to a topic; multiple consumers subscribe independently.

  • Each consumer gets a copy of every event
  • Consumers can be added without changing the producer
  • Used for fan-out notifications, event broadcasting

Choreography vs Orchestration

Choreography

Services react to events without a central coordinator. Service A emits OrderCreated; Service B (inventory) reacts by reserving stock; Service C (notifications) reacts by sending email. No one orchestrates the flow.

  • ✅ Loose coupling - services don't know about each other
  • ✅ Easy to add new consumers without changing existing services
  • ❌ Hard to trace and debug end-to-end flows
  • ❌ Business process logic is distributed across services

Orchestration

A central orchestrator (coordinator service or workflow engine) tells each service what to do and collects responses.

  • ✅ Clear visibility into process flow
  • ✅ Easier error handling and rollback
  • ❌ Orchestrator is a coupling point and potential bottleneck
  • ❌ Reduces autonomy of individual services

Loose Coupling Tradeoffs

EDA's greatest strength is also its greatest challenge:

Benefits of loose coupling:

  • Services can be deployed, scaled, and changed independently
  • Adding new consumers doesn't require modifying producers
  • Services can be written in different languages

Costs of loose coupling:

  • End-to-end request tracing requires distributed tracing (correlation IDs)
  • Debugging event flows requires good tooling
  • Eventual consistency: downstream state may lag after an event is published
  • Event schema changes require careful versioning (consumers may not be updated simultaneously)

Eventual Consistency Implications

When service A processes an order and emits OrderShipped, service B (notifications) may process the event 100ms later. During that window, the notification hasn't been sent yet. The system is eventually consistent - but any component that queries "has the notification been sent?" immediately after the order ships may see inconsistent state.

Design UIs and downstream logic to tolerate this:

  • Show "Your order is being processed" rather than assuming instant propagation
  • Use idempotent consumers (process the same event multiple times safely)

Event Schema and Versioning

Events are the API between services. Breaking changes to event schemas break consumers. Strategies:

  • Add new fields as optional (backward compatible)
  • Never remove or rename fields (version the event instead)
  • Use event_version field: consumers handle each version explicitly
  • Maintain event schema registry (Confluent Schema Registry for Kafka)

Technologies

TechnologyTypeUse When
Apache KafkaLog-based streamingHigh throughput, replay, event sourcing
RabbitMQMessage brokerTraditional queuing, routing
AWS SQS + SNSManagedAWS-native, simple setup
Google Pub/SubManagedGCP-native
Azure Service BusManagedAzure-native

Interview Angle

For any system with asynchronous workflows (order processing, user onboarding, notifications), propose EDA. Demonstrate understanding of choreography vs orchestration tradeoff, eventual consistency, and idempotent consumers. Mentioning event schema versioning shows senior-level thinking.

Recommended Resources