Event-Driven Architecture Cheat Sheet

Event-driven architecture patterns — event sourcing, CQRS, pub/sub, event buses, dead letter queues, ordering guarantees, and when to choose events over request-r.

Last Updated: May 1, 2025

Core Event Patterns

ItemDescription
Event NotificationLightweight: 'something happened, here's the ID.' Consumer calls back for details. Decoupled but requires API availability.
Event-Carried State TransferHeavyweight: event contains all data consumer needs. Consumer self-sufficient, no callback needed. Trade-off: larger payloads, potential staleness.
Event SourcingPersist state as a sequence of immutable events (not current state). Current state = fold over all events. Enables time-travel debugging, audit logs, and CQRS.
CQRS (Command Query Responsibility Segregation)Separate read model from write model. Commands mutate state → produce events → read models updated asynchronously. Optimize reads and writes independently.
Saga PatternDistributed transaction via orchestration or choreography of events. Each step emits event; compensating transactions undo steps on failure. See: distributed-transactions sheet.

Message Broker Comparison

BrokerDelivery GuaranteeOrderingThroughputBest For
Apache KafkaAt-least-once / Exactly-oncePer-partition strictExtreme (1M+ msg/s)Stream processing, log-based, replayable
RabbitMQAt-least-once (ack)Per-queue FIFOHigh (50K msg/s)Task queues, routing, complex topologies
AWS SQSAt-least-onceBest-effort (FIFO available)Elastic (auto-scale)Serverless, simple queueing, AWS ecosystem
Google Pub/SubAt-least-oncePer-key orderingGlobal scaleGCP-native, push subscriptions, auto-scaling
NATS / JetStreamAt-least-oncePer-subjectVery high (low latency)Edge/IoT, low-latency, lightweight
Azure Event HubsAt-least-oncePer-partitionVery highAzure ecosystem, big data pipelines

Event Design Principles

ItemDescription
Use past-tense namingOrderPlaced, PaymentCaptured, ShipmentDelivered. Events are immutable facts. This prevents confusion with commands (PlaceOrder, CapturePayment).
Schema evolutionAdd fields, never remove or rename. Use Avro/Protobuf with a schema registry. Semantic versioning: breaking changes = new event type (OrderPlacedV2).
Event identityEvery event needs a globally unique ID and a timestamp. Enables deduplication and ordering. Event ID ≠ entity ID (many events per entity).
Partitioning keyChoose partition key that groups related events. OrderID ensures all events for one order process in order. Avoid hot partitions (key skew).
Idempotent consumersEvery consumer must assume duplicate events. Use event ID + consumer name as dedup key in a processed_events table.

Failure Handling

Dead Letter Queue (DLQ)
Poison messages that can't be processed → routed to DLQ. Alert on DLQ depth > 0. Manually inspect, fix, and replay.
Retry with backoff
Exponential backoff: 1s, 2s, 4s, 8s, max 5min. Add jitter (±25%) to prevent thundering herd. Max retries: 3-5 before DLQ.
Circuit breaker on consumers
If downstream is down, stop consuming. Kafka: pause() the consumer. Prevents backpressure from killing the consumer group.
Outbox pattern
Write event to outbox table in same DB transaction as state change. Separate process polls outbox and publishes to broker. Guarantees at-least-once publish.
Exactly-once with Kafka
enable.idempotence=true on producer. read_committed isolation on consumer. Store offsets + results in one transaction via Kafka Streams / kafka-transactions.
Pro Tip: Events describe facts that happened (past tense): OrderPlaced, not PlaceOrder. This semantic distinction prevents coupling — producers declare what happened, consumers decide what to do about it.