Animated Architecture Patterns

Event-Driven Architecture

Producers publish events to a broker; any number of consumers react on their own schedule.

Also known as EDA

event broker Order Service producer publishes, then moves on keeps publishing, unaffected topic orders Inventory queue Payments queue Notifications queue backlog 0 1 2 3 4 5 Inventory reserves stock Payments charges the card Notifications sends the email scaled out to 3 instances OrderPlaced

Step 1 · Publish. The producer records a fact (an order was placed) as an event on a topic. It doesn't know or care who is listening, and it doesn't wait for anyone to finish.

Step 2 · Fan out. Each subscriber has its own subscription (a queue). The broker drops a copy of the event into every one, so Inventory, Payments and Notifications each react at their own pace, in parallel.

Step 3 · Backlog builds. During a spike the producer keeps publishing at full speed. Fast consumers stay at zero lag, while the slow one (waiting on an email API) builds a backlog. Nobody else is slowed down: the queue absorbs the difference.

Step 4 · Scale out. Because work waits in a queue, the slow consumer can scale out as competing consumers without any change to the producer or the other services. The backlog drains, and the instances can scale back in once it's empty.

The problem

In a request/response world, the service where something happens has to call every service that cares. Placing an order means calling Inventory, then Payments, then Notifications, and so on. That caller must know every downstream service, wait for all of them, fail when any of them fails, and change every time a new one is added. Its latency becomes the sum of theirs, and its availability the product of theirs.

How it works

Services communicate through events: immutable facts about something that happened (OrderPlaced, PaymentCaptured).

  • Producers publish events to a broker and move on. They don't know who is listening.
  • The broker (Kafka, Amazon SNS + SQS or EventBridge, Azure Service Bus or Event Grid, Google Pub/Sub, RabbitMQ) stores the events and delivers them to every subscription.
  • Consumers process events at their own pace. Each consumer can scale, fail and recover independently. Its subscription remembers where it left off.

Adding a new capability usually means adding a new subscriber, with no change to the producer.

When to use it

  • Several independent reactions to the same business fact.
  • Workloads with bursty traffic, where a queue should absorb spikes instead of the slowest dependency.
  • Integrating systems owned by different teams, where temporal coupling (everyone up at the same time) is a liability.
  • Streaming and near-real-time analytics that consume the same event log.

Trade-offs

  • Eventual consistency. When OrderPlaced is published, the stock is not reserved yet. UIs and APIs must be designed for that.
  • Harder to follow. There is no single call stack. You need correlation IDs, distributed tracing and good event catalogs to see a whole flow.
  • Delivery semantics. Most brokers are at-least-once: consumers must be idempotent and handle out-of-order events.
  • Schema evolution. Events are a public contract. Version them, and prefer additive changes.
  • Dual writes. Updating your database and publishing an event is not atomic. Use a transactional outbox or change data capture.

Implementation notes

  • Name events in the past tense after business facts (OrderPlaced), not as commands (SendEmail).
  • Decide between thin events (IDs only, consumers call back for details) and fat events (carry the data). Fat events reduce coupling at runtime but make the schema a bigger contract.
  • Watch consumer lag per subscription. It is the single best health signal in an event-driven system, and a natural autoscaling metric (for example KEDA scaling on queue length).
  • Put a dead-letter queue behind every subscription so one poison message can't block the rest.