design·lab

Architecture

Message Queue + Dead Letter Exchange

Put a durable queue between producer and consumer so spikes, crashes, and "poison" messages never take the whole system down.

✗ The problem

Calling the consumer synchronously

The producer calls the consumer directly and waits. One slow dependency, one crash, or one bad message stalls — or loses — everything behind it.

function placeOrder(order) {
  emailService.send(order); // blocks here!
  // spike of orders → emailService overloaded
  // crash mid-call → order is lost forever
  // one bad order → retried forever, blocks everyone
}
🏭 Producer
⚙️ Consumer
slow / crashing
Direct calls couple availability: if the consumer is down or overloaded, the producer is too — and a single poison message can jam the whole line.
✓ How it works

Decouple with a durable queue — and a DLX for failures

Producer publishes and moves on. Consumer pulls at its own pace and acks on success. Repeated failure → nack → retried, then routed to a Dead Letter Exchange instead of blocking the queue.

function consume(msg) {
  try {
    handle(msg);
    channel.ack(msg);
  } catch (e) {
    msg.retries++;
    if (msg.retries >= MAX_RETRIES)
      channel.deadLetter(msg); // → DLX
    else
      channel.nack(msg, true); // requeue
  }
}
🏭 Producer
📬 Queue
durable · buffered
⚙️ Consumer
ack / nack
↓ after N failed retries
☠️ DLX / DLQ
inspect · replay
✓ See it live

Produce, process, and watch a poison message fall through

Message #1 flows normally. Message #2 is "poison" — it fails 3 times, is retried each time, then gets routed to the DLQ instead of blocking the queue.

Follow the numbered messages top-to-bottom — #1 acks cleanly; #2 is nacked 3 times, then dead-lettered instead of stalling the queue.

✓ Takeaway

Buffer, retry, isolate

  • Decouples producer and consumer — neither needs the other to be up right now.
  • Absorbs spikes: the queue is a buffer, so bursts don't overload the consumer.
  • Durability + retries: messages survive crashes and get a bounded number of attempts.
  • DLQ isolates poison messages for later inspection/replay instead of blocking everyone else.
  • You already use it: RabbitMQ (exchanges + DLX), Amazon SQS (redrive policy), Kafka (retry topics).
🎯 Combines / relates: a message is a Command object; delivery is Observer/pub-sub style; enables async, event-driven systems and sagas.