5. Topic and Event Naming

Naming conventions for Kafka topics and event types, and the standard headers every event carries.
  • 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 events
  • db — 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_id and sequence_id enable 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.dlt and system.trading-service.dlt, contain a service name.
  • The Trading service’s event types have no aggregate prefix (e.g. credits-deposited, bank-account-opened).
Last modified October 22, 2026: Update documentation (f481966)