Event-Driven Architecture: Patterns and Pitfalls
A deep dive into event-driven systems. Learn about event sourcing, CQRS, and how to handle the challenges of eventual consistency in distributed systems.

Why Event-Driven?
Event-driven architecture enables loose coupling, scalability, and resilience. But it introduces complexity that must be carefully managed.
Core Patterns
1. Event Notification
Simple events that signal something happened:
interface OrderCreatedEvent {
type: "ORDER_CREATED";
orderId: string;
timestamp: Date;
}
Consumers fetch additional data as needed. Simple but creates coupling.
2. Event-Carried State Transfer
Events contain all relevant data:
interface OrderCreatedEvent {
type: "ORDER_CREATED";
orderId: string;
customerId: string;
items: OrderItem[];
totalAmount: number;
shippingAddress: Address;
timestamp: Date;
}
Reduces queries but increases event size.
3. Event Sourcing
Store state as a sequence of events:
class OrderAggregate {
private events: OrderEvent[] = [];
create(command: CreateOrderCommand) {
this.apply(new OrderCreatedEvent(command));
}
private apply(event: OrderEvent) {
this.events.push(event);
this.mutate(event);
}
private mutate(event: OrderEvent) {
switch (event.type) {
case "ORDER_CREATED":
this.status = "pending";
break;
case "ORDER_SHIPPED":
this.status = "shipped";
break;
}
}
}
Handling Eventual Consistency
The Problem
In distributed systems, consumers process events asynchronously:
User creates order → Order Service (committed)
↓
Event published
↓
Inventory Service (processing...)
↓
User checks inventory → Still shows old data!
Solutions
1. Optimistic UI Updates
async function createOrder(data) {
// Update UI immediately
updateLocalState(data);
// Then persist
await api.createOrder(data);
}
2. Polling with Backoff
async function waitForConsistency(orderId: string) {
for (let i = 0; i < 5; i++) {
const result = await checkOrderStatus(orderId);
if (result.synced) return result;
await sleep(Math.pow(2, i) * 100);
}
throw new Error("Sync timeout");
}
Common Pitfalls
Conclusion
Event-driven architecture is powerful but complex. Start simple, add patterns as needed, and invest heavily in observability.


