Sirocco
← Études de cas

SaaS B2B

Plateforme SaaS multi-tenant avec isolation PostgreSQL

Un éditeur SaaS finance/RH historique migrait 80 clients depuis des bases dédiées vers une vraie architecture multi-tenant — sans casser la promesse contractuelle d'isolation.

220
Clients
18k
MAU
99,93%
Uptime
×5,4
MRR
3 sem → 2h
Onboarding

01 — Le défi

Le défi

Le modèle « une base par client » tenait depuis huit ans, mais chaque mise à jour était multipliée par 80 et l'onboarding d'une nouvelle entreprise prenait trois semaines. Le coût d'infrastructure progressait linéairement avec le nombre de clients. Il fallait passer à du multi-tenant strict tout en garantissant — par des tests automatisés et un pentest externe — qu'aucune fuite cross-tenant n'était possible, y compris via les vues, les fonctions SECURITY DEFINER et les triggers.

02 — L'approche

L'approche

Architecture shared-database avec PostgreSQL Row Level Security. Chaque table porte une colonne `tenant_id` indexée, et une policy `USING (tenant_id = current_setting('app.tenant')::uuid)` s'applique en lecture comme en écriture. Le GUC est positionné par middleware depuis le JWT NextAuth. La discipline est imposée par un linter custom et la CI bloque les migrations qui dérogent. NextAuth SAML + SCIM v2 fait passer l'onboarding de 3 semaines à 2 heures. Argo Rollouts pilote des déploiements blue/green avec rollback SLI automatique.

Architecture multi-tenant

Modélisation shared-database avec PostgreSQL RLS, contrat d'isolation, linter custom et CI bloquant les migrations non conformes.

Migration des 80 clients

Dual-write 7 jours par client, bascule pilotée par feature flag, validation par scénarios synthétiques avant cutover.

Industrialisation Enterprise

SAML autodiscovery + SCIM v2, Argo Rollouts avec analyse SLI automatique, observabilité OpenTelemetry par tenant.

03 — Résultats

Résultats

18 mois après le lancement, 220 entreprises servies (80 migrées sans downtime + 140 nouvelles), 18 000 utilisateurs actifs mensuels et 99,93% d'uptime. Le MRR a été multiplié par 5,4, principalement par l'ouverture aux tiers Enterprise (SSO, SCIM, audit logs, SLA contractuels). L'onboarding est passé de 3 semaines à 2 heures.

Détails techniques

Le SaaS B2B avec lequel nous avons travaillé éditait depuis huit ans une suite finance / RH déployée chez 80 clients historiques sous forme de bases de données dédiées (une instance PostgreSQL par client, hébergée chez le client ou sur leur VPC). Le modèle fonctionnait mais devenait insoutenable : chaque mise à jour était une opération manuelle multipliée par 80, le coût d'infrastructure progressait linéairement avec le nombre de clients, et l'onboarding d'une nouvelle entreprise prenait trois semaines de travail commercial et technique. L'objectif de la refonte était double : passer à une vraie architecture multi-tenant pour absorber 10× le nombre de clients sans 10× le coût, et le faire sans casser la promesse contractuelle d'isolation des données. Nous avons retenu une architecture shared-database avec PostgreSQL Row Level Security. Chaque table porte une colonne `tenant_id`, indexée systématiquement, et chaque table active une policy RLS du type `USING (tenant_id = current_setting('app.tenant')::uuid)`. Le GUC `app.tenant` est positionné en début de transaction par un middleware qui dérive l'UUID du tenant depuis le JWT NextAuth. Cette discipline d'application a été inscrite dans un lint custom et dans la revue automatique des migrations : impossible d'ajouter une nouvelle table sans `tenant_id` et sans policy associée, le pipeline CI bloque le merge. L'isolation a été stressée avec un harnais de tests dédiés. Pour chaque table, un test passe un GUC tenant A puis tente de lire / écrire des lignes appartenant à tenant B, directement via SQL brut et via l'ORM (Prisma) à travers tous les chemins exposés par l'API. Nous testons aussi les fuites via les vues, les fonctions SECURITY DEFINER et les triggers — c'est par là que les régressions arrivent en pratique, jamais par les requêtes naïves. Un audit externe a tenté trois mois de pentest applicatif sans réussir à exfiltrer de données cross-tenant. L'authentification s'appuie sur NextAuth.js avec un provider SAML connecté à Auth0, plus TOTP obligatoire pour tous les administrateurs de tenant. Le SAML autodiscovery permet à un nouveau client de saisir son IdP (Azure AD, Okta, Google Workspace) et de finaliser la configuration en autonomie en quelques minutes. Le provisioning SCIM v2 synchronise automatiquement les utilisateurs et groupes depuis l'IdP — c'est ce qui rend possible un onboarding de 2 heures plutôt que 3 semaines, parce que les listes nominatives ne transitent plus par des fichiers Excel échangés par mail. La facturation utilise Stripe avec un metering custom : nous publions des UsageRecord vers Stripe en quasi-temps réel (file Kafka -> consumer dédié, idempotent par event_id), et les abonnements suivent le modèle tier + overage. Le piège classique en multi-tenant — facturer un client deux fois si le worker rejoue — est neutralisé par un index unique sur `(stripe_subscription_item, period, event_id)` côté Postgres et par le `idempotency_key` côté Stripe. La réconciliation quotidienne croise notre vérité interne et les factures Stripe ; les écarts sont systématiquement à zéro ou un, jamais structurels. La migration depuis l'ancienne base par client a été le passage le plus risqué. Nous avons écrit un outil qui dump la base source, applique un mapping de schéma vers le modèle multi-tenant, et replay les writes en double pendant une fenêtre de bascule de 7 jours. Le trafic est routé via un feature flag par client : chaque client est basculé individuellement avec un health check synthétique qui vérifie qu'une dizaine de scénarios métier critiques produisent des résultats identiques sur l'ancien et le nouveau backend. Les bascules réussies étaient instantanées ; les deux qui ont échoué ont été rollback en moins de 90 secondes via le même flag. L'observabilité repose sur OpenTelemetry (traces + métriques) vers Grafana Tempo et Prometheus. Chaque span est tagué avec le `tenant_id`, ce qui permet de produire des dashboards et alertes par client (utile pour le support N2 et pour les Enterprise SLA). Les déploiements passent par Argo Rollouts sur EKS en blue/green avec analyse SLI automatique : si l'error rate du nouveau ReplicaSet dépasse 0,3% pendant 3 minutes, ou si la latence p95 dérive de 25%, le rollback est immédiat sans intervention humaine. Après 18 mois d'exploitation, la plateforme accueille 220 entreprises (les 80 clients historiques migrés sans downtime + 140 nouveaux), 18 000 utilisateurs actifs mensuels, et maintient 99,93% d'uptime. Le MRR a été multiplié par 5,4, principalement par la capacité à servir des tiers Enterprise jusque-là inaccessibles avec l'ancienne architecture (SSO, SCIM, audit logs, SLA contractuels). L'onboarding moyen est passé de 3 semaines à 2 heures, et le coût d'infrastructure marginal d'un nouveau client est devenu négligeable face au coût de gestion commerciale et de support.