Conception d'un SaaS multi-locataires : base de données par locataire ou schéma partagé
Le choix architectural le plus important dans le développement SaaS : comment isoler les données des locataires. Nous comparons trois approches à l'aide d'une analyse des coûts réels, de stratégies de migration et d'exemples de code.
La décision qui détermine tout
Lors du développement d'un produit SaaS, la décision architecturale la plus importante concerne l'isolation des locataires. Si vous vous trompez, vous passerez des mois à migrer vos données par la suite. Il existe trois approches, chacune présentant des avantages et des inconvénients évidents :
Approche 1 : base de données partagée, schéma partagé (isolation au niveau des lignes)
Les données de chaque client sont stockées dans les mêmes tables, distinguées par une colonne « tenant_id ». C'est l'approche la plus simple et la moins coûteuse.
# Django : filtrage automatique des locataires à l'aide d'un
middleware class TenantMiddleware :
def __init__(self, get_response) :
self.get_response = get_response
def __call__(self, request) :
# Définir le locataire à partir du JWT ou du sous-domaine
request.tenant = get_tenant_from_request(request)
return self.get_response(request)
class TenantManager(models.Manager) :
def get_queryset(self) :
from threading import local
_thread_locals = local()
tenant = getattr(_thread_locals, 'tenant', None)
qs = super().get_queryset()
if tenant :
qs = qs.filter(tenant_id=tenant.id)
return
qsclass Order(models.Model) :
tenant = models.ForeignKey('Tenant', on_delete=models.CASCADE)
# ... autres champs
objects = TenantManager() # Toutes les requêtes sont filtrées automatiquementAvantages : simple, économique (50 à 200 $ par mois pour une base de données), facile à déployer, pool de connexions partagé. Inconvénients : risque de fuites de données si l'on oublie un filtre, problème de « voisin bruyant » (les requêtes gourmandes d'un locataire affectent les autres), mise en conformité plus difficile (suppression des données dans le cadre du RGPD).
Approche n° 2 : base de données partagée, schémas distincts (schémas PostgreSQL)
Chaque locataire dispose de son propre schéma PostgreSQL au sein d'une même base de données. Les tables sont identiques, mais isolées par espace de noms.
# Utilisation de la bibliothèque django-tenants#
Chaque requête définit le schéma en fonction du
sous-domaine# settings.
pyDATABASE_ROUTERS = ['django_tenants.routers.TenantSyncRouter']
# Le middleware définit le schéma pour chaque
requête# acme.yourapp.com -> schéma « acme
»# globex.yourapp.com -> schéma «
globex »# Équivalent SQL :
# SET search_path TO 'acme'; -- toutes les requêtes s'adressent désormais aux tables
d'acme# SELECT * FROM orders; -- renvoie uniquement les commandes d'acmeAvantages : isolation efficace sans bases de données supplémentaires, suppression facile des données (DROP SCHEMA CASCADE), index distincts par locataire. Inconvénients : complexité de la migration (il faut migrer chaque schéma), gestion du pool de connexions plus difficile, limite d'environ 500 locataires avant une baisse des performances.
Approche n° 3 : une base de données par locataire
Chaque locataire dispose de sa propre base de données. Isolation maximale, complexité opérationnelle maximale.
Avantages : isolation totale, évolutivité indépendante, conformité aisée, possibilité de proposer une infrastructure dédiée aux entreprises. Inconvénients : coût élevé (50 à 200 $ par locataire et par mois), gestion des pools de connexions particulièrement complexe, migrations impliquant des centaines de bases de données, analyse inter-locataires nécessitant un entrepôt de données.
Matrice de décision : quelle approche choisir ?
| Facteur | Schéma partagé | Schémas distincts | Bases de données distinctes |
|---|---|---|---|
| Coût mensuel (100 locataires) | 100 à 200 $ | 200 à 500 dollars | 5 000 à 20 000 dollars |
| Limite du nombre de locataires | Illimité | environ 500 | environ 100 (gérable) |
| Isolation des données | Faible (au niveau des lignes) | Fort (au niveau du schéma) | Terminer |
| Conformité (RGPD/SOC 2) | Difficile | Modéré | Facile |
| Risque lié aux voisins bruyants | Élevé | Moyen | Aucun |
| Idéal pour | B2C, SaaS pour PME | SaaS pour les PME | SaaS d'entreprise |
Notre conseil : commencez avec la version partagée, puis passez à la version supérieure
Commencez par un schéma partagé et un niveau d'isolation au niveau des lignes. Cela vous permet d'être rapidement sur le marché et de couvrir 90 % des cas d'utilisation. Lorsque vous décrochez des clients professionnels qui ont besoin de garanties d'isolation, proposez-leur des schémas ou des bases de données distincts dans le cadre d'une offre haut de gamme.
La clé réside dans la conception de vos modèles avec un tenant_id dès le départ. Cela permet de passer ultérieurement à n'importe quelle approche sans avoir à réécrire votre application.
La meilleure architecture multi-locataires est celle qui correspond à votre situation actuelle. Une start-up comptant 10 locataires et utilisant une base de données par locataire paie 10 fois plus que ce qu'elle devrait.
— alokknight Ingénierie
