pattern

Event-Driven Architecture

also called EDA

A style in which components communicate by publishing and reacting to facts, rather than by calling each other and waiting.

eventsasyncdecouplingspotifystreaming

Definition

Components emit events — statements that something happened, in the past tense, immutable — and other components react. The producer does not know its consumers and does not wait for them.

Distinguish carefully from commands ("send this email"), which have exactly one intended handler and belong on a queue, and from request/response, where the caller needs an answer to proceed.

Why organisations adopt it

  • Extensibility without modification. A new consumer of TrackPlayed requires no change to the producer. This is the strongest argument and the one that actually pays off.
  • Burst absorption. The log buffers; consumers process at their own rate.
  • Failure isolation. A dead consumer does not break the producer.
  • Multiple views of the same fact. Analytics, personalisation, billing and notifications all read one event for different purposes.

Industry example

Spotify's personalisation stack is a clean illustration of why the style fits. A single user action — a track played, skipped, saved — is a fact with many independent interested parties: the recommendation models, the playlist generators, the artist analytics that feed royalty reporting, the experimentation platform measuring whether a change moved behaviour, and the year-end summary features.

Building that as synchronous calls would mean the playback path depends on the availability of six downstream systems, and adding a seventh would require modifying playback. As events, playback writes one fact and moves on. The systems that consume it can be deployed, scaled, broken and fixed independently, and a new one can be added by a different team without a conversation.

The costs are equally visible in that architecture: everything downstream is eventually consistent (a play counts toward a listening total in seconds or minutes, not immediately), the event schema is a public contract that cannot be changed casually, and answering "why did this user get this recommendation?" requires correlating across many asynchronous systems.

When request/response is the better answer

  • The caller needs the result to continue — an authorisation, a price, a validity check.
  • The interaction is genuinely one-to-one and always will be.
  • Strong consistency is required at the point of the call.
  • The team is small and the operational cost of a broker exceeds the coupling it removes.

Turning a synchronous need into events produces correlation IDs, reply topics and timeouts: a request/response call with worse tooling and much worse debugging.

Failure scenarios

  • Events as disguised commands, creating hidden one-to-one coupling with none of the clarity.
  • No consumer contract testing, so a producer's additive change breaks a consumer that parsed strictly.
  • Unbounded lag during a spike, so downstream state is hours behind and nobody is alerted.
  • No idempotency at consumers, so at-least-once delivery duplicates side effects.
  • Debugging without correlation. A user-visible problem spans nine asynchronous systems and there is no shared trace ID.

Trade-offs

Bought: decoupling, extensibility, elasticity. Sold: immediate consistency, straightforward debugging, simple reasoning about "what is the current state", and a meaningful operational investment in schema governance and lag monitoring.

Interview question

"Which parts of a checkout flow would you make event-driven and which would you keep synchronous? Justify each boundary."