Hermes Wiki
Developer/CommunicationPatterns/MessageQueues/Fundamentals/message-queues-vs-event-streaming

Message Queues vs Event Streaming

Concept

Both patterns decouple a producer from a consumer through an intermediary, but they solve different problems and have fundamentally different delivery semantics. A message queue (SQS, RabbitMQ, ActiveMQ) models work as discrete tasks to be consumed once and then gone — a message is delivered to a consumer, that consumer processes and acknowledges it, and the message is removed from the queue. If ten consumers pull from the same queue, each message goes to exactly one of them (competing-consumers pattern) — the queue is a work-distribution mechanism. Event streaming (Kafka, Kinesis) models data as a durable, ordered, append-only log that multiple independent consumers can each read at their own pace, each tracking their own read offset — the same event can be consumed by an analytics pipeline, a fraud-detection service, and a notification service simultaneously, none of them removing it from the log or affecting the others' view of it.

The practical distinction: queues answer "get this task done, exactly once, by someone" — the classic case is a job to process (resize an image, send an email, charge a card). Streams answer "record that this happened, and let anyone who cares replay or subscribe to the history" — the classic case is an event other systems need to react to (an order was placed, a price changed) where multiple independent consumers each need their own full view, and where being able to replay history from an arbitrary point (reprocessing, backfilling a new consumer, debugging) has real value that a queue's delete-on-consume model doesn't offer.

Tradeoffs

Property Message Queue (SQS/RabbitMQ) Event Stream (Kafka/Kinesis)
Consumption model Each message consumed by one consumer, then removed Each event retained for a window (hours to indefinite); many independent consumers can each read the full stream
Ordering Often best-effort or per-message-group only Strict ordering guaranteed within a partition
Replay Not supported — once consumed and acked, it's gone First-class — a consumer can rewind to any retained offset
Fan-out to N independent consumers Requires N separate queues (typically via a fan-out topic/exchange) Native — every consumer group gets its own offset into the same log
Operational complexity Lower — simpler mental model, less infrastructure to run/tune Higher — partitioning, consumer group rebalancing, retention tuning all add real operational surface
Best fit Discrete units of work to be done exactly once Facts about what happened, to be observed by however many interested parties

The core tension is simplicity versus reusability of the data: a queue is simpler to reason about and operate for pure task distribution, but a stream's durable, replayable, multi-consumer log becomes disproportionately valuable the moment more than one system needs to react to the same event, or the moment "replay from history" (a new service backfilling, a bug requiring reprocessing) becomes a real requirement instead of a hypothetical one.

When to use / when not to

  • Use a message queue for background job processing where exactly one worker should handle each task and there's no need for other systems to independently observe the same events — a thumbnail-generation queue, an email-send queue.
  • Use event streaming when more than one downstream system needs to react to the same event independently, or when replay/reprocessing has real operational value — order events feeding both a fulfillment service and an analytics pipeline, application state changes feeding an audit log.
  • Don't reach for Kafka/Kinesis by default for simple task distribution — the operational overhead (partition management, consumer group tuning, retention policy) isn't justified when SQS-style competing-consumers is all the problem actually calls for.
  • Don't force a queue to do a stream's job by fanning out to N queues per event — it works, but as consumer count grows it becomes a maintenance burden a native multi-consumer log doesn't have.

Common pitfall

Choosing Kafka because it's the industry-trendy choice for "event-driven architecture," without a second consumer group that actually needs independent replay — paying the operational cost of a distributed log for what is, in practice, a single-consumer task queue. The tell is a Kafka topic with exactly one consumer group that always reads from the latest offset and never rewinds — that's a queue wearing a stream's operational overhead.

Engineering Lens

The decision isn't "which technology is better" — Kafka isn't strictly superior to SQS, it's solving a different problem at a different operational cost. The Principal-level question to ask in a design review is "does more than one independent system need to react to this event, and does replay have real value here" — if the honest answer is no on both counts, a queue is the right call and reaching for a stream is over-engineering, not rigor. This maps directly onto Fintech/trading contexts: a stream of trade executions genuinely needs multiple independent consumers (settlement, risk, reporting, audit) each replaying and tracking their own offset, which is exactly the shape event streaming was built for — while a one-off "send this confirmation email" is exactly the shape a plain queue was built for.

Sources

Hermes Wiki