Event-Driven Architecture Design: [Solution or domain landscape name]
Solution-level design for an event-driven architecture that spans
many producers, consumers and event APIs: business events and domains,
event types and their semantics, broker topology and clusters, topic
and channel naming, partitioning, the event catalogue, envelope and
schema registry with compatibility modes, delivery, ordering and
idempotency, the transactional outbox and change data capture,
retention and replay, security and access control, observability and
lineage, governance and capacity. Contracts follow the AsyncAPI
Specification 3.1.0 and the CloudEvents 1.0.2 envelope; pattern names
follow Enterprise Integration Patterns. Replace every [bracketed]
value and every row marked Example.
Document control
| Field | Value |
|---|
| Document ID | [EDA-001] |
| Landscape or programme | [Name] |
| Broker estate | [Product and clusters covered] |
| Version | [0.1] |
| Status | [Draft / In review / Approved / Superseded] |
| Owner | [@eda-lead] |
| Approvers/reviewers | [@chief-architect], [@platform-lead], [@security-architect], [@data-governance-lead] |
| Classification | [Internal / Confidential] |
| Last updated | YYYY-MM-DD |
| Related documents | [Integration strategy], [Event API specifications], [Event storming summary] |
Approvals
| Role | Name | Decision | Date |
|---|
| Chief architect | [@name] | [Approve / Approve with conditions / Reject] | YYYY-MM-DD |
| Platform owner | [@name] | [Decision] | YYYY-MM-DD |
| Security | [@name] | [Decision] | YYYY-MM-DD |
| Data governance | [@name] | [Decision] | YYYY-MM-DD |
Revision history
| Version | Date | Author | Change summary |
|---|
| 0.1 | YYYY-MM-DD | [@name] | First draft |
| 1.0 | YYYY-MM-DD | [@name] | Example: Approved; CloudEvents envelope and BACKWARD_TRANSITIVE default adopted |
| 1.1 | YYYY-MM-DD | [@name] | Example: Added a second region with topic mirroring |
Contents
1. Scope, drivers and context
1.1 Drivers
| ID | Driver | Why events |
|---|
| EDR-001 (Example) | Stock must be visible to every channel within seconds | One stock change fans out to many consumers without the WMS knowing them |
| EDR-002 (Example) | Order processing must survive downstream outages | Consumers catch up from the log after recovery |
| EDR-003 (Example) | New consumers (fraud, analytics, loyalty) are added every quarter | Subscribing does not require a change to the producer |
| [EDR-NNN] | [Driver] | [Why events] |
1.2 Scope
| In scope | Out of scope |
|---|
| Example: Business events between order, payment, fulfilment, stock and customer domains | Request and response APIs (see API Design Guidelines, architecture/api-design-guidelines) |
| Example: Change events from the ERP and the WMS | Streaming analytics jobs (see Streaming Solution Design, architecture/streaming-solution-design) |
| Example: Outbound partner webhooks fed from internal events | Webhook delivery platform internals (system-design/design-webhook-delivery) |
| [Item] | [Item] |
1.3 Context
The context view shows producers, the broker estate and the main consumers for the worked example.
2. Business events and domains
| Domain | Owning team | Key business events | System of record |
|---|
| Order (Example) | @team-order | OrderPlaced, OrderCancelled, OrderCompleted | Order service |
| Payment (Example) | @team-payment | PaymentAuthorised, PaymentCaptured, RefundIssued | Payment service |
| Fulfilment (Example) | @team-fulfilment | ShipmentDispatched, ShipmentDelivered | WMS |
| Stock (Example) | @team-stock | StockLevelChanged, StockReserved | WMS and ERP |
| Customer (Example) | @team-customer | CustomerRegistered, CustomerConsentChanged | CRM |
| [Domain] | [@team] | [Events] | [System] |
Rules for business events:
- Name each event as a past-tense fact in the language of the domain.
- One owning domain per event type; only the owner publishes it.
- An event describes what happened, not what a consumer should do.
- Publish an event only after the change it describes is committed.
3. Event types and semantics
| Kind | Meaning | Payload | Consumers | Example |
|---|
| Event notification | Something happened; fetch details if needed | Identifiers and minimal facts | Many, unknown to producer | Example: OrderPlaced with order ID and total |
| Event-carried state transfer | Something happened and here is the new state | Full or partial entity state | Many; can build local read models | Example: StockLevelChanged with SKU, location, quantity |
| Domain event (internal) | Fact inside one bounded context | Domain model, may change often | Same domain only | Example: Internal pricing rule applied |
| Integration event (public) | Fact published across domain boundaries | Stable public contract | Other domains | Example: PaymentCaptured |
| Command | Request to one receiver to do something | Instruction and parameters | One known receiver | Example: ReserveStock on a queue |
| Change event (CDC) | Row-level change in a database | Before and after images | Same domain or translation step | Example: inventory table update |
| ID | Rule |
|---|
| EVR-001 (Example) | Only integration events and commands cross domain boundaries; domain events and raw change events stay inside the owning domain |
| EVR-002 (Example) | Commands use point-to-point channels with exactly one consumer group; events use publish-subscribe channels |
| EVR-003 (Example) | Prefer event-carried state transfer for entities that consumers cache; prefer notification for large or sensitive entities |
| EVR-004 (Example) | Never put a reply expectation inside an event; use a request and reply pattern with correlation IDs if a reply is needed |
| [EVR-NNN] | [Rule] |
4. Broker topology
| Cluster | Purpose | Region and zones | Tenancy | Replication |
|---|
| prod-core (Example) | Business events and commands | [Region 1], three zones | Shared by domains, quotas per domain | Replication factor 3, minimum in-sync replicas 2 |
| prod-cdc (Example) | Raw change events and connectors | [Region 1], three zones | Platform team only | Replication factor 3 |
| prod-dr (Example) | Disaster recovery copy of prod-core | [Region 2] | Read only until failover | Asynchronous topic mirroring |
| nonprod (Example) | Development and test | [Region 1] | Shared | Replication factor 3, shorter retention |
| [Cluster] | [Purpose] | [Region] | [Tenancy] | [Replication] |
Decisions to record: managed service or self-run, cross-region model
(active and passive, or active and active with per-region topics),
expected recovery point (mirroring lag) and recovery time, and how
consumer offsets are translated on failover. Link the tiering to
Resilience Tiering and HA/DR Design (architecture/resilience-tiering-ha-dr-design).
5. Topic and channel design
5.1 Naming convention
Pattern: [env].[domain].[event-family].[kind].v[major]
| Part | Values | Example |
|---|
| env | prod, stg, dev | prod |
| domain | Owning domain from section 2 | order |
| event-family | Aggregate or entity | order |
| kind | event, cmd, cdc | event |
| major | Major contract version | 1 |
Example: prod.order.order.event.v1, prod.stock.reservation.cmd.v1,
prod.fulfilment.shipment.event.v2.
Dead-letter topics append .dlq.[consumer-group] to the source topic
name, so each consuming group owns its own dead-letter topic, for example
prod.order.order.event.v1.dlq.fulfilment. Retry topics follow the same
rule with .retry.[consumer-group].
5.2 Topic granularity
| Option | When to use |
|---|
| One topic per event type | Consumers want only one type; types have different retention or security |
| One topic per entity with several event types | Consumers need all changes of an entity in order (the default here) |
| One topic per domain | Very small domains; rarely recommended |
5.3 Partitioning
| Topic | Partition key | Ordering guarantee | Partitions | Reason |
|---|
| prod.order.order.event.v1 (Example) | orderId | All events of one order in order | 24 | Peak 6,000 events per second, consumers process 500 per second per partition, so 12 needed, doubled for growth |
| prod.stock.level.event.v2 (Example) | sku + locationId | Per SKU and location | 48 | High fan-in from stores |
| prod.customer.customer.event.v1 (Example) | customerId | Per customer | 12 | Low volume |
| [Topic] | [Key] | [Guarantee] | [Count] | [Reason] |
6. Event catalogue
| Event ID | Type (CloudEvents type) | Topic | Owner | Schema subject | Version | Classification | Consumers |
|---|
| EVT-001 (Example) | com.example.order.placed.v1 | prod.order.order.event.v1 | @team-order | order.OrderPlaced | 1.3.0 | Internal | Fulfilment, Notifications, Data platform |
| EVT-002 (Example) | com.example.payment.captured.v1 | prod.payment.payment.event.v1 | @team-payment | payment.PaymentCaptured | 1.1.0 | Confidential | Fulfilment, Finance |
| EVT-003 (Example) | com.example.stock.level-changed.v2 | prod.stock.level.event.v2 | @team-stock | stock.StockLevelChanged | 2.0.0 | Internal | Web, Mobile, Marketplaces |
| [EVT-NNN] | [type] | [topic] | [@team] | [subject] | [x.y.z] | [Class] | [Consumers] |
Each entry links to the producing domain's AsyncAPI 3.1.0 document and
its Event API Specification. The catalogue is published in the developer
portal next to HTTP APIs.
7. Envelope and schema governance
7.1 Envelope
All events use the CloudEvents 1.0.2 envelope. In the structured mode
the attributes travel in the payload; in the binary mode, preferred on
the broker, they travel as message headers (prefixed ce_ on Kafka).
| Attribute | Rule in this landscape |
|---|
| specversion | Always 1.0 |
| id | UUID generated by the producer; unique per source; used for deduplication |
| source | URI of the producing service, for example /order-service/prod |
| type | Reverse-domain name from the catalogue ending in the major contract version, which matches the topic major, for example com.example.order.placed.v1 |
| time | Time of the business fact in UTC, RFC 3339 format |
| subject | Entity identifier, for example the order ID |
| datacontenttype | application/avro, application/protobuf or application/json |
| dataschema | URI of the payload schema in the schema registry |
| Extension: traceparent | W3C Trace Context value for end-to-end tracing (CloudEvents distributed tracing extension) |
| Extension: partitionkey | Value used as the partition key (CloudEvents partitioning extension) |
7.2 Serialisation and registry
| Decision | Choice | Reason |
|---|
| Serialisation format (Example) | Avro for internal events, JSON Schema for partner-facing events | Compact, strong evolution rules; JSON for external readability |
| Registry (Example) | One schema registry per environment | Producers and consumers fetch schemas at runtime |
| Subject naming (Example) | [domain].[EventName] | Several event types per topic |
| Default compatibility (Example) | BACKWARD_TRANSITIVE | New schema readable by consumers of all previous versions |
| Override (Example) | FULL_TRANSITIVE for events consumed by partners | Both old and new consumers keep working whatever upgrades first |
7.3 Compatibility modes
| Mode | What it guarantees | Upgrade order |
|---|
| BACKWARD | Consumers using the new schema can read data written with the previous one | Upgrade consumers first |
| BACKWARD_TRANSITIVE | As BACKWARD, checked against all earlier versions | Upgrade consumers first |
| FORWARD | Consumers using the previous schema can read data written with the new one | Upgrade producers first |
| FORWARD_TRANSITIVE | As FORWARD, against all earlier versions | Upgrade producers first |
| FULL and FULL_TRANSITIVE | Both directions, against the previous or all earlier versions | Either order |
| NONE | No check | Not allowed in production |
Mode names are those used by widely deployed schema registries; check
the documentation of the registry you run. Breaking changes need a new
major version, a new topic (.v2), a period of dual publishing and a
deprecation notice following API Deprecation Policy (architecture/api-deprecation-policy).
8. Delivery, ordering and idempotency
| Concern | Platform guarantee | Producer obligation | Consumer obligation |
|---|
| Delivery | At-least-once for all business events | Wait for acknowledgement from all in-sync replicas; enable idempotent producer where supported | Commit offsets only after processing |
| Ordering | Per partition only | Use the partition key from section 5.3 | Process one key at a time; do not reorder in thread pools |
| Duplicates | Possible after retries and rebalances | Stable id across retries | Idempotent Receiver: store processed IDs or use upserts by version |
| Poison messages | Dead Letter Channel per consumer | Validate against schema before publishing | Retry with backoff [3] times, then move to the consumer's .dlq.[consumer-group] topic with error headers |
| Late or out-of-order events | Not prevented across partitions | Include entity version or sequence | Ignore events older than the stored version |
9. Publishing from systems of record
| Technique | How it works | Use when | Watch for |
|---|
| Transactional outbox with polling publisher | Business change and outbox row written in one transaction; a relay polls the outbox and publishes | Application code can be changed; moderate volume | Polling delay, outbox cleanup |
| Transactional outbox with log tailing | As above, but the relay reads the outbox through CDC on the database log | Higher volume, low latency | CDC tooling must be operated |
| Log-based CDC of business tables | Connector reads the database log and emits change events | Packaged or legacy application that cannot be changed | Exposes internal schema; translate to business events before publishing across domains |
| Native events from the application | Packaged application publishes its own events | SaaS or ERP with an event feature | Vendor schema; map to the catalogue type |
| Direct publish after commit (dual write) | Application commits, then publishes | Not allowed for business events | Lost or phantom events on failure |
| Producer | Technique | Translation step | Owner |
|---|
| Order service (Example) | Outbox with log tailing | None, outbox holds the integration event | @team-order |
| WMS (Example) | Log-based CDC | Stream processor maps inventory rows to StockLevelChanged | @team-stock |
| ERP (Example) | Native business events | Adapter maps ERP events to catalogue types | @team-erp |
| [Producer] | [Technique] | [Translation] | [@owner] |
10. Retention, replay and archive
| Topic class | Retention on broker | Cleanup policy | Archive | Replay method |
|---|
| Business events (Example) | 7 days | Delete | Object storage for 13 months, partitioned by date | Reset consumer group offset by time, or reload from archive |
| Entity state topics (Example) | Unlimited | Compact by key | Not needed | New consumers read from the beginning |
| Commands (Example) | 3 days | Delete | None | Not replayed; producers resend |
| CDC raw (Example) | 3 days | Delete | None | Re-snapshot from source |
| Dead letter (Example) | 14 days | Delete | Ticket attached | Replay tool after fix |
| [Class] | [Retention] | [Policy] | [Archive] | [Method] |
Replay rules: replays use a dedicated consumer group or a flag in the
event headers so that side effects (emails, payments) are not repeated;
every replay is logged with requester, range and reason. For personal
data, prefer notification events with identifiers, or crypto-shredding
(per-subject keys deleted on erasure), and record the approach in the
DPIA (compliance/dpia).
11. Security and access control
| Control | Rule |
|---|
| Authentication | Each producer and consumer has its own identity (mutual TLS certificate or OAuth 2.0 client credentials), issued by the platform |
| Authorisation | Access control lists per topic: write for the owning domain only, read per approved consumer, granted through a request in the catalogue |
| Encryption in transit | TLS on every client and inter-broker connection |
| Encryption at rest | Broker storage encrypted with platform-managed or customer-managed keys |
| Payload protection | Field-level encryption or tokenisation for card and special-category fields; keys held outside the broker |
| Schema registry | Write access for CI pipelines only; humans read |
| Admin | Topic creation only through infrastructure as code with review |
| Audit | Admin actions, ACL changes and replays logged and retained [13 months] |
12. Observability and lineage
| Signal | Measure | Alert threshold (Example) |
|---|
| Consumer lag | Messages or seconds behind the head, per consumer group and partition | Above 60 seconds for 5 minutes on tier 1 consumers |
| End-to-end latency | Event time to consumer completion, 95th percentile | Above 5 seconds for stock events |
| Dead letter arrivals | Count per consumer | Any arrival on tier 1 |
| Producer errors | Failed publishes and retries | Above 1 % for 5 minutes |
| Under-replicated partitions | Count | Any for more than 5 minutes |
| Schema rejections | Failed registrations in CI and at runtime | Any in production |
| Outbox backlog | Unpublished outbox rows | Older than 2 minutes |
Lineage: the catalogue records producer, topic and consumer groups for
every event type; trace context in the envelope links spans across
hops; the data platform records which datasets are built from which
topics.
13. Capacity and service levels
| Input | Value (Example) |
|---|
| Average event rate, all topics | 1,500 events per second |
| Peak event rate | 6,000 events per second |
| Average event size including envelope | 1 KB |
| Retention for business events | 7 days |
| Replication factor | 3 |
Example calculation: 1,500 events per second x 1 KB x 86,400 seconds
= 129.6 GB per day; x 7 days = 907.2 GB; x 3 replicas = about 2.7 TB of
broker storage, plus [30 %] headroom = about 3.5 TB. Peak ingress is
6,000 x 1 KB = 6 MB per second before replication.
| Tier | Example topics | Availability | End-to-end latency, 95th percentile | Recovery point | Recovery time |
|---|
| Tier 1 (Example) | Order, payment, stock | 99.95 % | 2 seconds | Mirroring lag below 30 seconds | 30 minutes |
| Tier 2 (Example) | Customer, notifications | 99.9 % | 30 seconds | 5 minutes | 4 hours |
| Tier 3 (Example) | Analytics feeds | 99.5 % | 15 minutes | 1 hour | 24 hours |
14. Governance and lifecycle
| Activity | EDA lead | Platform team | Producing domain | Consuming domain | Security | Data governance |
|---|
| Event naming and envelope rules | A, R | C | C | I | I | C |
| New event type and schema | C | I | A, R | C | C | C |
| Topic provisioning and quotas | I | A, R | R | I | C | I |
| Consumer access request | I | R | A | R | C | C |
| Breaking change and deprecation | C | I | A, R | C | I | I |
| Retention and privacy rules | C | R | C | I | C | A |
Lifecycle: Proposed, Designed (schema and AsyncAPI reviewed), Published
(registered in the catalogue), Deprecated (successor published,
consumers notified, sunset date set), Retired (topic deleted after
archive). A review checklist for every new event type:
15. Risks and decisions
| ID | Type | Description | Response | Owner |
|---|
| RSK-001 (Example) | Risk | Hot partition on popular SKUs during promotions | Composite key with location; monitor partition skew | @team-stock |
| RSK-002 (Example) | Risk | Consumers depend on CDC row formats | EVR-001 enforced in review; translation step owned by producer | @eda-lead |
| RSK-003 (Example) | Risk | Failover loses events within the mirroring lag | Producers can replay from the outbox for the last [1 hour] | @platform-lead |
| DEC-001 (Example) | Decision | CloudEvents binary mode on the broker | ADR-EDA-001 | @eda-lead |
| DEC-002 (Example) | Decision | BACKWARD_TRANSITIVE as default compatibility | ADR-EDA-002 | @eda-lead |
| [ID] | [Type] | [Description] | [Response] | [@owner] |
16. Glossary and references
16.1 Glossary
| Term | Meaning |
|---|
| Event | Immutable record of a business fact that already happened |
| Command | Message asking one receiver to perform an action |
| Topic or channel | Named stream on the broker that carries messages |
| Partition | Ordered subset of a topic; ordering holds only inside it |
| Consumer group | Set of consumer instances that share the work of reading a topic |
| Consumer lag | Distance between the newest message and the consumer's position |
| Compaction | Cleanup that keeps only the latest message per key |
| Outbox | Table written in the business transaction and published by a relay |
| Crypto-shredding | Making data unreadable by deleting the key that encrypted it |
16.2 References
| Reference | Version or date checked | Use |
|---|
| AsyncAPI Specification, asyncapi.com | 3.1.0, released January 2026 | Channel, operation and message contracts |
| CloudEvents specification, CNCF, github.com/cloudevents/spec | 1.0.2 | Envelope attributes in section 7.1 |
| Enterprise Integration Patterns, Hohpe and Woolf, enterpriseintegrationpatterns.com | Site checked YYYY-MM-DD | Pattern names |
| Transactional outbox, polling publisher and transaction log tailing, microservices.io | Checked YYYY-MM-DD | Section 9 |
| Schema evolution and compatibility, schema registry documentation of your product | Checked YYYY-MM-DD | Section 7.3 mode names |
| W3C Trace Context, w3.org/TR/trace-context | Level 1 Recommendation (Level 2 in progress), checked YYYY-MM-DD | traceparent extension |
| [Internal standard] | [Version] | [Use] |
Standard and licence note
Based on public specifications: the AsyncAPI Specification 3.1.0 and the
CloudEvents specification 1.0.2, both published under the Apache License
2.0. Pattern names (Publish-Subscribe Channel, Dead Letter Channel,
Idempotent Receiver and others) come from Enterprise Integration
Patterns by Gregor Hohpe and Bobby Woolf and are used by name with
attribution; the outbox and log tailing techniques are described in
original words with credit to microservices.io. Schema compatibility
mode names follow common schema registry usage; product names are
trademarks of their owners and this template is not affiliated with or
endorsed by any vendor. This template is original text written for this
collection. Check the current official version of every specification
before use. Template licence: MIT.