Event-driven architecture patterns help teams move work between services without tying every operation to a synchronous request. This practical catalog explains how event notification, event-carried state transfer, event sourcing, CQRS, choreography, orchestration, and the transactional outbox solve different problems. It also provides a repeatable way to track reliability, consistency, cost, and operational complexity as your system changes.
Overview
In an event-driven system, a service publishes a fact or request and one or more consumers react to it. A broker, event streaming platform, message queue, or managed pub/sub service may carry the message, but the platform does not determine the architecture by itself. The important decisions are what an event means, who owns the resulting state, how consumers recover, and how the system behaves when delivery is delayed or duplicated.
These patterns are complementary rather than mutually exclusive. A service might use a transactional outbox to publish reliable domain events, event-carried state transfer to reduce read-time calls, and orchestration to coordinate a multi-step workflow. Choosing the right combination is usually more useful than searching for a single “best” pattern.
| Pattern | Best fit | Main trade-off |
|---|---|---|
| Event notification | Tell another service that something happened | Consumers may need a follow-up API call for details |
| Event-carried state transfer | Give consumers the data needed to build a local view | Payloads and schemas require careful versioning |
| Event sourcing | Retain an append-only history as the system of record | Queries, corrections, and projections become more involved |
| CQRS | Separate write behavior from read models | Introduces synchronization and eventual consistency concerns |
| Choreography | Allow services to react independently to events | End-to-end behavior can be difficult to see |
| Orchestration | Coordinate a visible, multi-step workflow | The orchestrator becomes an important dependency |
| Transactional outbox | Publish database-backed changes reliably | Requires an outbox relay, cleanup, and duplicate handling |
Start with the business workflow, not the broker. Document the command or change that begins the flow, the events that represent durable facts, the consumers that depend on them, and the recovery path when a step fails. This produces a clearer event-driven system design than beginning with topics, partitions, or subscription names.
What to track
1. Event meaning and ownership
For every important event, record its producer, owner, purpose, schema, retention requirement, and expected consumers. Distinguish an event from a command. “OrderPlaced” describes a fact that should not be silently reinterpreted; “ReserveInventory” asks a particular service to perform work. Mixing these meanings makes pub/sub architecture harder to reason about and can create accidental coupling.
Track whether each event is authoritative or derived. An authoritative event represents a change in the owning service. A derived event or projection can be rebuilt from earlier data, but the distinction should be explicit. Review ownership whenever a service begins writing another service’s state directly or consumers start relying on undocumented fields.
2. Delivery and failure behavior
Write down the intended message delivery guarantee for each workflow: at-most-once, at-least-once, or a stronger guarantee supported by the chosen design. In practice, at-least-once delivery means consumers should be idempotent. A consumer can use an event identifier, business key, or stored processing record to recognize a duplicate before applying the same effect twice.
Track retry counts, processing age, dead-letter volume, redelivery rate, and the time between publication and successful handling. A growing dead-letter queue is not merely a broker metric; it may indicate an invalid schema, a permissions problem, an unavailable dependency, or a poison message. Define who owns investigation and whether messages can be safely replayed.
For a deeper review of delivery choices, use the Message Delivery Guarantees Explained guide. Its principles also apply when selecting between message queue solutions, streaming systems, and managed pub/sub services.
3. Consistency and workflow visibility
Record where eventual consistency is acceptable and where the user or business process requires a visible outcome. CQRS can make read-heavy or specialized views easier to serve, but the read model may temporarily lag behind the write model. That delay should have a defined operational threshold and a user-facing treatment, such as a pending status.
For multi-service workflows, track whether choreography or orchestration provides enough visibility. Choreography is often straightforward when services have small, independent reactions. Orchestration is easier to inspect when there are explicit steps, timeouts, compensating actions, and approval points. Neither removes the need to design failure handling.
4. Schema and compatibility health
Monitor schema changes alongside application releases. Check whether producers and consumers agree on required fields, default behavior, data types, and compatibility rules. Prefer additive changes when possible, and test old consumers against new events before release. A schema registry tutorial can help establish a repeatable approach for versioning events in streaming systems.
5. Operational cost and complexity
Track the number of topics or queues, consumer groups, subscriptions, replay procedures, infrastructure dependencies, and on-call runbooks. Complexity grows when every integration creates a new event variant or when the same business fact is translated repeatedly. Also review throughput, backlog, storage or retention usage, connection counts, and consumer lag according to the capabilities of your platform. The goal is not to minimize every number; it is to identify complexity that no longer earns its operational cost.
Cadence and checkpoints
A lightweight review cadence keeps an event-driven architecture aligned with the business instead of turning into a one-time diagram.
- Per release: Review event contracts, ownership, compatibility, idempotency, and rollback or replay procedures for changed producers and consumers.
- Monthly: Inspect backlog, consumer lag, retry and dead-letter patterns, processing latency, failed workflows, and changes in event volume. Confirm that alerts still correspond to meaningful user or business impact.
- Quarterly: Revisit service boundaries, high-value workflows, retention requirements, access permissions, disaster recovery exercises, and the number of inactive topics or subscriptions.
- After an incident: Trace the full path from producer to consumer. Update the runbook, event ownership record, alerting, and recovery test rather than correcting only the immediate symptom.
Keep a small architecture register with one row per workflow. Useful columns include business purpose, initiating action, authoritative service, event names, delivery expectation, consistency window, retry policy, dead-letter owner, replay method, and last review date. This turns architecture review into a manageable operational habit.
For metric selection and alert design, compare your register with the Kafka observability checklist, even if your implementation uses another event streaming platform. The underlying questions—lag, saturation, errors, and capacity—are broadly applicable.
How to interpret changes
Not every increase means the architecture is failing. Higher event volume may reflect healthy product growth, while higher consumer lag may be expected during a planned batch operation. Interpret metrics against business volume, service-level objectives, deployment history, and dependency health.
Use these diagnostic questions:
- Is the producer faster, or is the consumer slower? A backlog may come from traffic growth, inefficient processing, a downstream API limit, or a failed deployment.
- Are failures isolated to one event type or consumer? A narrow pattern suggests a contract or handler issue; broad failures may indicate broker, network, authentication, or dependency problems.
- Can the workflow be replayed safely? If not, document the manual correction process and prioritize idempotency or a compensating action.
- Did a pattern create hidden coupling? If many consumers must change whenever one producer changes, event-carried state transfer or the schema design may need review.
- Is the pattern still proportionate? Event sourcing and CQRS can be valuable where history, auditability, or read/write specialization matters. They can add unnecessary complexity to a small workflow with simple consistency needs.
When comparing event choreography versus orchestration, look for the source of delay. If teams cannot determine which step is pending, an explicit coordinator, workflow state, or trace may help. If a central coordinator has become a bottleneck for unrelated reactions, some flows may be better expressed as independent subscriptions.
Use distributed tracing or correlation identifiers to connect a user request, published event, consumer attempt, external call, and resulting state change. Logs without a shared identifier often show individual successes while hiding an incomplete end-to-end workflow.
When to revisit
Revisit the architecture on a monthly operational cadence and conduct a deeper review each quarter. Bring the review forward when event volume changes materially, a new consumer is added, a service boundary moves, a workflow begins crossing organizational or trust boundaries, or a platform migration is planned.
Also revisit the design after repeated retries, unexplained dead letters, rising lag, duplicate side effects, schema incompatibilities, or manual reconciliation. These symptoms often indicate a mismatch between the chosen pattern and the workflow’s actual needs. For example, a transactional outbox may be appropriate when a database change and event publication must remain aligned; it does not by itself solve consumer idempotency or downstream ordering.
Use this short action plan:
- Choose one important workflow and draw its producer, broker, consumers, state stores, and external dependencies.
- Label each message as a command, notification, or durable domain event.
- Document ownership, schema compatibility, delivery expectation, retry policy, and replay safety.
- Measure lag, processing age, failure rate, dead-letter volume, and end-to-end completion time.
- Review the results monthly and record one concrete improvement, such as an idempotency key, clearer ownership, a compatibility test, or a recovery drill.
As the system evolves, update the architecture register rather than relying on an old diagram. This recurring practice keeps event-driven system design understandable, supports safer realtime application changes, and makes the choice between queue, stream, choreography, and orchestration easier to defend.