Le multi-tenancy (ou multi-tenant) est une architecture essentielle dans le développement d’applications SaaS modernes. Il permet à une seule instance de code de gérer plusieurs clients, appelés tenants, avec des données et des configurations isolées. Si vous êtes développeur Laravel et que vous envisagez de concevoir une solution SaaS modulaire, performante et évolutive, vous êtes au bon endroit.
Dans cet article, je vais partager avec vous mon expérience concrète de développeur Laravel freelance spécialisé en SaaS, en vulgarisant les concepts, tout en creusant les aspects techniques qui intéressent les profils plus avancés.
Comprendre le multi-tenant et ses avantages
Une application multi-tenant héberge plusieurs clients (organisations ou entreprises) dans une seule base logicielle. Chaque tenant possède ses propres utilisateurs, ressources et données, et peut disposer d’un espace dédié (sous-domaine, branding, configuration…).
Pourquoi l’adopter ?
- Économies d’échelle : vous réduisez vos coûts d’infrastructure.
- Maintenance centralisée : une seule base de code à maintenir, mettre à jour ou déployer.
- Onboarding simplifié : un nouveau client peut être activé via une simple création d’entrée en base.
- Expérience homogène : tous les clients profitent des mêmes fonctionnalités.
Prenons un exemple : imaginons une plateforme de réservation pour salles de sport. Chaque club est un tenant. Ils ont leurs propres administrateurs, coachs, membres et créneaux, mais tous utilisent la même application Laravel.
Pour une introduction académique : Azure Architecture Center.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Stratégies d’architecture multi-tenant dans Laravel
Laravel n’est pas multi-tenant out-of-the-box, mais sa flexibilité permet d’implémenter plusieurs modèles. Le choix dépend souvent de vos objectifs métier, de vos contraintes techniques, ou encore de vos exigences de performance et de sécurité.
Voici les trois grandes approches les plus courantes :
Base de données par tenant
Chaque tenant dispose d’une base de données totalement séparée. Cela garantit un cloisonnement fort des données et simplifie les audits ou suppressions globales d’un tenant. Elle est idéale pour les applications B2B à forte criticité ou les contextes réglementaires (RGPD, données de santé, etc.). Cependant, cela augmente la complexité de la gestion des migrations, des sauvegardes et des performances.
Pour connecter dynamiquement chaque base selon le tenant :
config(['database.connections.tenant.database' => $tenant->db_name]);
DB::purge('tenant');
DB::reconnect('tenant');Cette approche offre une isolation maximale mais nécessite une orchestration rigoureuse, surtout pour les migrations et sauvegardes multiples.
Schéma par tenant (PostgreSQL only)
Cette stratégie repose sur une seule base de données PostgreSQL, avec un schéma dédié par tenant. Cela permet d’avoir une bonne séparation des données tout en profitant d’une gestion centralisée. Laravel ne propose pas cette gestion nativement, mais certains packages ou adaptations spécifiques permettent de la mettre en place. C’est un bon compromis entre isolation et scalabilité, surtout pour les systèmes internes à forte densité de données.
Pour approfondir le sujet avec PostgreSQL, ce guide sur schemas PostgreSQL multi-tenant est très pertinent.
Base partagée avec tenant_id
Dans cette configuration, toutes les données des tenants résident dans les mêmes tables, distinguées par une colonne tenant_id. C’est la méthode la plus simple à implémenter et idéale pour un MVP ou un petit SaaS à faible risque. Cependant, elle implique une vigilance accrue dans les requêtes pour ne jamais exposer de données inter-tenant. Elle est souvent utilisée avec des scopes globaux ou des middlewares pour encapsuler ce filtrage automatiquement.
Voici un exemple d’implémentation avec un scope global :
class TenantScope implements Scope {
public function apply(Builder $builder, Model $model) {
$tenant = app(Tenant::class);
$builder->where('tenant_id', $tenant->id);
}
}C’est la méthode la plus simple à mettre en œuvre et idéale pour démarrer un MVP.
Les deux packages de référence
stancl/tenancy
Un framework complet de multi-tenancy pour Laravel, très populaire dans l’écosystème SaaS.
Il permet d’implémenter une isolation des données flexible :
- par base de données (chaque tenant a sa propre DB),
- par schéma PostgreSQL,
- ou même via du in-memory (utile pour des environnements de tests ou des systèmes légers).
Stancl se distingue par son système de « tenancy automatique ». Grâce à une configuration fine, il intercepte les requêtes entrantes, détecte automatiquement le tenant (via domaine, sous-domaine ou autre stratégie), et injecte le contexte dans les modèles, le filesystem, le cache, les queues, etc.
Voici un exemple simple :
// routes/tenant.php
Route::middleware(['tenant'])->group(function ()
Route::get('/dashboard', [DashboardController::class, 'index']);
});
Pour appliquer une migration à tous les tenants automatiquement.
Enfin, il est compatible avec Laravel Octane, Vapor et même des configurations multi-process comme Horizon, ce qui en fait une solution robuste pour les apps SaaS en production.
Consultez la documentation officielle Stancl Tenancy pour aller plus loin.
spatie/laravel-multitenancy
Une approche plus minimaliste mais extrêmement efficace. Idéal si vous souhaitez garder le contrôle et ajouter uniquement les briques nécessaires à votre cas d’usage.
Ce package repose sur trois principes :
- Détection du tenant via une logique personnalisable (sous-domaine, domaine principal, paramètre, etc.)
- Contexte injecté via middleware sur les requêtes
- Gestion explicite du cache, des queues et du filesystem en fonction du tenant actif
Un exemple de détection via sous-domaine :
class DomainTenantFinder extends Spatie\Multitenancy\TenantFinder\TenantFinder {
public function findForRequest(Request $request): ?Tenant {
$domain = $request->getHost();
return Tenant::where('domain', $domain)->first();
}
}
Vous pouvez aussi définir des actions spécifiques lors du changement de contexte (connexion base de données, cache, config, etc.).
Il est parfait pour ceux qui veulent garder une structure simple tout en isolant correctement leurs tenants. Il peut aussi être combiné avec Laravel Scout, Horizon, et même des configurations hybrides (base partagée avec modèles spécifiques, etc.).
Voir la documentation Spatie Laravel Multitenancy
Bien utiliser le multi-tenant : bonnes pratiques et pièges à éviter
Une fois votre stratégie choisie, encore faut-il bien l’exécuter. Voici quelques conseils issus de retours d’expérience sur des applications SaaS Laravel multi-tenant :
Toujours filtrer les données par tenant
Peu importe la stratégie (shared DB ou non), veillez à toujours filtrer toutes les requêtes entrantes et sortantes par le tenant_id actif. Cela évite toute fuite de données entre clients.
Post::where('tenant_id', $currentTenant->id)->get();
L’utilisation de scopes globaux, d’un service provider ou d’un middleware est fortement conseillée pour automatiser ce filtrage.
Scope global Eloquent
Ajoutez ce scope à vos modèles pour automatiquement filtrer les requêtes :
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
class TenantScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
if (app()->has('currentTenant')) {
$tenantId = app('currentTenant')->id;
$builder->where('tenant_id', $tenantId);
}
}
}
Et dans votre modèle :
class Post extends Model
{
protected static function booted()
{
static::addGlobalScope(new TenantScope);
}
}
Middleware de contexte tenant
Utile si vous injectez dynamiquement le tenant par domaine ou token :
class IdentifyTenant
{
public function handle($request, Closure $next)
{
$domain = $request->getHost();
$tenant = Tenant::where('domain', $domain)->firstOrFail();
app()->instance('currentTenant', $tenant);
return $next($request);
}
}
protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\IdentifyTenant::class,
],
];
Service Provider pour initialisation centralisée
Si vous voulez initialiser le contexte tenant dès le boot de l’app :
class TenantServiceProvider extends ServiceProvider
{
public function boot()
{
$domain = request()->getHost();
$tenant = Tenant::where('domain', $domain)->first();
if ($tenant) {
app()->instance('currentTenant', $tenant);
// Change database connection dynamically
config(['database.connections.tenant.database' => $tenant->db_name]);
DB::purge('tenant');
DB::reconnect('tenant');
}
}
}
Tester en environnement multi-tenant
Ne testez pas votre logique uniquement avec un tenant unique. Créez plusieurs comptes avec différentes configurations pour valider que tout s’adapte : migrations, seeders, accès, logique de business.
Organiser votre code
Regroupez le code métier sensible aux tenants dans des dossiers dédiés, ou bien utilisez des namespaces ou des services qui injectent clairement le contexte tenant. Cela rendra vos règles plus maintenables.
Éviter les données globales mal isolées
Faites attention aux entités globales (ex : utilisateurs d’administration ou configurations systèmes). Décidez lesquelles doivent être globales (shared) ou spécifiques à un tenant, et structurez votre base en conséquence.
Monitorer les performances tenant par tenant
Chaque tenant peut générer une charge différente. Il est utile de suivre les performances, logs ou erreurs par client pour mieux diagnostiquer les ralentissements ou abus.
Construire un SaaS Laravel Multi-Tenant Robuste et Scalable
Le multi-tenancy est bien plus qu’un simple modèle d’architecture : c’est un pilier fondamental de toute application SaaS ambitieuse. Que vous optiez pour une base de données partagée, une séparation par schéma ou une base dédiée par tenant, Laravel 12 vous donne les outils nécessaires pour le faire proprement, à condition d’avoir une bonne stratégie dès le départ.
Prenez le temps de choisir l’approche la plus adaptée à votre cas d’usage : MVP rapide ? Application B2B hautement sécurisée ? Forte scalabilité ? Les réponses peuvent varier selon votre marché, vos ressources, vos clients.
Mais surtout, documentez, testez, automatisez. Adoptez les bons réflexes dès aujourd’hui et vous éviterez les migrations douloureuses demain.
En tant que développeur Laravel freelance, je vous recommande de garder un œil sur les évolutions des packages comme stancl/tenancy et spatie/laravel-multitenancy, qui évoluent vite et s’adaptent aux nouveaux usages (Octane, Serverless, etc.). Et pour le reste , n’hésitez pas à me contacter.