Event-Driven Architecture: Where the Boundaries Actually Matter
Events create leverage only when ownership, delivery guarantees, and recovery paths are explicit.
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
Designing High-Throughput Systems Without Losing the Plot
A practical framework for separating capacity, latency, correctness, and operational risk before choosing technology.
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.