MQMarshall Qiao

Event-Driven Architecture: Where the Boundaries Actually Matter

Events create leverage only when ownership, delivery guarantees, and recovery paths are explicit.

ArchitectureEvent-DrivenDistributed SystemsKafka

Event-driven architecture is often introduced as a diagram: producers on the left, a broker in the middle, consumers on the right. The difficult part is everything the arrows do not say.

An event is a contract about the past

A useful event describes something that has already happened in the producer's domain. OrderAccepted is stronger than ProcessOrder because it expresses a fact, identifies the owner, and lets consumers decide what that fact means to them.

Good event contracts include:

  • a stable identity;
  • a versioned schema;
  • the time the domain fact occurred;
  • enough context to process without calling the producer for every message;
  • a documented policy for sensitive data and retention.

At-least-once changes the application

Most production pipelines should assume duplicates. Idempotency is therefore not a broker setting; it is application behavior.

A consumer needs a stable idempotency key and an atomic relationship between “I processed this message” and its business side effect. Depending on the data store, that may be a unique constraint, an inbox table, a conditional update, or a naturally idempotent state transition.

Retries should be bounded and observable. A dead-letter queue is not a recovery strategy unless someone owns replay and can explain whether replaying is safe.

Ownership matters more than topology

The producer owns the meaning and quality of the event. The platform owns transport reliability and operational standards. Each consumer owns its processing, lag, and recovery.

When those responsibilities are vague, teams compensate with shared databases, synchronous callbacks, and manual repair scripts. The system looks event-driven but behaves like a distributed monolith.

Prefer a small number of meaningful events

Publishing every database mutation creates noise and couples consumers to storage details. Start with domain facts that other capabilities genuinely need. Add events when there is a clear consumer and an understood contract.

The goal is not maximum asynchrony. It is independent evolution with a recovery story.

Related notes

  1. 3 min readWriting

    Designing High-Throughput Systems Without Losing the Plot

    A practical framework for separating capacity, latency, correctness, and operational risk before choosing technology.

  2. 2 min readInterview Lab

    System Design Drill: A High-Throughput Rule Engine

    How to structure requirements, data flow, scaling, correctness, and operations in a 45-minute system design interview.

Working on something similar?

I'm always happy to compare notes on distributed systems, delivery, and applied AI.

qiaoyuanshou@gmail.com