Quand on construit un SaaS, l’une des premières décisions structurantes est celle de l’isolation des données entre clients. C’est ce qu’on appelle l’architecture multi-tenant : plusieurs clients (les “tenants”) partagent la même application, mais leurs données ne doivent jamais se mélanger. En apparence, le problème est simple. En pratique, la bonne réponse dépend d’une combinaison de facteurs techniques, économiques et organisationnels qui varient d’un projet à l’autre.
J’ai travaillé sur des SaaS utilisant chacune des trois grandes approches disponibles — parfois par choix, parfois par héritage. Cet article n’est pas un tour d’horizon théorique : c’est la façon dont je raisonne concrètement quand je dois trancher, avec les expériences qui ont forgé cette réflexion.
Ce qui rend ce choix particulièrement délicat, c’est qu’il est difficile à défaire une fois en production. Migrer d’une approche à une autre est techniquement faisable, mais c’est un chantier coûteux qui touche à la structure même de la base de données, à la logique applicative, et souvent aux contrats d’accès des clients existants. Il vaut mieux y consacrer du temps en amont, même si certains arbitrages ne peuvent se faire qu’une fois confronté à la réalité du terrain.
Il existe principalement trois approches, chacune avec ses compromis.
La base partagée avec un tenant_id — tous les clients cohabitent dans
les mêmes tables, distingués par une colonne tenant_id présente sur chaque
enregistrement. C’est l’approche la plus simple à mettre en place
initialement, et la plus répandue dans les SaaS à fort volume de clients.
Elle repose entièrement sur la couche applicative pour garantir l’isolation.
Le schéma dédié par tenant (PostgreSQL uniquement) — une seule instance
de base de données, mais chaque client dispose de son propre schéma logique
(tenant_a.commandes, tenant_b.commandes). C’est une fonctionnalité
native de PostgreSQL qui offre une isolation structurelle solide tout en
mutualisant l’infrastructure. Les migrations restent centralisées, mais
l’isolation est garantie au niveau de la base, pas seulement au niveau du
code.
La base de données dédiée par tenant — chaque client a sa propre instance. L’isolation est totale et physique : même en cas de bug applicatif grave, un tenant ne peut pas accéder aux données d’un autre. C’est l’approche la plus robuste sur le plan de la sécurité, mais aussi la plus lourde à opérer à mesure que le nombre de clients augmente.
tenant_id : simple à démarrer, exigeant à maintenirMa première expérience du multi-tenant, c’est chez Evenmedia. Je suis
arrivé sur un projet déjà en production, conçu dès le départ avec un
tenant_id. Des tables avec des millions d’enregistrements, plusieurs
clients actifs, et aucun problème de performance — à condition que la base
ait été pensée sérieusement dès le début : bons index, requêtes optimisées,
modélisation rigoureuse. Cette expérience m’a convaincu que l’approche
tient en charge, pour peu qu’on ne néglige pas les fondamentaux SQL.
J’ai ensuite appliqué cette stratégie sur Coordesk, un SaaS que j’ai
développé de zéro. Le client souhaitait MySQL, ce qui excluait d’emblée la
piste des schémas PostgreSQL. J’ai donc fait le choix du tenant_id, mais
en investissant sérieusement dans la sécurité applicative : scope global
appliqué automatiquement sur tous les modèles, middleware par sous-domaine
pour résoudre le tenant courant à l’entrée de chaque requête, et une règle
stricte de ne jamais contourner l’ORM. L’idée centrale est que l’isolation
doit être le comportement par défaut du système, pas une responsabilité
laissée à chaque développeur au moment d’écrire une requête.
Sur un SaaS de gestion de livraisons pour une société de transport, j’ai découvert l’approche “une base par tenant” — non pas par choix initial, mais parce que c’était l’existant sur lequel j’intervenais. Au quotidien, l’architecture fonctionnait bien. Chaque client était proprement isolé, sans risque de fuite entre tenants, et les performances de chaque base étaient prévisibles.
Le problème s’est posé lors de modifications de schéma. Répercuter une évolution structurelle sur N bases distinctes demande une orchestration rigoureuse : scripts de migration à appliquer dans le bon ordre sur chaque instance, vérifications post-migration, gestion des erreurs partielles. Plus il y a de tenants, plus l’opération est sensible. Ce qui prend quelques minutes sur une base centralisée devient une procédure à part entière. C’est là que j’ai vraiment mesuré le coût opérationnel de cette approche : pas à l’usage quotidien, mais au moment où le schéma évolue.
Sur mes projets personnels, j’utilise systématiquement PostgreSQL avec des
schémas par tenant. C’est l’approche qui m’offre le meilleur rapport
isolation/simplicité opérationnelle : les migrations sont centralisées comme
dans le tenant_id, mais l’isolation est structurelle comme dans la base
dédiée. PostgreSQL propose aussi des fonctionnalités avancées qui s’y
intègrent naturellement, dont je parlerai plus loin.
Quand je dois choisir, je me pose une série de questions dans un ordre assez constant. Rarement une seule d’entre elles suffit à trancher — c’est leur combinaison qui oriente.
Quelle est la fréquence d’acquisition de nouveaux clients ?
C’est souvent ma première question. Si l’on sait qu’on ne signera qu’un
client par an, créer manuellement une base dédiée représente une demi-journée
de travail acceptable. Si l’objectif est d’onboarder un client par semaine,
voire par jour, cette opération doit être entièrement automatisée — ou
l’approche doit changer. La base partagée (avec tenant_id ou schémas) s’y
prête naturellement ; la base dédiée exige une ingénierie d’onboarding
spécifique pour ne pas devenir un goulot d’étranglement.
Quel est le coût infra associé ?
Une base par tenant, c’est une infrastructure par tenant. Ce coût croît linéairement avec le nombre de clients, et il ne se limite pas aux ressources : c’est aussi du monitoring supplémentaire, des sauvegardes multipliées, des connexions à gérer. Pour un SaaS mass-market avec des centaines ou des milliers de clients, ça devient rapidement significatif. Pour un logiciel vendu à quelques grands comptes à prix élevé, c’est plus absorbable et souvent justifié par les exigences contractuelles de ces clients.
Quelle est la sensibilité des données ?
Des données médicales, financières ou juridiques peuvent imposer des contraintes réglementaires qui rendent l’isolation physique non négociable. Avant de choisir une approche sur ces projets, je vérifie si des exigences de conformité (RGPD, normes sectorielles, exigences contractuelles) dictent la décision indépendamment de ce que je préférerais techniquement. Il vaut mieux poser la question tôt que de devoir migrer sous pression.
Quelle est la maturité opérationnelle de l’équipe ?
Gérer plusieurs bases ou plusieurs schémas en production demande de la
rigueur : migrations orchestrées, monitoring par tenant, sauvegardes
granulaires, restaurations ciblées. Si l’équipe est petite ou peu
expérimentée sur ces sujets, la base partagée avec tenant_id reste
l’option la plus simple à opérer — à condition d’avoir investi dans la
couche de sécurité applicative.
Quel ORM ou framework est utilisé ?
Certains facilitent nativement la gestion de schémas multiples, d’autres beaucoup moins. Ce n’est pas un critère bloquant, mais c’est un facteur d’effort réel : avant de décider, je vérifie que l’approche choisie est bien supportée par la stack du projet, ou j’anticipe honnêtement l’effort d’implémentation. Choisir une architecture en désaccord avec ses outils, c’est s’assurer des frictions permanentes.
tenant_idLe tenant_id est l’approche la plus simple à mettre en place, mais c’est
aussi celle qui demande le plus de discipline sur la durée. Un filtre oublié
dans une requête, une jointure mal écrite, un accès direct à la base sans
passer par l’ORM : autant de vecteurs par lesquels les données d’un tenant
peuvent être exposées à un autre. Ce n’est pas un risque théorique — c’est
une surface d’attaque réelle qui grandit avec la taille de la codebase et
le nombre de développeurs.
La réponse à ce problème ne peut pas reposer sur la vigilance individuelle. Les mécanismes de protection doivent être systématiques et automatiques : un scope global qui s’applique à tous les modèles sans exception, un middleware qui résout le tenant courant dès l’entrée de chaque requête, et une discipline stricte autour de l’ORM. L’objectif est que l’isolation soit le chemin de moindre résistance, pas une case à cocher.
PostgreSQL pousse ce raisonnement encore plus loin avec les Row Level
Security (RLS) : des politiques de sécurité définies directement au
niveau de la base de données. Même si une requête applicative oublie un
filtre, la base refuse d’exposer les lignes appartenant à un autre tenant.
C’est une défense en profondeur particulièrement intéressante en
complément des schémas PostgreSQL, ou comme filet de sécurité sur une
architecture tenant_id.
Si je démarre un SaaS aujourd’hui avec le choix de la stack, je pars sur PostgreSQL avec des schémas par tenant. C’est selon moi le meilleur compromis entre isolation, simplicité opérationnelle et coût. Les migrations restent centralisées, l’isolation est structurelle plutôt qu’uniquement applicative, et l’écosystème PostgreSQL offre des outils puissants qui s’inscrivent naturellement dans cette approche.
Le tenant_id reste mon choix quand PostgreSQL n’est pas disponible —
ou quand le volume de tenants est tel que la gestion de schémas devient
une charge à part entière. Mais dans ce cas, j’investis systématiquement
dans la couche de sécurité applicative, parce que c’est ce qui fait tenir
l’édifice sur le long terme.
La base dédiée par tenant, je la réserve aux contextes où les contraintes réglementaires ou contractuelles l’imposent, ou quand le profil client (peu nombreux, à forte valeur contractuelle) le justifie économiquement. Ce n’est pas une mauvaise approche — c’est une approche coûteuse, qui mérite d’être choisie pour les bonnes raisons.
Un dernier point que je garde toujours en tête : si un tenant grossit au point de poser des problèmes de performance ou d’isolation, la bonne réponse n’est pas seulement d’isoler sa base. C’est d’isoler toute son infrastructure — applicatif, réseau, base de données. La base n’est qu’un élément parmi d’autres dans ce raisonnement, et le traiter isolément donne une fausse impression de sécurité.