RabbitMQ dans une plateforme SaaS : où le mettre, et pourquoi
Quand utiliser RabbitMQ dans une plateforme SaaS, quels flux lui confier, et comment s'en servir pour découpler le provisioning, les notifications et certains traitements métier sans event-driver tout le système.
- Integrations
- Engineering
- Architecture
RabbitMQ est facile à mal positionner. Quand une plateforme commence à avoir plusieurs frontaux, plusieurs APIs, des traitements longs et des intégrations externes, le broker devient vite une réponse tentante à tout. C’est souvent une erreur.
Dans notre cas, RabbitMQ n’est ni le centre de l’architecture, ni un détail d’infra posé sur le côté. C’est un outil de découplage pour quelques flux précis, là où le synchrone commence à coûter plus cher que le message.
Le problème à résoudre
Une plateforme SaaS qui combine plusieurs frontaux, plusieurs APIs, des traitements métier et des intégrations externes finit par rencontrer les mêmes besoins :
- lancer des traitements longs sans bloquer une requête utilisateur
- isoler les effets de bord
- absorber des indisponibilités partielles
- faire circuler des signaux entre services sans les coupler trop fort
La vraie question n’est donc pas “faut-il mettre RabbitMQ ?”. La vraie question est : quels flux méritent vraiment un broker.
La solution retenue
La solution retenue est de limiter RabbitMQ à des flux bien identifiés :
- provisioning
- notifications ou intégrations
- traitements qui traversent plusieurs couches techniques
- opérations qui doivent survivre à une indisponibilité temporaire d’un service aval
Tout le reste reste synchrone si le gain du message n’est pas net. C’est le point le plus important : RabbitMQ est utile quand il simplifie une frontière, pas quand il ajoute une couche abstraite de plus.
Le rôle de RabbitMQ dans la plateforme
RabbitMQ joue ici trois rôles concrets :
- Découpler le tempo entre un producteur et un consommateur
- Rendre un flux rejouable ou rattrapable
- Permettre une séparation propre entre services qui n’ont pas la même responsabilité
Dans une plateforme comme la nôtre, ça veut dire en pratique :
- un service peut accepter une demande et sortir vite de la requête HTTP
- un autre service peut traiter la suite avec son propre cycle de vie
- le système garde une trace explicite de l’état avant, pendant et après traitement
Le broker ne remplace pas l’état applicatif. Il transporte un signal. La robustesse vient du couple message + état métier explicite.
Pourquoi cette solution tient bien dans ce type de plateforme
Cette solution sert directement le projet :
- certaines opérations quittent le temps de la requête HTTP
- les services gardent des responsabilités plus claires
- l’architecture peut grandir sans tout transformer en appels synchrones fragiles
- les incidents sont plus rattrapables quand l’état applicatif est bien posé
L’article doit montrer RabbitMQ comme un composant d’architecture, pas comme une simple brique d’infra.
Exemple 1 : le provisioning
Le premier cas où RabbitMQ a une vraie place, c’est le provisioning.
Quand une plateforme crée un nouvel espace client, la demande initiale peut partir d’une API de contrôle ou d’un cockpit opérateur. Mais la suite ne se limite pas à “créer une ligne en base”. Il faut souvent :
- créer un runtime local
- préparer une configuration par défaut
- ouvrir les bons perimetres de donnees
- renvoyer un statut exploitable à l’opérateur
Ce genre de flux ne gagne rien à rester entièrement synchrone.
Dans notre implémentation, le service de contrôle publie un message de provisioning avec un contrat explicite :
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"
};
Ce qui compte ici, ce n’est pas RabbitMQ seul. C’est le fait que le message soit petit, clair, et accompagne un état métier visible comme payment_confirmed, provisioning, active ou failed.
Exemple 2 : notifications et effets de bord
Deuxième bon usage : les notifications et, plus largement, les effets de bord.
Quand un événement important arrive dans le produit, le cœur métier ne devrait pas se mettre à envoyer directement des emails, des SMS, des pushs ou des appels externes dans la même requête. Sinon, le service principal devient trop conscient de tous les canaux secondaires.
Le bon découpage est plutôt :
- le metier valide un changement important
- il émet un signal
- un consumer ou un service dérivé décide quoi notifier, sur quels canaux, avec quelle priorité
Dans notre contexte, les bons candidats sont typiquement :
- publication d’un événement
- rappels
- confirmations d’action
- signaux internes vers des interfaces admin ou membre
La règle utile pour d’autres devs : si l’action principale doit réussir même si le canal de notification est temporairement indisponible, le broker commence à être un bon candidat.
Exemple 3 : paiements
Les paiements sont plus subtils.
Le checkout initial, lui, n’a pas vocation à passer par RabbitMQ. Quand un utilisateur veut payer, il faut une réponse immédiate : création de la commande, création du paiement, redirection vers le prestataire. Ce chemin-là reste synchrone.
En revanche, des choses autour du paiement peuvent sortir de la requête :
- notifications après confirmation
- mise à jour de vues dérivées
- traitements transverses non critiques pour la reponse immediate
- réconciliation ou reporting interne
La bonne leçon ici, c’est que RabbitMQ n’est pas “la solution paiement”. Le paiement a souvent un noyau synchrone, puis des satellites asynchrones autour.
Exemple 4 : invitations et codes
Les invitations et les codes d’accès donnent un autre bon critère.
La validation du code, le contrôle du TTL, les tentatives, le lockout IP : tout cela reste mieux en synchrone, dans le service qui possède le modèle de sécurité. On veut une décision immédiate et déterministe.
En revanche, l’envoi du code lui-même peut très bien devenir asynchrone si le produit grandit :
- génération du code en synchrone
- persistance du code et de ses bornes de validite
- émission d’un message d’envoi
- traitement email/SMS dans un worker ou un service dédié
Le pattern est toujours le même : la décision métier reste près du modèle. Le transport vers l’extérieur peut sortir dans RabbitMQ.
Comment on l’implémente
Le bon niveau de code à montrer n’est pas un diff de bug. Ce sont plutôt :
- un producteur côté NestJS
- un consumer ou handler cote Symfony Messenger
- la forme du message transporté
- l’état applicatif autour du message
Le lecteur doit sortir de l’article avec une vision claire de la place du broker dans le système.
Le point clé est de garder les messages sobres :
- pas de payload inutilement riche
- pas de logique métier dispersée dans cinq consumers
- pas de dépendance implicite entre publish et effet final
Chaque message doit etre lisible comme un contrat, pas comme un dump de contexte.
Ce qu’il ne faut pas envoyer dans RabbitMQ
Le meilleur moyen de mal utiliser un broker est de lui confier des flux qui ne gagnent rien à devenir asynchrones.
Je ne mettrais pas RabbitMQ au coeur :
- d’une lecture simple
- d’une validation immédiate de sécurité
- d’une décision qui doit répondre tout de suite à l’utilisateur
- d’un flux qui n’a qu’un producteur, un consommateur et aucun vrai besoin de decouplage
Si le message ne retire ni couplage, ni latence, ni fragilité, il ajoute juste de la complexité.
Ce que j’ai appris en l’implémentant
Le point important à transmettre n’est pas “on a résolu un bug”. C’est plutôt :
- ne pas event-driver tout le systeme
- choisir quelques flux où l’asynchrone crée une vraie valeur
- traiter le contrat de message avec la meme rigueur qu’un contrat HTTP ou SQL
- rendre les états applicatifs explicites autour du broker
Le point le plus sous-estimé, à mon avis, c’est celui-là : le broker ne rend pas un système plus robuste par magie. Si le producteur et le consommateur n’ont pas un contrat net, si l’état métier n’est pas visible, ou si le retry n’est pas pensé, RabbitMQ ne fait que déplacer la complexité.
Ce que je veux montrer à d’autres devs
Ce que je veux montrer ici, c’est comment utiliser RabbitMQ avec discernement dans une plateforme réelle :
- où il apporte une vraie valeur
- comment il structure les frontières entre services
- et comment éviter d’en faire une abstraction “magique” qui complique plus qu’elle n’aide
Le bon résultat n’est pas “on a mis un broker”. Le bon résultat, c’est :
- des services plus lisibles
- des flux mieux découpés
- un produit plus rattrapable quand une partie du systeme ralentit ou tombe
RabbitMQ vaut le coup quand il rend l’architecture plus claire. Pas quand il sert à donner un vernis distribué à des problèmes qui n’en avaient pas besoin.