JUL6ART
🇫🇷 FR
Retour au blog
12 April 2026

Pourquoi nous avons choisi le multi-tenant natif

Une organisation, un scope, zéro fuite. Retour sur la décision la plus structurante de la plateforme.

Architecture multi-tenant

Le multi-tenant partagé — toutes les organisations dans la même base, isolées par filtres applicatifs — est le compromis le plus dangereux qu'une plateforme SaaS puisse adopter. Une seule requête mal scope, une jointure oubliée, un voter manquant, et la fuite est silencieuse, durable, et indétectable sans audit complet.

C'est pourquoi Jul6art applique le multi-tenant à toutes les couches de l'application :

  • Doctrine — chaque entité métier porte un FK organization_id, indexé en composite avec deleted_at. Aucun findAll() n'est jamais valide ; les repositories exposent queryByOrganization(Organization) qui devient le point d'entrée canonique.
  • HTTP — l'en-tête X-ORGANIZATION est résolu en début de chaîne par un subscriber qui rejette toute incohérence avec le tenant de l'utilisateur authentifié.
  • API Platform — chaque StateProvider applique automatiquement le filtre tenant. Les processeurs vérifient le scope avant le persist sur les FK potentiellement cross-org (mass-assignment guard).
  • VotersdenyAccessUnlessGranted(Voter::VIEW, $entity) sur chaque show, edit, delete, avec un re-check du organization_id côté entité.

La défense en profondeur évite qu'une seule erreur ne devienne une faille. Une régression sur un voter sera rattrapée par le filtre Doctrine ; une régression sur le filtre Doctrine sera rattrapée par le voter.