Standard for Kafka Topic and Message Design
1. Topic Naming Format
<exposure>.<domain>.<messagename>.<version>
| Component | Description | Allowed Values |
|---|
| exposure | Visibility level | public, internal, private |
| domain | Architecture-defined domain aligned with IDP's application catalogue | Technology concept, not org/team |
| messagename | Describes the payload purpose | PascalCase (see rules below) |
| version | Version number | v1, v2, … (increment only on breaking changes) |
Casing & Characters
exposure, domain, version: lowercase ASCII letters and digits only
messagename: PascalCase (e.g., TransactionAuthorised, TransactionEntity, RefundCommand)
- Separator is a dot (
.). No spaces, no underscores
- Keep names succinct and meaningful
Examples
public.payments.TransactionAuthorised.v1
internal.onboarding.MerchantEntity.v2
private.hospitality.CreateBookingCommand.v1
2. Exposure Types
| Exposure | Who can consume | Typical use | Notes |
|---|
public | Partners / non-company products | Open integration events and canonical entities | Treat as product interface; harden contract, docs, versioning, observability |
internal | Any company product | Cross-domain integrations within the company | Avoid leaking domain-internal details/implementation artefacts |
private | Products within the same domain | Internal choreography/orchestration | Expect known consumers and tighter change coordination |
3. Domain
- Domain is assigned by the Architecture team and must match the IDP catalogue at:
- Do NOT create domains ad hoc
- Reuse existing domains or request one if truly needed
- Domains represent technology groups that support a business function, not teams (those can change too fast)
4. Message Types
Pick one of three shapes per topic:
Event (Notification)
Something happened to an entity.
| Aspect | Guidance |
|---|
| Naming | Noun + verb in past tense: TransactionAuthorised, InvoiceIssued, MerchantCreated |
| Payload | Minimal-but-sufficient event data + identifiers; avoid duplicating full entity unless needed |
Snapshot (Latest State)
Full current representation of an entity.
| Aspect | Guidance |
|---|
| Naming | Suffix Snapshot: TransactionSnapshot, CustomerSnapshot |
| Payload | Canonical, complete state for consumers that need read models |
Command (Directive)
Request to do something (orchestration).
| Aspect | Guidance |
|---|
| Naming | Suffix Command: RefundCommand, RecalculateFeesCommand |
| Ownership | The consumer owns the contract (command handler) |
Important: Choose one shape per topic; don't mix event, snapshot, and command semantics on the same topic.
5. Versioning Rules
| Rule | Description |
|---|
Start at v1 | All new topics begin with version 1 |
| Increment only for breaking changes | Removing fields, changing meaning/type incompatibly, structural changes |
| Non-breaking changes do NOT bump version | Additive fields, optional data |
Breaking Change Process
- Create a new topic with the next version
- Dual-publish (if needed) for a transition period
- Communicate the deprecation timeline
- Remove the old version once consumers migrate
Rationale: Keep consumer upgrades explicit and safe; avoid silent breakage.
6. Contract Ownership & Placement
| Message Type | Contract Owner |
|---|
| Events / Snapshots | Producer owns the contract (schema, docs, examples) |
| Commands | Consumer (command handler) owns the contract |
Placement Guidelines
- In the absence of a centralised contracts repository, keep the contract close to the owner
- Producer's repo for events/entities
- Consumer's repo for commands
- CDC is currently considered technical debt as per the API-Driven and Event-Driven Communication standard
- Adoption is not recommended
- Consult the Architecture team before using it or integrating with CDC-related topics
7. Do's ✅
| Practice | Description |
|---|
| Check with Architecture team | Confirm which domain fits best — don't invent new ones |
| Choose correct exposure | Private = known consumers, tighter control; Internal/Public = avoid leaking internal logic |
| Keep contracts discoverable | README, schema, examples; include change logs and deprecation notes |
8. Don'ts ❌
| Anti-Pattern | Why |
|---|
| Application names in message name | We are looking for an evolving platform and decoupled producers/consumers; app-specific names age poorly |
| Mixed semantics on same topic | Don't combine event + command + snapshot on one topic |
| Leak implementation details | Don't expose internal IDs with meaning, internal error codes, or transient states into public/internal contracts |
9. Examples by Scenario
Event Example
internal.payments.TransactionCaptured.v1
When a capture is completed; payload includes transactionId, amount, currency, capturedAt, …
Snapshot Example
internal.hospitality.ProductSnapshot.v1
Full product entity for search/indexers; payload is the canonical product model.
Command Example
private.taxfree.GenerateVoucherCommand.v1
Directive to tax-free voucher engine; payload includes relevant information for the voucher to be created.
10. Quick Reference
| Question | Answer |
|---|
| Who assigns domains? | Architecture team |
| When to bump version? | Only on breaking changes |
| Who owns event contracts? | Producer |
| Who owns command contracts? | Consumer |
| Can I mix message types? | No, one type per topic |
| Can I use CDC? | Consult Architecture first (considered technical debt) |