When a StreamHub creator goes live, the stream service publishes stream.started. Notification, analytics, search, and moderation services each react independently — no central orchestrator, no synchronous call chain that breaks when one service is slow.
Events vs commands
| Aspect | Event (past tense) | Command (imperative) |
|---|---|---|
| Naming | stream.started, payment.completed | SendNotification, ProcessPayment |
| Coupling | Publisher doesn't know subscribers | Sender knows receiver |
| Routing | Topic/bus; many consumers | Point-to-point queue |
| Failure | One slow consumer doesn't block others | Blocked queue stalls sender |
| StreamHub | Kafka topic `stream.lifecycle` | RabbitMQ transcode job queue |
Naming
Event (past tense)stream.started, payment.completedCommand (imperative)SendNotification, ProcessPaymentCoupling
Event (past tense)Publisher doesn't know subscribersCommand (imperative)Sender knows receiverRouting
Event (past tense)Topic/bus; many consumersCommand (imperative)Point-to-point queueFailure
Event (past tense)One slow consumer doesn't block othersCommand (imperative)Blocked queue stalls senderStreamHub
Event (past tense)Kafka topic `stream.lifecycle`Command (imperative)RabbitMQ transcode job queue
Event-driven flow
Amazon MSK event pipeline
StreamHub production architecture (AWS)
EDA building blocks
- Event: Immutable record of something that happened, with schema version.
- Event bus / broker: Kafka topic or cloud event bridge routing events to subscribers.
- Event handler: Stateless consumer that reacts to one event type.
- Event store (optional): Append-only log as source of truth (event sourcing).
- Schema registry: Enforces Avro/Protobuf/JSON Schema compatibility across producers.
Choreography vs orchestration
| Style | Control flow | Trade-off |
|---|---|---|
| Choreography | Each service reacts to events; no central coordinator | Simple, decoupled; hard to trace full flow |
| Orchestration | Central saga/workflow engine coordinates steps | Visible flow; orchestrator is a bottleneck/SPOF |
| StreamHub checkout | Choreography for stream lifecycle events | Orchestration for multi-step subscription saga |
Choreography
Control flowEach service reacts to events; no central coordinatorTrade-offSimple, decoupled; hard to trace full flowOrchestration
Control flowCentral saga/workflow engine coordinates stepsTrade-offVisible flow; orchestrator is a bottleneck/SPOFStreamHub checkout
Control flowChoreography for stream lifecycle eventsTrade-offOrchestration for multi-step subscription saga
StreamHub's go-live flow is choreographed: stream service publishes, others react. Subscription upgrade uses orchestration via a saga coordinator because payment + entitlement + email must succeed or roll back together.
Event schema example
{
"specversion": "1.0",
"type": "com.streamhub.stream.started",
"source": "/streams/sh_4420",
"id": "evt_a1b2c3d4",
"time": "2026-04-01T20:00:00Z",
"data": {
"stream_id": "live_9912",
"streamer_id": "sh_4420",
"title": "Friday Night Ranked",
"category": "gaming"
}
}
Handling failures in EDA
Resilience patterns
- Idempotent handlers: Same event processed twice produces the same outcome.
- Dead letter topic: Failed events after N retries go to DLQ for investigation.
- Retry with backoff: Exponential delay prevents thundering herd on downstream recovery.
- Outbox pattern: Write event to local DB outbox in same transaction as business data; separate publisher reads outbox — guarantees no lost events.
- Saga compensations: On failure, publish compensating events (refund, revoke entitlement).
-- Transactional outbox table
CREATE TABLE outbox (
id BIGSERIAL PRIMARY KEY,
event_type VARCHAR(128) NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
published BOOLEAN DEFAULT false
);
Quick recall
Everything you need if you only revisit this box.
- EDA publishes facts; consumers react independently without the producer knowing them.
- Choreography is decoupled; orchestration coordinates multi-step flows with rollback.
- Use Kafka for event streams; pair with schema registry for compatibility.
- Transactional outbox ensures events are never lost relative to DB writes.
- Idempotent handlers and DLQ are mandatory for at-least-once brokers.
- Include trace_id in every event for end-to-end observability.
Test yourself
Answer these before moving on — recall is what makes it stick.