La question revient sur quasiment chaque projet SaaS que je cadre : « Comment séparer les données de mes clients entre eux ? » Derrière cette question simple se cache l'un des choix d'architecture les plus structurants pour un éditeur SaaS - et l'un des plus difficiles à inverser une fois la production lancée. C'est ce qu'on appelle l'architecture multi-tenant. Voici les trois modèles classiques, leurs compromis réels, et comment je les implémente concrètement en Symfony et Doctrine sur les SaaS que je conçois et maintiens.
Qu'est-ce que le multi-tenant, et pourquoi c'est central pour un SaaS
Un SaaS multi-tenant est une application unique qui sert plusieurs clients (les tenants) depuis la même infra. C'est la nature même du modèle SaaS : mutualiser le code, l'infrastructure et la maintenance pour offrir un service à un coût marginal faible. À l'inverse, l'approche single-tenant consiste à instancier une copie complète de l'application pour chaque client. C'est ce que font historiquement les ERP on-premise.
Le multi-tenant pose deux questions opposées : comment isoler les données pour qu'aucun tenant ne voie celles d'un autre, et comment mutualiser les ressources pour que l'ajout d'un tenant coûte le moins possible. Tout le travail d'architecture, c'est d'arbitrer ces deux exigences selon le secteur, la sensibilité des données et les contraintes réglementaires.
La documentation de référence sur le sujet reste celle de Microsoft Azure, qui formalise les modèles de tenancy de manière claire et indépendante du fournisseur.
Les 3 modèles d'architecture multi-tenant
On distingue trois modèles principaux, par ordre croissant d'isolation et de coût.
| Modèle | Isolation | Coût marginal par tenant | Scalabilité | Complexité |
|---|---|---|---|---|
Base partagée + colonne tenant_id | Logique (filtres applicatifs) | Très faible | Excellente jusqu'à plusieurs milliers de tenants | Faible |
| Schéma par tenant (même serveur) | Forte (séparation de schéma SQL) | Modérée | Bonne jusqu'à quelques centaines de tenants | Moyenne |
| Base par tenant (instance dédiée) | Très forte (séparation physique) | Élevée | Limitée par le nombre d'instances administrables | Forte |
Modèle 1 : base partagée avec colonne tenant_id
Toutes les tables métier ont une colonne tenant_id. Chaque requête est filtrée applicativement. C'est le modèle le plus simple, le plus économique, et celui que je recommande par défaut pour la grande majorité des SaaS B2B. Le risque principal : le leak applicatif. Oublier le filtre dans une requête expose les données d'un autre tenant.
Modèle 2 : un schéma SQL par tenant
Chaque tenant a son propre schéma (ou base nommée) sur le même serveur. Les tables sont identiques mais physiquement séparées. L'isolation est garantie par le moteur SQL, plus par l'application. C'est le modèle de Salesforce historique. Il est intéressant pour les secteurs sensibles (santé, finance) où l'on veut un argument fort vis-à-vis du DPO du client.
Modèle 3 : une base de données par tenant
Chaque tenant dispose d'une instance de base dédiée, parfois sur un serveur dédié. C'est l'isolation maximale, qui permet aussi des sauvegardes par tenant, des montées de version par tenant, et une localisation géographique par tenant. Le coût opérationnel est élevé : chaque ajout de client demande un provisionnement.
Quel modèle multi-tenant pour quel besoin
Il n'y a pas de meilleur modèle dans l'absolu. Le bon choix dépend du secteur, de la volumétrie cible et de la sensibilité des données.
- TPE débutante, SaaS B2B horizontal (CRM, gestion, facturation) : modèle 1 (base partagée). C'est le seul qui permet de tenir un coût d'exploitation bas tant qu'on n'a pas atteint plusieurs centaines de clients.
- Secteur sensible (santé, finance, juridique) : modèle 2 (schéma par tenant). L'argument vis-à-vis du DPO du client est clair, sans payer le prix opérationnel du modèle 3. C'est le modèle que je conseille pour un SaaS médical ou un cabinet de conseil traitant des données réglementées.
- Très grands comptes, contraintes de localisation par pays, exigences ISO 27001 ou HDS : modèle 3 (base par tenant). On le réserve aux SaaS qui adressent moins de 100 clients, chacun à fort enjeu contractuel.
D'expérience, sur les SaaS que je conçois pour des TPE et des éditeurs B2B, le modèle 1 couvre 80 % des cas. Pour les 20 % restants, on bascule sur le modèle 2, rarement sur le modèle 3.
Implémentation pratique en Symfony et Doctrine
Modèle 1 : Doctrine filters pour le tenant_id
Doctrine fournit un mécanisme idéal pour le modèle de base partagée : les SQLFilter. On définit un filtre qui ajoute automatiquement la condition WHERE tenant_id = :current_tenant_id à toutes les requêtes des entités concernées. Le filtre s'active dès l'identification de l'utilisateur (via un event subscriber sur la requête HTTP). Du coup, même un développeur distrait qui écrirait findAll() sans filtre ne ramènera que les données du tenant courant. C'est la ceinture et les bretelles.
Modèle 2 : multi-connexion Doctrine et résolveur de schéma
En Symfony, on peut configurer plusieurs entity managers ou utiliser un mécanisme de bascule de schéma à la requête. Le bundle doctrine/doctrine-bundle supporte nativement plusieurs connexions. Pour un usage avancé, on combine avec un middleware Doctrine qui injecte dynamiquement le bon schéma selon le tenant identifié. La complexité monte d'un cran : tests, fixtures et migrations doivent être pensés par schéma.
Migrations par tenant : le piège opérationnel
Sur les modèles 2 et 3, chaque migration Doctrine doit être appliquée à chaque tenant. Si vous avez 200 tenants, une migration qui prend 30 secondes prend 100 minutes au total. Pour gérer cela proprement, j'écris une commande Symfony dédiée (app:tenants:migrate) qui parcourt les tenants, applique les migrations en mode transactionnel et journalise chaque exécution. Sur des SaaS sensibles, on lance les migrations par lots avec validation entre chaque lot.
Les pièges récurrents du multi-tenant
- Le leak entre tenants : un développeur ajoute une requête SQL native sans filtre, et un tenant voit les données d'un autre. La parade est double : filtres Doctrine systématiques, plus tests d'intégration qui vérifient explicitement l'isolation entre deux tenants fictifs.
- Les sauvegardes globales mais pas par tenant : en cas de demande RGPD d'un tenant (« supprimez toutes mes données »), il faut pouvoir extraire et purger un tenant sans toucher aux autres. Cela se prépare dès le modèle de données, pas au moment de la demande.
- La montée de version qui casse un tenant : sur le modèle 3, chaque tenant peut être sur une version différente. C'est un piège : on multiplie les configurations à maintenir. Ma règle est de garder un parc iso-version, avec une fenêtre de migration coordonnée.
- L'oubli du tenant_id sur une table technique : les tables d'audit, de log applicatif ou de fichiers uploadés doivent aussi porter le
tenant_id, sinon on crée des fuites latérales (un export d'audit qui mélange les tenants).
Pour une vue d'ensemble du SEO technique d'un SaaS Symfony multi-tenant, j'ai compilé les optimisations utiles dans mon article SEO technique Symfony : 15 optimisations (sitemap, canonical, hreflang par tenant le cas échéant).
Choisir le bon modèle multi-tenant pour votre SaaS
Le multi-tenant est l'un des choix d'archi les plus structurants d'un SaaS. Mal arbitré, il piège l'éditeur dans une dette technique chère à corriger 3 ans plus tard. Bien arbitré, il devient un avantage commercial : argument de sécurité pour le client final, levier de marge pour l'éditeur.
Si vous portez un projet SaaS et que vous hésitez entre les trois modèles, je propose un atelier de cadrage qui examine votre secteur, votre cible, votre trajectoire de croissance et vos contraintes réglementaires. Comme tous mes accompagnements, le périmètre est défini sur devis personnalisé après un échange initial gratuit de 30 minutes. Pour la méthodologie complète d'un projet SaaS sur-mesure, mon guide du développement SaaS détaille les six étapes du cadrage à la maintenance. Le formulaire de contact permet de prendre date.