Skip to content
7 min read Moustakime KIFIA

RabbitMQ in a SaaS platform: where to use it, and why

When to use RabbitMQ in a SaaS platform, which flows should go through it, and how it helps decouple provisioning, notifications and selected business workflows without event-driving the whole system.

  • Integrations
  • Engineering
  • Architecture

RabbitMQ is easy to misplace. As soon as a platform grows multiple frontends, multiple APIs, long-running workflows and third-party integrations, the broker starts looking like an answer to everything. That is usually a mistake.

In our case, RabbitMQ is neither the centre of the architecture nor an infrastructure detail living off to the side. It is a decoupling tool for a few specific flows, where synchronous calls start costing more than a message.

Problem to solve

A SaaS platform that combines several frontends, several APIs, business workflows and third-party integrations eventually hits the same needs:

  • launch long-running work without blocking a user request
  • isolate side effects
  • absorb partial service outages
  • move signals between services without coupling them too tightly

So the real question is not “should we use RabbitMQ?”. The real question is: which flows actually deserve a broker.

Chosen solution

The chosen solution is to reserve RabbitMQ for clearly identified flows:

  • provisioning
  • notifications or integrations
  • workflows crossing several technical layers
  • operations that must survive a temporary outage downstream

Everything else stays synchronous when the message adds no clear value. That is the key point: RabbitMQ is useful when it simplifies a boundary, not when it adds one more abstract layer.

RabbitMQ’s role in the platform

RabbitMQ plays three concrete roles here:

  1. Decouple timing between producer and consumer
  2. Make a flow recoverable or replayable
  3. Keep a clean separation between services with different responsibilities

In a platform like this, that means in practice:

  • one service can accept a request and leave the HTTP timeline quickly
  • another service can process the next step on its own lifecycle
  • the system can keep an explicit trace of state before, during and after processing

The broker does not replace application state. It carries a signal. Robustness comes from the pair message + explicit business state.

Why this solution fits this kind of platform

This solution matters because:

  • some operations leave the request/response timeline
  • service responsibilities stay clearer
  • the architecture can grow without turning everything into fragile synchronous calls
  • incidents are easier to recover from when application state is explicit

The point is to present RabbitMQ as an architecture component, not just an infrastructure detail.

Example 1: provisioning

The first place where RabbitMQ earns its keep is provisioning.

When a platform creates a new customer space, the initial request may start in a control-plane API or an operator console. But the next steps are rarely limited to “insert a row in the database”. You often need to:

  • create a local runtime
  • prepare default configuration
  • open the right data perimeter
  • report an actionable status back to the operator

That kind of flow gains very little by staying fully synchronous.

In our implementation, the control-plane service publishes a provisioning message with an explicit contract:

const message: TenantProvisionRequested = {
  idempotencyKey: `${app.uuid}-v1`,
  applicationUuid: app.uuid,
  tenantCode: app.tenantCode,
  tenantName: app.organizationName,
  primaryHostname: `${app.tenantCode}.${domainSuffix}`,
  planCode: app.planCode,
  billingCycle: app.billingCycle as "monthly" | "yearly"
};

What matters here is not RabbitMQ alone. It is the fact that the message stays small and explicit, and that it lives next to visible business states such as payment_confirmed, provisioning, active or failed.

Example 2: notifications and side effects

The second strong use case is notifications and, more broadly, side effects.

When an important event happens in the product, the core business service should not immediately start sending emails, SMS, push notifications or external calls in the same request. Otherwise the primary service becomes too aware of all secondary delivery channels.

The cleaner shape is:

  • business logic validates a meaningful state change
  • it emits a signal
  • a consumer or downstream service decides what to notify, on which channels, and with what priority

Typical candidates in a system like this are:

  • event publication
  • reminders
  • confirmation messages
  • internal signals toward admin or member interfaces

The practical rule is simple: if the primary action must succeed even when the notification channel is temporarily unavailable, a broker becomes a strong candidate.

Example 3: payments

Payments are more subtle.

The initial checkout path should usually not go through RabbitMQ. When a user wants to pay, the system needs an immediate answer: create the order, create the payment, redirect to the provider. That core path stays synchronous.

What can move out of the request are the surrounding pieces:

  • post-payment notifications
  • derived view updates
  • cross-cutting processing that is not critical to the immediate response
  • internal reconciliation or reporting

That is the useful distinction: RabbitMQ is not “the payment solution”. Payments often have a synchronous core with asynchronous satellites around it.

Example 4: invitations and access codes

Invitations and access codes provide another good decision rule.

Code validation, TTL enforcement, attempt limits and IP lockout are better kept synchronous, inside the service that owns the security model. You want an immediate, deterministic decision.

By contrast, the delivery of the code itself can move to async processing if the product grows:

  • generate the code synchronously
  • persist the code and its validity window
  • emit a send message
  • let email or SMS delivery run in a worker or dedicated service

The pattern stays the same: the business decision remains close to the model. The outward transport can move through RabbitMQ.

How we implement it

The right level of code to show is not a bug diff. It is rather:

  • a producer on the NestJS side
  • a consumer or handler on the Symfony Messenger side
  • the message shape itself
  • the application state around the message

The reader should leave with a clear mental model of the broker’s place in the system.

The key is to keep messages sober:

  • no oversized payload
  • no business logic scattered across five consumers
  • no implicit dependency between publish and final effect

Every message should read like a contract, not like a context dump.

What should not go through RabbitMQ

The easiest way to misuse a broker is to send it flows that gain nothing from becoming asynchronous.

I would not put RabbitMQ at the centre of:

  • a simple read path
  • immediate security validation
  • a decision that must answer the user right away
  • a flow with one producer, one consumer and no real decoupling need

If the message removes neither coupling, nor latency, nor fragility, it is probably just adding complexity.

What I learned while implementing it

The important takeaway is not “we fixed a bug”. It is closer to this:

  • do not event-drive the whole system
  • pick a few flows where async processing creates real value
  • treat message contracts with the same rigor as HTTP or SQL contracts
  • keep explicit application state around the broker

The most underestimated point, in my view, is this one: the broker does not make a system robust by magic. If the producer and consumer do not share a clean contract, if business state is not visible, or if retry behaviour is undefined, RabbitMQ only moves the complexity around.

What I want to show other developers

What I want to show here is how to use RabbitMQ with restraint inside a real platform:

  • where it creates actual value
  • how it shapes boundaries between services
  • and how to avoid turning it into a “magic” abstraction that adds more complexity than it removes

The right outcome is not “we added a broker”. The right outcome is:

  • services that are easier to read
  • workflows that are better split
  • a product that is easier to recover when one part slows down or fails

RabbitMQ is worth it when it makes the architecture clearer. Not when it gives a distributed veneer to problems that never needed one. *** Add File: /Users/mkifia/Projects/keyson/web/docs/linkedin-rabbitmq-fr.md

Post LinkedIn — RabbitMQ

On parle souvent de RabbitMQ comme si un broker etait automatiquement un signe d’architecture “serieuse”.

En pratique, le sujet n’est pas “mettre RabbitMQ”. Le sujet est de savoir ou il apporte une vraie valeur.

Dans une plateforme SaaS, je le trouve utile surtout pour :

  • le provisioning
  • les notifications et effets de bord
  • certains traitements transverses qui n’ont pas besoin de rester dans la requete HTTP

En revanche, je ne le mettrais pas au coeur :

  • d’une lecture simple
  • d’une validation de securite immediate
  • d’un flux qui doit repondre tout de suite a l’utilisateur

Le point cle : un broker ne rend pas un systeme robuste par magie.

La robustesse vient de :

  • messages petits et explicites
  • contrats lisibles entre services
  • etats metier visibles
  • retries et rattrapage penses des le depart

J’ai publie un retour d’experience sur le sujet : ou mettre RabbitMQ dans une plateforme SaaS, et surtout ou ne pas le mettre.

Keep reading

A few related articles to extend the topic and keep the internal linking strong.

2 min

Building a full stack SaaS as lead tech

A look at the technical choices behind Cercly — from the dev environment to production, across microservices, mobile, and AI-assisted delivery.

  • Architecture
  • Engineering
  • DevOps
  • SaaS
Read the note