SC
Distributed Systems

Event-Driven Architecture

Events decouple producers from consumers — and quietly hand you ordering, delivery, and schema problems in return.

Event-driven architecture decouples the thing that happened from everything that reacts to it. A service emits a fact. Other services respond on their own schedule.

It is the backbone of most enterprise integration and fintech automation work I have done. It is also where a lot of accidental complexity hides.

Commands and events are not the same

A command is a request to do something. ApprovePayment. It has one intended handler. It can be rejected.

An event is a statement that something already happened. PaymentApproved. It has zero or more interested consumers. It cannot be rejected. It is history.

Most cross-service traffic should be events. Commands should mostly stay inside a service.

Why reach for events

  • Decoupling. The producer doesn't know who consumes the event. New consumers appear without touching the producer.
  • Temporal decoupling. The consumer doesn't have to be online when the event fires. The broker holds it.
  • Fan-out. One InvoiceApproved can drive notifications, ledger updates, and reconciliation independently.

What you inherit the moment you add a broker

  • Ordering. Most brokers only guarantee order within a partition. Pick partition keys (account id, tenant id) so related events stay ordered.
  • Delivery. "At least once" is the realistic default. Every consumer will see duplicates, so consumers must be idempotent.
  • Schema. Events outlive the code that produced them. Version the payload and treat it as a public contract.

Lesson. The diagram is the easy part. The cost of events shows up later — in ordering, replays, and schema drift. Design for those on day one, not after the first incident.

Separate transfer from processing

A pattern that keeps showing up: the arrival of something is itself the event.

A file lands. A payment settles. An invoice is approved.

Receiving is one responsibility. Processing is another. Keeping them apart lets each scale independently and makes the pipeline replayable.

Rules of thumb

  1. Name events as past-tense facts. They are immutable.
  2. Put enough in the payload that common consumers don't need a callback — but not so much that the event leaks your internal model.
  3. Assume duplicates and out-of-order delivery. Always.