5. Topic and Event Naming
- Status: accepted
- Date: 2025-06-17
- Deciders: Jannik Alexander, Stefan Bente, Daniel Köllgen (governance board)
Context
Every core service publishes events, and every player consumes them. Without a shared convention, topic names, event type names and headers drift apart, and every consumer needs service-specific code to read them. Names must also survive reorganizations — which service produces or consumes a topic can change — and topics and schemas must be versioned to allow change management.
Decision
Topic names
Topics for events of the core services MUST follow the pattern
<message-type>.<aggregate/domain>.<data-name>[.<subset>].v<version>
with these message types:
bl— business logic: domain events, usually delta eventsdb— database: state transfer, such as event-carried state transfer (ecst) or change data capture
Further message types are reserved: logging/tracing, queuing, tracking, streaming, push and user.
Topic names MUST NOT contain anything that may change, such as service names, teams, producers or consumers, and MUST
contain a version. Examples: bl.game.events.v1, db.game.ecst.v1, bl.robot.events.v1, db.map.ecst.v1.
Intent topics have no message-type prefix. They MUST follow the pattern
<aggregate/domain>.intents.v<version>
Examples: robot.intents.v1, voucher.intents.v1 (see
ADR-0003).
Event type names
The event_type header names the payload schema as <aggregate/domain>.<event-name>, in lower case with . and -
only (e.g. player.registered, robot.moved, game.state-transfer). The version is not part of the name; it is
carried in event_type_version.
Event headers
| Header | Required | Content |
|---|---|---|
event_id |
MUST | UUID identifying the event across topics and partitions; used to detect duplicates |
event_type |
MUST | Payload schema name, see above |
event_type_version |
MUST | Payload schema version, integer > 0 |
created_at |
MUST | UTC epoch millis of the moment the event was created (written to the outbox), not published |
sequence_id |
SHOULD | Position of the event in its partition’s sequence, strictly increasing; lets consumers drop duplicates |
trace_id |
SHOULD | Current trace id for context propagation, where one exists |
entity |
MAY | <entity-name>.<entity-id>, for debugging only |
Intents from players carry only event_id, event_type and event_type_version
(see ADR-0003). The authoritative
definition of the header keys and their byte serialization is HeaderKeys in the outbox-lib, copied to the
consumer-lib.
Alternatives
- No convention, each service decides — rejected because consumers could not process events of all services with the same code (e.g. the consumer-lib).
Consequences
- Good: topic names describe data, not the organization, and stay valid when responsibilities move.
- Good: the same consumer code processes events of every service.
- Good:
event_idandsequence_idenable duplicate detection (ADR-0007). - Bad: existing names that deviate must be renamed, which is a breaking change for consumers.
Current State
These names do not follow the convention yet; each needs to be renamed or accepted as a documented exception:
- The Trading service’s dead-letter topics,
intents.trading-service.dltandsystem.trading-service.dlt, contain a service name. - The Trading service’s event types have no aggregate prefix (e.g.
credits-deposited,bank-account-opened).