Event-driven architecture (EDA) is the pattern behind every high-scale system you use daily. When you place an Amazon order, dozens of services — inventory, payment, shipping, notifications — react to a single OrderPlaced event without knowing each other exist. EDA enables this decoupling, horizontal scaling, and fault isolation. This guide explains the patterns and trade-offs.
⚡ TL;DR: Events notify (OrderPlaced). Commands request (ProcessPayment). Queries ask (GetOrderStatus). Use SQS for point-to-point queues, SNS for fan-out, Kafka for ordered event streams with replay. Outbox pattern prevents lost events. Consumer groups enable parallel processing.
Events vs Commands vs Queries — event-driven architecture
// Event: something that happened (past tense, no expected response)
// OrderPlaced, UserRegistered, PaymentFailed, InventoryUpdated
// Producer fires and forgets — doesn't care who consumes
// Command: request to do something (imperative, expects action)
// ProcessPayment, SendEmail, UpdateInventory
// Usually has a single consumer
// Query: request for data (no side effects)
// GetOrderStatus, ListUserOrders
// Usually synchronous REST/GraphQL
// EDA shines for events:
const event = {
type: 'order.placed',
id: uuid(),
timestamp: new Date().toISOString(),
version: '1.0',
data: {
orderId: '12345',
userId: 'u-789',
items: [{ productId: 'p-1', quantity: 2 }],
total: 99.98
}
};
// → Inventory service: reserve stock
// → Payment service: charge card
// → Email service: send confirmation
// → Analytics service: record saleSQS vs SNS vs Kafka — when to use each
// SQS (Simple Queue Service): point-to-point, at-least-once delivery
// - One consumer group processes each message
// - Visibility timeout: message hidden during processing
// - DLQ: failed messages after N retries go here
// Use for: task queues, background jobs, decoupled services
// SNS (Simple Notification Service): fan-out pub/sub
// - One message → many subscribers (SQS, Lambda, HTTP, email)
// - No replay, no ordering guarantee
// Use for: notifications fan-out, cross-service events
// SNS + SQS pattern (reliable fan-out):
// Order Placed → SNS Topic
// → SQS Queue → Inventory Lambda
// → SQS Queue → Payment Lambda
// → SQS Queue → Email Lambda
// Each service gets its own queue with DLQ and retry logic
// Kafka: ordered, replayable event streams
// - Events persisted to disk (configurable retention)
// - Consumer groups: each group gets all events
// - Partitions: parallel consumption within a group
// Use for: event sourcing, audit logs, data pipelines, replayThe Outbox Pattern — no lost events
// Problem: writing to DB and publishing to queue are two operations
// If queue publish fails after DB write: event is lost forever
// WRONG:
async function placeOrder(order) {
await db.saveOrder(order); // Saved
await queue.publish('OrderPlaced', order); // If this fails: lost!
}
// RIGHT: Outbox pattern
async function placeOrder(order) {
await db.transaction(async trx => {
await trx.saveOrder(order);
await trx.saveOutbox({
type: 'OrderPlaced',
payload: order,
status: 'pending'
});
}); // Atomic: both succeed or both fail
}
// Separate process: poll outbox and publish
async function processOutbox() {
const pending = await db.getOutbox({ status: 'pending' });
for (const event of pending) {
await queue.publish(event.type, event.payload);
await db.updateOutbox(event.id, { status: 'published' });
}
}Consumer groups and parallel processing
// Consumer group: multiple consumers sharing partitions
// Each message processed by ONE consumer in the group
// AWS SQS with multiple Lambda instances:
// SQS → Lambda (scales automatically to match queue depth)
// Each Lambda processes different messages in parallel
// Kafka consumer group:
const { Kafka } = require('kafkajs');
const kafka = new Kafka({ brokers: ['kafka:9092'] });
const consumer = kafka.consumer({ groupId: 'order-processor' });
await consumer.subscribe({ topic: 'orders', fromBeginning: false });
await consumer.run({
partitionsConsumedConcurrently: 4, // Process 4 partitions in parallel
eachMessage: async ({ topic, partition, message }) => {
const event = JSON.parse(message.value.toString());
await processOrderEvent(event);
}
});- ✅ Events are immutable facts — never update an event, publish a new one
- ✅ Outbox pattern for reliable event publishing with ACID guarantees
- ✅ SQS DLQ for every consumer — catch and investigate failed messages
- ✅ Idempotent consumers — handle duplicate delivery gracefully
- ✅ Consumer group ID per service — all instances share work
- ❌ Never lose events by publishing without persistence
- ❌ Never use events for synchronous operations that need immediate response
Event-driven architecture powers the AWS fraud detection architecture and the clickstream analytics pipeline. External reference: Martin Fowler on event-driven architecture.
Recommended Reading
→ Designing Data-Intensive Applications — The essential book every senior developer needs.
→ The Pragmatic Programmer — Timeless engineering wisdom for writing better code.
Affiliate links. We earn a small commission at no extra cost to you.
Free Weekly Newsletter
🚀 Don’t Miss the Next Cheat Code
Join 1,000+ senior developers getting expert JS, Python, AWS and system design secrets weekly.
Related: Building AI Agents That Actually Work: The Architecture No Tutorial Shows You · Hypothesis for Distributed Systems: Model Partitions and Message Reordering · How Netflix Handles 250M Users: Event-Driven Video Processing Architecture on AWS
Related: Day 52: Blackboard vs Message Bus Orchestration · Day 2: Transformer Attention as a Systems Diagram · Building Production AI Agents: Tool Calling, Memory, and Orchestration That Actually Work
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- REST API Design Best Practices: The Patterns That Make APIs a Joy to Use
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
