A question I often get: "Do you use message-driven architecture just because REST APIs are slow?" - Well… yes, but actually no. There’s much more behind that decision.
Why use Message-Driven architecture
Cross-device support
The messaging systems allow for real-time communication across servers, desktops, web & mobile apps — all decoupled and event-driven.
Asynchronous communication
REST API forces the sender to wait. Messaging lets you offload heavy work to background consumers while the app stays responsive.
Supports monoliths
The producer-consumer architecture can be used within a single service using in-process events.
Multi-destination delivery
With REST APIs, the sender needs to know the exact endpoint of each receiver. Messaging system publishes to a topic — whoever’s subscribed gets the message. No need to know who or where they are.
Retention
REST API fails if the receiving server is down. Messaging brokers store messages until consumers reconnect. With TTL, you can control how long unconsumed messages are retained before they are discarded.
Fault tolerance
Retries and dead‑letter queues are ideal for systems that favor built‑in recovery mechanisms to reduce the need for manual intervention.
But is messaging suitable for every backend?
No. You should also be aware of the drawbacks.
Drawbacks of MDA
Extra complexity
You need to manage brokers, consumers, retry policies, and observability.
Tracking requests is harder
Messages can fan out to multiple destinations. Tracing who picked up what, when, and what happened next is a challenge.
And because messaging is inherently push‑based...
No immediate response
Publishers don't wait for a response; the server can’t guarantee instant feedback to user actions.
Eventual consistency
Messaging systems can always send requests, but the downed services need to catch up later. This is not ideal for systems that require strict transactional processes.
Idempotency
The retry mechanism may redeliver the same message when a consumer fails or times out. The consumers must be idempotent to avoid processing the same request multiple times.
Idempotency doesn't spare REST APIs either.
Every software architecture is a trade‑off. Which direction do you lean into?
If you’re curious to dive deeper into Messaging or Event‑Driven Architecture, I’ve written a handful of articles on the topic.
Top comments (4)
the idempotency point hits different in payment systems. REST can have the same problem, but at least failure modes are obvious. with messaging, a consumer crash mid-processing can leave you with duplicate side effects that are nearly impossible to detect without audit logs. one pattern I've seen work well: storing a processed_message_ids table and checking before committing state changes. adds a small write overhead but makes redelivery safe. have you found a cleaner approach to consumer idempotency?
I've used the inbox pattern.
Set a unique ID (or correlation ID) as the message header
Save it into the inbox table after you complete processing
If the same ID occurs, automatically acknowledge the message
Yes. DB operations are overhead, and what if you cannot insert or update for some reason? If not a table, push it into a different queue (or Kafka topic) and peek to see if the message is already there. Take the latter with a grain of salt.
Or enforce exactly once delivery by always acknowledging messages on the consumer side. But what happens to messages that are lost? Apply the outbox pattern to the producer. To avoid the dual-write problem, store all messages in a table with unique message IDs and use CDC tools to move newly inserted records to the message broker.