Event-Driven Backend Architecture is a software design paradigm where system components communicate and react to events—discrete, immutable records of state changes—rather than through direct synchronous requests. This architectural style has become foundational in modern distributed systems, powering loose coupling, horizontal scalability, and real-time responsiveness across microservices, cloud platforms, and high-throughput applications. Unlike request-response patterns where services call each other directly, event-driven systems rely on asynchronous message passing through brokers, streams, or queues, allowing producers and consumers to operate independently at their own pace. The key mental model to internalize: events are facts about what happened, immutable and append-only, forming the source of truth from which all other state is derived. This inversion—where the history of changes becomes more important than the current snapshot—unlocks audit trails, temporal queries, and resilient failure recovery, but demands careful attention to ordering, idempotency, eventual consistency, and schema evolution as long-lived contracts between services.
What This Cheat Sheet Covers
This topic spans 15 focused tables and 142 indexed concepts, 130 flashcards. Below is a complete table-by-table outline of this topic, spanning foundational concepts through advanced details.
A jump-to index of every table row in this cheat sheet.
An interactive map of every table and concept in this topic.
Table 1: Core Architectural Patterns
These are the high-level design patterns that define how event-driven systems are structured and how services coordinate. Start here to understand the vocabulary before choosing tools or platforms. Patterns range from simple event notifications to full event-sourced architectures and distributed transaction coordination.
| Pattern | Example | Description | |
|---|---|---|---|
UserRegistered { userId, timestamp }→ notify subscribers | • Lightweight events that signal something happened • consumers fetch additional data if needed • minimizes coupling — publisher doesn't know what consumers do • increases network chattiness as consumers must query for details | ||
OrderPlaced { orderId, items[], total, customer }→ full order data in event | • Events contain complete state snapshot needed by consumers, eliminating follow-up queries • reduces coupling and latency but increases event size • enables local caching by consumers for immediate reads | ||
AccountOpened, MoneyDeposited, MoneyWithdrawn→ replay to get current balance | • Persists all state changes as immutable events instead of current state; application state derived by replaying the event log • enables complete audit trail, temporal queries, and rebuilding read models • best for financial systems, compliance, and domains where history matters | ||
WriteModel: ProcessCommand()ReadModel: GetData()→ separate models | • Separates write operations (commands) from read operations (queries) using different models • write model stores events; read models are projections optimized for queries • adds eventual consistency between reads and writes; pairs naturally with Event Sourcing | ||
OrderCreated → InventoryReserved → PaymentCharged→ each service reacts to previous event | • Decentralized coordination — each service listens for events and publishes new ones with no central orchestrator • high autonomy and scalability but harder to track workflow state across services | ||
OrderSaga coordinates:ReserveInventory → ChargePayment → ShipOrder | • Centralized coordinator manages distributed transaction sequence; each step can be compensated on failure • clear workflow visibility and easier debugging but creates single point of coordination • uses compensating transactions instead of ACID rollback | ||
INSERT INTO orders ...;INSERT INTO outbox (event_data);→ single DB transaction | • Solves dual-write problem by writing business data and event in the same local DB transaction, then a separate relay publishes to broker • guarantees at-least-once delivery without distributed transactions | ||
DB transaction log → Debezium → Kafka topic→ events from DB changes | • Captures database-level changes from transaction logs and streams them as events without application code changes • different from Event Sourcing — captures storage-level changes, not domain events; 80% of EDA benefit at 20% of complexity | ||
Batch layer (Spark) + Speed layer (Flink) → merge results in serving layer | • Dual-pipeline design: batch layer for accuracy over historical data, speed layer for low-latency approximations • serving layer merges outputs; main drawback is maintaining two codebases with same logic | ||
Single Kafka log + Flink job → reprocess by replaying from offset 0 | • Replaces Lambda's dual layers with a single streaming pipeline backed by a replayable log; reprocessing = replay events through updated job • simpler to operate; enabled by Kafka tiered storage for indefinite event retention | ||
Flink + Apache Paimon + Fluss → unified streaming + lakehouse | • Next evolution combining real-time streaming with Lakehouse analytics in a single unified architecture • Paimon provides near-real-time lakehouse storage; Fluss provides sub-second streaming storage; enables both batch and stream queries on same data |