Event-Driven Architecture — Async Decoupling via Events
Producers emit events; consumers react asynchronously — zero direct coupling between them.
When to use
- Fan-out: one event needs to trigger multiple independent consumers
- Audit trail or replay required
- Temporal decoupling needed (producer/consumer scale independently)
Tradeoffs
- End-to-end flow tracing requires distributed tracing (harder to debug)
- Eventual consistency — consumers may lag
- Go
- Python
type OrderPlaced struct {
OrderID string
CustomerID string
Total float64
}
func publishOrder(ch chan<- OrderPlaced, evt OrderPlaced) {
ch <- evt
}
func consumeOrders(ch <-chan OrderPlaced) {
for evt := range ch {
fmt.Printf("handling order %s\n", evt.OrderID)
}
}
from dataclasses import dataclass
from queue import Queue
@dataclass
class OrderPlaced:
order_id: str
customer_id: str
total: float
broker: Queue[OrderPlaced] = Queue()
def publish(evt: OrderPlaced) -> None:
broker.put(evt)
def consume() -> None:
while True:
evt = broker.get()
print(f"handling order {evt.order_id}")
Gotcha: Events are facts, not commands. Name them in past tense:
OrderPlaced, notProcessOrder.