EDA governance: event catalog, ownership, and avoiding “event spaghetti” 🧭
EDA scales very well technically. The problem is that it often scales the chaos just as well.
This guide exists to avoid the “we have 200 topics and nobody knows why” effect.
Why governance matters (before it is too late) 🧠
The problem is not the technology: without shared rules, every team optimizes locally and the global system becomes incomprehensible. Incidents, at that point, are not bugs: they are consequences.
Without governance:
- duplicate events with different names;
- consumers depending on internal details;
- silent breaking changes;
- topics created like mushrooms;
- difficult-to-diagnose incidents.
With minimal governance:
- explicit contracts;
- clear ownership;
- discoverability;
- controlled evolution.
Event catalog: the map of your ecosystem 🗺️
flowchart LR
PR[Event proposal] --> REV["Review<br/>(naming/schema/privacy)"]
REV --> PUB["Publication<br/>+ doc + schema"]
PUB --> CAT[Event catalog]
CAT --> DISC[Discoverability<br/>who publishes/consumes]
PUB --> DEP[Deprecation]
DEP --> RET[Retire]
An event catalog should answer:
- which events exist?
- who publishes them?
- who consumes them?
- what is the schema and semantics?
- what is the lifecycle state? (active, deprecated, legacy)
It does not need to be an endless tome: it must be discoverable and updated.
Ownership: who answers when something breaks 👤
Ownership is not “the name of a team written in a wiki nobody reads”: it is the guarantee that someone can answer when something goes wrong—and knows why that event exists.
Every event or topic should have:
- an identified team owner (with a real point of contact, not just a generic alias);
- a reachable contact channel (Slack, email, rotation);
- declared minimum SLOs: expected latency, retention window, reliability guarantees.
Without an owner, you are not in a situation of “shared ownership”: you are in a situation of absence of ownership, which is very different. Orphan events accumulate, nobody dares touch them, and at some point someone wonders whether it is safe to deprecate them. They do not know. Nobody knows. That is the real “event spaghetti”.
A good ownership registry should be part of the catalog itself: not a separate Google sheet that syncs badly with reality, but a single source of truth updated along the event lifecycle.
“If everyone is responsible, nobody is responsible.” This applies to microservices, APIs, and obviously events.
Standards: naming, metadata, versioning 📏
Without shared standards, each team produces events with its own conventions. The result: OrderPlaced, order_placed, order-place-event, PlaceOrderCommand — all doing similar things, often in different systems, never interoperable without ad hoc transformations.
Naming
The most common and sensible rule for events is past tense + domain noun:
OrderPlaced,PaymentFailed,UserRegistered;- avoid imperative verbs (those are commands, not events);
- prefix with the bounded context if you use a shared event bus:
payments.PaymentFailed,orders.OrderShipped.
For topics/streams, a common convention is {domain}.{entity}.{version} or {domain}-{entity}-events, as long as it is not changed halfway through the project.
Mandatory metadata
Every event should carry a minimum of cross-cutting context:
|
|
event_id and occurred_at ensure traceability and ordering; correlation_id allows you to follow a request through multiple services; schema_version is vital for compatibility.
Schema evolution and compatibility
Three modes, in increasing rigor:
- Backward compatible: add optional fields, do not remove or rename existing ones;
- Forward compatible: consumers ignore fields they do not know;
- Fully compatible: both directions simultaneously.
In practice: use a schema registry (Confluent, AWS Glue, Apicurio) to centralize schemas, enforce compatibility, and version explicitly. The registry becomes the contractual source of truth, not an infrastructure detail.
Standards do not mean bureaucracy: they mean preventing every team from reinventing the alphabet—and then failing to communicate with each other.
Organization, domain, and boundaries: Conway does not forgive 🏛️
There is a law nobody can evade: Conway’s Law. “Organizations generate systems that reflect their communication structure.” Applied to EDA: if teams communicate poorly, events will be inconsistent; if ownership and boundaries are clear, contracts tend to be clear too.
Bounded context as a unit of coherence
EDA and DDD (Domain-Driven Design) combine naturally: bounded contexts define the boundaries within which a domain model is coherent and understandable. Each context should:
- publish events that reflect its ubiquitous language;
- not expose internal details (database model, persistence entities);
- evolve independently from other contexts.
A OrderPlaced in the Orders context may have different semantics from what the Shipping context expects. That is normal: the important thing is not to let internal details leak across boundaries.
Anti-corruption layer
When a service must consume events from another domain, the anti-corruption layer (ACL) translates incoming events into the consumer’s internal model. The benefits are concrete:
- the consumer is isolated from producer changes;
- the team’s internal language is not contaminated by others’ models;
- producer breaking changes become an ACL problem, not the whole consumer system.
Effective governance also reduces cognitive load: less ambiguity, predictable naming, searchable catalog, explicit boundaries. Everything that does not need to be understood should not be exposed.
Lightweight but real processes 🔁
“Process” should not be scary: a few explicit steps are enough to prevent the event catalog from becoming a wild west. Here is a sustainable example for medium-sized teams.
1. Proposal
Anyone who wants to publish a new event opens a proposal—issue, ADR, or PR in a governance repo—that includes:
- event name and bounded context;
- motivation and use case (why it exists, what it represents);
- target consumers;
- schema draft.
2. Review
It is not a bureaucratic committee, but a distributed review on:
- compliance with naming and metadata standards;
- compatibility with existing events (duplicates? semantic conflicts?);
- privacy and compliance implications (GDPR, sensitive data in the payload?);
- approval from at least one other team that knows the domain area.
3. Publication
Merge into the catalog with:
- final schema registered in the schema registry;
- minimal documentation: semantics, when it is emitted, and what it means;
- owner declared and contact information;
- lifecycle state set to
active.
4. Deprecation and retirement
Change the status in the catalog, notify known consumers, define a support window (for example, 3 months), then retire. Automate reminders where possible—nobody remembers deadlines spontaneously.
The secret is that the process exists in writing and is actually used: even with some compromises, it is infinitely better than a spontaneous situation.
Governance checklist ✅
If you want a litmus test for understanding whether you are already in “event spaghetti” territory, this is a good start. You do not need to be perfect: you need to be explicit.
- Catalog: it must be searchable and each entry should include at least schema, owner, and lifecycle state.
- Owner: for every event/topic, indicate a contact and a channel (for example Slack/Email) and define minimum SLOs.
- Metadata/naming standards: publish a standard with examples and use cases, including templates for proposing new events.
- Breaking change policy: define a support window and a deprecation process (automated or manual, but real).
- DLQ and reprocessing: document the runbook, ownership, and how to test reprocessing in staging.
Next steps 🚀
Governance without operations is theory; operations without governance is efficient chaos. You need both.
- For operations: see the delivery semantics and DLQ guide.
- For contracts: see the event design guide.