Passer au contenu principal
Tous les articles
26 septembre 2025·3 min de lecture·Diogo Hudson

La multi-tenancy sans la douleur

Chaque ligne non-auth porte un tenant. Les services filtrent. Les views restent passives. Les recherches cross-tenant renvoient 404, jamais 403. C'est tout l'ensemble de règles.

La multi-tenancy sans la douleur

La multi-tenancy a la réputation d'être difficile. Elle ne l'est pas, à condition de s'engager sur quatre règles dès le début.

Cette réputation vient de systèmes qui ajoutent la multi-tenancy après coup — en ajoutant des colonnes tenant à des tables existantes, en faisant transiter le contexte tenant par des couches qui n'ont pas été conçues pour cela, et en découvrant les cas limites un client mécontent à la fois. L'approche de Quotery est différente : chaque business model a été conçu avec la multi-tenancy dès le premier jour. La colonne tenant n'est pas une réflexion après coup ; elle est aussi fondamentale que la clé primaire.

Règle un : chaque ligne porte un tenant Chaque business model non-auth possède tenant = ForeignKey('tenants.Tenant', ...). Aucune exception en dehors de la table auth. Si une ligne métier existe sans connaître son tenant, vous avez un bug.

Cette règle est appliquée par convention et par revue de code, pas par la base de données — mais l'application est stricte. Chaque model dans api/<app>/models/ qui hérite de BaseModel possède une FK tenant ou une raison explicite et documentée pour ne pas en avoir (models auth, configuration à l'échelle du système). Les nouveaux contributeurs apprennent cette règle lors de leur première revue de PR. C'est l'invariant structurel le plus important du codebase.

Règle deux : les services filtrent, les views non Chaque fonction de service accepte tenant= et applique un filtre. Les views sont un transport simple : elles résolvent request.user.tenant, le transmettent à l'appel de service et font confiance à la couche.

Cette séparation est ce qui maintient le codebase maintenable. Si les views filtraient, vous devriez auditer chaque view pour vérifier l'isolation des tenants. Si les models filtraient (via un manager personnalisé), vous perdriez la capacité d'écrire des requêtes cross-tenant à des fins administratives. En centralisant le filtrage des tenants dans la couche de service, il n'y a qu'un seul endroit où vérifier que chaque requête est scopée : la fonction de service propriétaire de la requête.

Règle trois : cross-tenant renvoie 404, pas 403 Si un utilisateur demande une ressource qui existe dans le tenant d'un autre, l'API renvoie 404, pas 403. Renvoyer 403 révèle que la ressource existe. Le 404 la traite comme inexistante, ce qui correspond à ce que l'utilisateur doit voir.

C'est un argument de sécurité par l'obscurité qui est en réalité correct dans ce contexte. Un 403 dit « vous n'avez pas la permission de voir ceci, mais cela existe ». Un attaquant déterminé peut utiliser les réponses 403 pour cartographier quels tenants possèdent quelles ressources. Un 404 dit « cela n'existe pas dans votre univers ». Il est impossible de le distinguer d'une ressource réellement inexistante. La couche de service y parvient en filtrant les requêtes par tenant et en levant DoesNotExist lorsque la requête filtrée renvoie un résultat vide — jamais en vérifiant la propriété après la récupération.

Le routage des tenants par URL est courant (Slack, par exemple) mais introduit une classe de bugs où un utilisateur met une URL en signet, la partage, et révèle accidentellement son contexte tenant — ou pire, où une URL préparée permet à un utilisateur de tenter d'accéder à un autre tenant en modifiant le slug. La résolution des tenants par session élimine entièrement cette surface d'attaque. L'utilisateur s'authentifie, la session porte le tenant, et chaque requête ultérieure est automatiquement scopée. L'utilisateur ne voit jamais ni ne pense à son identifiant de tenant.

À propos de l'auteur
Diogo Hudson

Diogo Hudson est le fondateur de Quotery et ingénieur logiciel avec plus d'une décennie d'expérience dans la construction de systèmes d'entreprise pour des sociétés de distribution, de logistique et de chaîne d'approvisionnement. Avant Quotery, il a conçu l'architecture de plateformes gérant des milliers de SKU, des tarifications par paliers complexes et un stock en temps réel réparti sur plusieurs entrepôts. Il dirige DHDTech, l'équipe produit derrière Quotery, et utilise la plateforme au quotidien.

Quotery sur LinkedIn

Comment la plateforme Quotery est construite.

Tous les articles
Textes courts sur les devis, le stock, l'IA et comment les petits distributeurs expédient de gros volumes sans se compliquer la vie.

© 2026 quotery.io. Tous droits réservés

DHDTech.ioDHDTech.io