Architecture Multi-Site pour DSOs, Chaînes de Cliniques Vétérinaires, Salles de Sport et Franchises
Votre DSO dentaire lance le lieu #50 et le temps de déploiement passe de quatre minutes à onze. Votre franchise de salle de sport ajoute le site #127 et la compilation génère une erreur mémoire. Votre chaîne vétérinaire met à jour une adresse et 200 pages se reconstruisent. Chaque opérateur multi-site — groupes dentaires, franchises de salles de sport, chaînes hôtelières, réseaux d'églises — heurte le même plafond architectural : le contrôle centralisé de la marque se rompt quand vous essayez de donner à chaque lieu sa propre page SEO, son propre contenu, ses propres balises meta. Une mise à jour CMS ne devrait pas redéployer 200 sites statiques. Un changement d'adresse ne devrait pas retrigger tout votre pipeline de build. Mais la plupart des configurations Next.js font exactement cela — parce qu'elles traitent chaque lieu comme un projet séparé au lieu de lignes dynamiques dans un seul schéma. Voici l'architecture qui le corrige.
J'ai construit ce pattern pour des groupes dentaires, des franchises de fitness, des réseaux vétérinaires et des chaînes de restaurants. À chaque fois, je commence par le même schéma de base de données, la même structure de routes Next.js et le même contrôle d'accès basé sur les rôles. Ce qui change, c'est les données de seed et les étiquettes des composants. « Services » devient « Classes » dans une salle de sport ou « Éléments de Menu » dans un restaurant. « Staff » devient « Dentistes » ou « Entraîneurs » ou « Vétérinaires ». La plomberie dessous ? Identique.
Cet article expose le pattern d'architecture multi-site universel une fois, puis montre comment il s'adapte à cinq industries complètement différentes. Si vous exploitez n'importe quel type d'activité multi-site — ou si vous êtes un développeur qui en construit une — ceci est le blueprint.
Table des matières
- Le Problème Fondamental Auquel Chaque Entreprise Multi-Site Fait Face
- Le Schéma de Base de Données Universel
- Architecture des Routes Next.js
- Sécurité au Niveau des Lignes et le Tableau de Bord Admin
- Variation Industrielle 1 : DSOs Dentaires
- Variation Industrielle 2 : Chaînes de Salles de Sport et de Fitness
- Variation Industrielle 3 : Groupes Hôteliers
- Variation Industrielle 4 : Chaînes de Cliniques Vétérinaires
- Variation Industrielle 5 : Chaînes de Restaurants
- Tableau de Comparaison de l'Architecture
- Déploiement et Performance à l'Échelle
- Ventilation des Coûts : Ce Que Cela Coûte Réellement en 2026
- FAQ

Le Problème Fondamental Auquel Chaque Entreprise Multi-Site Fait Face
Soyons directs sur ce qui se passe généralement. Une franchise ou une entreprise multi-site commence avec un seul site web. Puis ils ouvrent un deuxième lieu. Quelqu'un lance une deuxième installation WordPress. Au moment où il y a 15 sites, vous avez 15 installations WordPress séparées, 15 thèmes différents (certains sont à trois versions de retard), 15 ensembles différents de plugins, et zéro contrôle centralisé.
Le directeur marketing veut mettre à jour l'appel à l'action principal de la marque sur tous les sites. C'est 15 logins, 15 éditions, et une prière que personne n'ait cassé son modèle. L'équipe SEO veut voir quels sites publient du contenu blog et lesquels sont restés silencieux pendant six mois. Il n'y a pas de tableau de bord pour cela — juste une feuille de calcul que quelqu'un a oublié de mettre à jour en mars.
C'est le même problème que vous gériez 50 pratiques au sein d'une organisation de support dentaire (DSO) ou 200 sites pour un groupe de restaurants. Les symptômes sont identiques :
- Dérive de marque. Les sites vont hors-marque parce que personne n'impose la cohérence.
- Fragmentation SEO. Pas de pages SEO locales structurées, pas de cohérence dans le balisage schéma, pas de sitemap centralisé.
- Chaos administratif. Chaque site gère le sien (mal), ou le siège gère tout (lentement).
- Risque de déploiement. Mettre à jour le site d'un site ne devrait pas pouvoir faire tomber un autre.
La solution n'est pas un meilleur thème CMS. C'est une architecture entièrement différente.
Le Schéma de Base de Données Universel
Tout commence par une table locations. C'est l'ancre de tout le système. J'utilise Supabase comme couche de base de données et d'authentification parce qu'elle vous donne Postgres, Row-Level Security, les abonnements en temps réel, et un tier gratuit généreux — mais le schéma fonctionne avec n'importe quelle base de données relationnelle.
Voici le schéma fondamental :
-- La table d'ancrage. Chaque contenu spécifique à un lieu
-- la référence via location_id.
CREATE TABLE locations (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
address TEXT NOT NULL,
city TEXT NOT NULL,
state TEXT NOT NULL,
zip TEXT NOT NULL,
lat DECIMAL(10, 8),
lng DECIMAL(11, 8),
phone TEXT,
email TEXT,
hours JSONB DEFAULT '{}',
photos TEXT[] DEFAULT '{}',
description TEXT,
metadata JSONB DEFAULT '{}',
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
-- Les tables de contenu suivent le MÊME pattern :
-- location_id est NULLABLE.
-- NULL = partagé sur tous les sites
-- Une valeur = spécifique à ce site
CREATE TABLE services (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
location_id UUID REFERENCES locations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT NOT NULL,
description TEXT,
price_range TEXT,
duration TEXT,
category TEXT,
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT true,
metadata JSONB DEFAULT '{}'
);
CREATE TABLE staff (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
location_id UUID REFERENCES locations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT NOT NULL,
title TEXT,
photo TEXT,
bio TEXT,
credentials TEXT[],
specialties TEXT[],
sort_order INT DEFAULT 0,
is_active BOOLEAN DEFAULT true
);
CREATE TABLE blog_posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
location_id UUID REFERENCES locations(id) ON DELETE CASCADE,
title TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
content TEXT,
excerpt TEXT,
author_id UUID REFERENCES staff(id),
published_at TIMESTAMPTZ,
is_published BOOLEAN DEFAULT false,
tags TEXT[] DEFAULT '{}',
metadata JSONB DEFAULT '{}'
);
CREATE TABLE testimonials (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
location_id UUID REFERENCES locations(id) ON DELETE CASCADE,
author_name TEXT NOT NULL,
rating INT CHECK (rating >= 1 AND rating <= 5),
content TEXT,
is_approved BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE events (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
location_id UUID REFERENCES locations(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT,
event_date TIMESTAMPTZ,
end_date TIMESTAMPTZ,
is_active BOOLEAN DEFAULT true
);
Le pattern location_id nullable est le point clé. Quand un article de blog a location_id = NULL, c'est un article au niveau du réseau (« 5 Conseils pour des Dents Saines » partagés sur les 50 pratiques dentaires). Quand location_id a une valeur, c'est spécifique à ce lieu (« Le Dr Smith Rejoint Notre Cabinet d'Austin »). Même table, mêmes patterns de requête, mais le contenu peut être partagé ou localisé avec une seule colonne.
La colonne metadata JSONB est où vivent les champs spécifiques à l'industrie. Un lieu dentaire pourrait stocker {"insurance_accepted": ["Delta Dental", "Cigna"], "parking_info": "Parking gratuit derrière le bâtiment"}. Une salle de sport stocke {"equipment": ["squat racks", "rowing machines"], "peak_hours": "5-7 PM en semaine"}. Pas de migration de schéma nécessaire — juste des formes JSON différentes.
Architecture des Routes Next.js
Le Next.js App Router mappe proprement à ce modèle de données. Voici la structure de routes qui fonctionne pour chaque industrie :
app/
├── page.tsx # Page d'accueil
├── locations/
│ ├── page.tsx # Localisateur de sites (carte + recherche géo)
│ └── [slug]/
│ ├── page.tsx # Page de détail du site
│ ├── staff/page.tsx # Liste du personnel du site
│ └── services/page.tsx # Services du site
├── services/
│ └── [service]/page.tsx # Description de service partagé
├── blog/
│ ├── page.tsx # Tous les articles de blog
│ └── [post]/page.tsx # Article de blog individuel
├── about/page.tsx
└── contact/page.tsx
La page de détail du site (/locations/[slug]) est où la magie opère. Un seul appel generateStaticParams interroge tous les sites actifs et les pré-rend tous au moment de la build :
// app/locations/[slug]/page.tsx
import { createClient } from '@/lib/supabase/server'
export async function generateStaticParams() {
const supabase = createClient()
const { data: locations } = await supabase
.from('locations')
.select('slug')
.eq('is_active', true)
return locations?.map((loc) => ({ slug: loc.slug })) ?? []
}
export async function generateMetadata({ params }: { params: { slug: string } }) {
const supabase = createClient()
const { data: location } = await supabase
.from('locations')
.select('*')
.eq('slug', params.slug)
.single()
if (!location) return {}
return {
title: `${location.name} | ${location.city}, ${location.state}`,
description: location.description,
openGraph: {
title: `${location.name} - ${location.city}`,
images: location.photos?.[0] ? [location.photos[0]] : [],
},
}
}
export default async function LocationPage({ params }: { params: { slug: string } }) {
const supabase = createClient()
const [{ data: location }, { data: staff }, { data: services }, { data: testimonials }] =
await Promise.all([
supabase.from('locations').select('*').eq('slug', params.slug).single(),
supabase.from('staff').select('*').eq('location_id', params.slug), // simplifié
supabase.from('services').select('*').or(`location_id.is.null,location_id.eq.${locationId}`),
supabase.from('testimonials').select('*').eq('is_approved', true),
])
// Rendre la page du site avec toutes les données
// C'est la même structure de composant quelle que soit l'industrie
}
La requête services utilise ce filtre or — récupérer les services où location_id est null (services partagés) OU correspond au lieu actuel. Cela signifie qu'un DSO dentaire peut définir « Nettoyage des Dents » une fois pour tous les sites, puis ajouter « Invisalign » uniquement pour les sites qui l'offrent. Pas de duplication.
Pour la page du localisateur de sites, je stocke les coordonnées lat/lng et j'utilise l'extension PostGIS de Supabase pour les requêtes géo :
-- Trouver les sites à moins de 25 miles des coordonnées de l'utilisateur
SELECT *,
(point(lng, lat) <@> point($1, $2)) * 1.60934 AS distance_miles
FROM locations
WHERE is_active = true
ORDER BY point(lng, lat) <@> point($1, $2)
LIMIT 20;

Sécurité au Niveau des Lignes et le Tableau de Bord Admin
C'est là que l'architecture montre vraiment ses résultats. Les politiques Supabase RLS vous permettent de définir l'accès aux données au niveau de la base de données — pas dans le code de votre application.
-- Les gestionnaires de sites ne peuvent voir que les données de leur propre site
CREATE POLICY "Location managers see own data" ON services
FOR ALL
USING (
location_id IN (
SELECT location_id FROM user_locations
WHERE user_id = auth.uid()
)
OR
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid() AND role = 'network_admin'
)
);
Les administrateurs réseau voient tout. Les gestionnaires de sites ne voient que leur site. Cela s'applique à chaque table — services, staff, articles de blog, témoignages, événements. Un pattern de politique, appliqué de manière cohérente.
Le tableau de bord admin affiche les métriques au niveau du réseau :
- Fraîcheur du contenu : Quels sites n'ont pas mis à jour leur blog depuis 30+ jours ?
- Trafic par site : Données de Google Search Console agrégées par slug de site
- Pistes par site : Soumissions de formulaires et demandes de réservation par site
- Conformité de marque : Tous les sites utilisent-ils le logo approuvé, les couleurs et le texte CTA ?
Variation Industrielle 1 : DSOs Dentaires
Un site DSO doit ressembler à une marque dentaire unifiée tout en permettant à chaque cabinet de mettre en avant ses fournisseurs et spécialités uniques.
Services correspondent aux procédures dentaires : nettoyages, obturations, couronnes, implants, Invisalign, soins dentaires d'urgence. Certains sont universels (chaque site fait des nettoyages), d'autres sont spécifiques à un site (seuls trois sites offrent les soins sous sédation).
Staff sont les dentistes, les hygiénistes et les directeurs de bureaux. Chacun reçoit un profil avec les diplômes (DDS, DMD), les spécialités, la formation et une photo professionnelle. Les parents qui choisissent un dentiste pédiatrique veulent voir qui traitera leur enfant.
CTA est « Prendre un Rendez-vous ». Cela se connecte à Calendly, NexHealth ou un système de réservation personnalisé. Le widget de réservation pré-sélectionne le site en fonction duquel site l'utilisateur a visité.
Cibles SEO locales : « dentiste à [ville] », « [procédure] à [ville] », « dentiste d'urgence [ville] [état] ». Chaque page de site reçoit un balisage de données structurées pour les schémas Dentist et LocalBusiness.
Metadata JSONB stocke : les régimes d'assurance acceptés, les informations de stationnement, les caractéristiques d'accessibilité, les langues parlées, s'ils acceptent les nouveaux patients.
Variation Industrielle 2 : Chaînes de Salles de Sport et de Fitness
Les chaînes de salles de sport échangent « services » contre « classes » — mais le modèle de données est le même. Une classe de yoga au Site A et une classe HIIT au Site B sont juste des lignes dans la table services avec différentes valeurs location_id.
Services sont des types de classes avec des données d'horaire. Les métadonnées stockent l'horaire hebdomadaire en JSON, l'attribution de l'instructeur, les limites de capacité, et si les sans-réservation sont autorisés.
Staff sont les entraîneurs et instructeurs avec les certifications (NASM, ACE, CrossFit L2), les spécialités et la disponibilité pour les réservations d'entraînement personnel.
CTA est « Rejoignez-nous » — un paiement d'abonnement Stripe qui gère les niveaux d'adhésion et l'accès multi-site. Un membre qui s'inscrit au site du centre-ville devrait pouvoir se présenter au site de banlieue aussi.
Cibles SEO locales : « salle de sport près de moi », « classes de fitness [ville] », « classes [type] [ville] », « entraîneur personnel [ville] ».
Metadata JSONB stocke : liste d'équipement, horaire des classes, heures de pointe, commodités (sauna, piscine, garderie), disponibilité du parking gratuit.
Variation Industrielle 3 : Groupes Hôteliers
Les groupes hôteliers de niche et les chaînes hôtelières indépendantes bénéficient énormément de ce pattern — surtout parce qu'il permet les réservations directes qui contournent les frais des OTA (généralement 15-25% par réservation sur Booking.com ou Expedia).
Services deviennent des types de chambres : Chambre Standard, Suite King, Penthouse. Chacun reçoit des photos, une liste d'équipements, la superficie et le prix de base. Le pricing spécifique au site vit dans les métadonnées ou une table de tarifs séparée avec des plages de dates.
Staff est plus léger ici — peut-être un directeur général ou concierge vedette de la marque pour la narration de la marque.
CTA est « Réserver Directement » — le pattern FME (Find, Match, Engage) qui donne aux clients une raison de réserver sur le propre site de l'hôtel au lieu d'un OTA. Généralement une « garantie du meilleur tarif » ou une surclassement gratuit.
Cibles SEO locales : « hôtels à [ville] », « avis [nom hôtel] », « hôtel de niche [quartier] [ville] », « hôtels près de [point de repère] ».
Metadata JSONB stocke : commodités (piscine, spa, restaurant, gym, recharge VE), attractions à proximité, calendrier d'événements locaux, heures d'arrivée/départ, politique sur les animaux de compagnie.
Variation Industrielle 4 : Chaînes de Cliniques Vétérinaires
Les chaînes vétérinaires se développent rapidement en 2026 — la consolidation en médecine vétérinaire reflète ce qui s'est passé avec les DSOs dentaires il y a une décennie. La même architecture multi-site s'applique parfaitement.
Services sont les services de soins pour animaux : examens de bien-être, vaccinations, nettoyage dentaire, chirurgie, soins d'urgence, pension, toilettage. Certains sites offrent des soins aux animaux exotiques ; la plupart ne le font pas.
Staff sont les vétérinaires avec l'expertise des espèces (petits animaux, équidés, exotiques), les certifications spécialisées et la formation.
CTA est « Prendre un Rendez-vous » avec une particularité — le formulaire d'admission doit capturer les informations sur l'animal (espèce, race, âge, raison de la visite) pour acheminer correctement le rendez-vous.
Cibles SEO locales : « vétérinaire à [ville] », « vétérinaire d'urgence [ville] », « vétérinaire [espèce] [ville] », « nettoyage dentaire pour animaux [ville] ».
Metadata JSONB stocke : espèces acceptées, heures d'urgence (si différentes des heures normales), capacité de pension, s'ils ont un laboratoire et un imagerie sur site.
Variation Industrielle 5 : Chaînes de Restaurants
Services deviennent des sections de menu : entrées, plats principaux, desserts, boissons. L'élément critique ici est que le prix peut varier selon le site. Un burger coûte 14 $ à Austin et 19 $ à Manhattan. La colonne metadata gère cela avec des remplacements de tarification spécifiques au site.
Staff sont les chefs ou maîtres de barbecue vedettes — cela fonctionne mieux pour les marques où les personnes derrière la nourriture font partie de l'histoire.
CTA est « Commander en Ligne » — un lien conscient de la géolocalisation qui acheminent vers le système de commande en ligne correct (Toast, Square, ChowNow ou personnalisé) pour le site le plus proche de l'utilisateur.
Cibles SEO locales : « menu [nom restaurant] [ville] », « restaurants près de moi », « restaurant [type cuisine] [ville] », « heures [nom restaurant] ».
Metadata JSONB stocke : rayon de livraison, disponibilité des réservations (avec lien OpenTable ou Resy), détails de stationnement, capacité de salles pour événements privés, heures d'happy hour.
Tableau de Comparaison de l'Architecture
| Composant | DSO Dentaire | Chaîne de Salle de Sport | Groupe Hôtelier | Chaîne Vétérinaire | Restaurant |
|---|---|---|---|---|---|
| Étiquette « Services » | Procédures | Classes | Types de Chambres | Services pour Animaux | Articles de Menu |
| Étiquette « Staff » | Dentistes | Entraîneurs | Gestion | Vétérinaires | Chefs |
| CTA Principal | Prendre Rendez-vous | Rejoindre Adhésion | Réserver Chambre | Prendre Rendez-vous | Commander en Ligne |
| Intégration de Réservation | NexHealth, Calendly | Abonnements Stripe | Personnalisé / Cloudbeds | Personnalisé + admission animal | Toast, Square |
| Données Locales Clés | Assurance, stationnement | Horaire, équipement | Commodités, attractions | Espèces, heures d'urgence | Menu pricing, livraison |
| Mot-clé SEO Principal | « dentiste à [ville] » | « salle de sport près de moi » | « hôtels à [ville] » | « vétérinaire à [ville] » | « [marque] [ville] menu » |
| Balisage Schéma | Dentist, LocalBusiness | SportsActivityLocation | Hotel, LodgingBusiness | VeterinaryCare | Restaurant, Menu |
| Tables DB Modifiées | 0 | 0 | 0 | 0 | 0 |
Cette dernière ligne est le point clé. Zéro table de base de données ne change entre les industries. Vous utilisez les mêmes tables locations, services, staff, blog_posts, testimonials et events. Les étiquettes dans l'interface utilisateur changent. Les formes des métadonnées changent. L'architecture ne change pas.
Déploiement et Performance à l'Échelle
Nous déployons cela sur Vercel avec ISR (Incremental Static Regeneration). Chaque page de site est générée statiquement au moment de la build et se revalide toutes les 60 secondes. Pour une chaîne de 200 sites, cela représente 200 pages HTML statiques qui se chargent en moins d'une seconde sur n'importe quel appareil.
Les chiffres ont de l'importance. Voici ce que nous voyons généralement :
- Temps de build pour 200 sites : ~45 secondes sur Vercel Pro
- TTFB par page de site : < 50ms (servi depuis le CDN edge)
- Scores Lighthouse : 95+ dans tous les domaines
- Revalidation ISR : 60 secondes de stale-while-revalidate signifient que les mises à jour de contenu apparaissent en moins d'une minute sans une rebuild complète
Ajouter un nouveau site est une insertion de base de données plus un appel de revalidation à la demande optionnel. Aucun déploiement nouveau nécessaire. La fonction generateStaticParams récupère les nouveaux sites au prochain build ou cycle ISR.
// Route API pour déclencher la revalidation quand un site est ajouté/mis à jour
import { revalidatePath } from 'next/cache'
export async function POST(request: Request) {
const { slug } = await request.json()
revalidatePath('/locations')
revalidatePath(`/locations/${slug}`)
return Response.json({ revalidated: true })
}
Ventilation des Coûts : Ce Que Cela Coûte Réellement en 2026
Parlons des vrais chiffres. C'est une question courante que nous recevons lors des conversations de tarification.
| Composant | Coût Mensuel (50 sites) | Coût Mensuel (200 sites) |
|---|---|---|
| Supabase Pro | 25 $ | 25 $ (même tier gère les deux) |
| Vercel Pro | 20 $ | 20 $ |
| Bande Passante Vercel (dépassement) | ~0 $ | ~40 $ |
| Domaine + DNS (Cloudflare) | 0 $ | 0 $ |
| CDN Image (Cloudflare R2) | ~5 $ | ~15 $ |
| Surveillance (Sentry) | 26 $ | 26 $ |
| Total infrastructure | ~76 $/mo | ~126 $/mo |
Comparez cela à 50 sites WordPress séparés à ~30 $/mois chacun pour l'hébergement géré — c'est 1 500 $/mois avant même de penser à la maintenance, les licences de plugin, ou la personne qui doit les maintenir tous à jour.
L'investissement de développement est plus élevé au début — nous devis généralement les builds multi-site entre 30K-80K $ selon la complexité — mais le coût opérationnel continu est une fraction de l'alternative WordPress multisite. Et vous ne payez pas 500 $/mois par site à un vendeur de site web de franchise qui vous verrouille dans leur plateforme.
Pour les équipes intéressées par l'exploration des intégrations de CMS headless ou envisageant Astro au lieu de Next.js pour des builds statiques encore plus rapides, la même architecture de base de données s'applique. La structure frontale est interchangeable ; le modèle de données n'est pas.
FAQ
Cette architecture peut-elle gérer des sites dans différents fuseaux horaires ?
Absolument. La colonne hours JSONB stocke les heures d'exploitation de chaque site dans leur fuseau horaire local. Nous incluons un champ timezone (par exemple, « America/Chicago ») dans les métadonnées du site et l'utilisons pour tous les affichages sensibles au temps comme les badges « Ouvert Maintenant ». Tous les timestamps dans la base de données sont stockés en UTC et convertis sur le frontend.
Comment gérez-vous les sites qui offrent différents services ?
C'est le pattern location_id nullable en action. Les services avec location_id = NULL sont partagés sur tous les sites — ils apparaissent sur la page de chaque site. Les services avec un location_id spécifique n'apparaissent que pour ce site. Vous pouvez également utiliser une table de jonction (location_services) pour les relations plusieurs-à-plusieurs si les services partagés ont besoin de remplacements par site comme les tarifs ou la disponibilité personnalisés.
Que se passe-t-il quand un nouveau site ouvre ?
Un administrateur réseau ajoute le site via le tableau de bord. Cela crée une ligne dans la table locations, déclenche un webhook qui déclenche la revalidation ISR, et la page du site est en direct en moins de 60 secondes. Aucun développeur nécessaire, aucun déploiement, aucun changement DNS. Le site hérite immédiatement de tous les services et contenus partagés.
C'est mieux que WordPress Multisite pour les franchises ?
Pour la plupart des entreprises multi-site, oui. WordPress Multisite a été la réponse incontournable pendant une décennie, mais elle a de vrais problèmes : une vulnérabilité de plugin unique peut faire tomber le réseau entier, les performances se dégradent quand vous ajoutez des sites, et vous avez besoin d'un sysadmin dédie pour le garder en bonne santé. Cette architecture headless vous donne les performances des sites statiques, la sécurité au niveau de la base de données, et zéro risque d'exécution partagé entre sites.
Comment les gestionnaires de sites éditent-ils leur propre contenu sans casser d'autres sites ?
Row-Level Security au niveau de la base de données garantit qu'un gestionnaire de site à Austin ne peut littéralement pas voir ou modifier les données appartenant au site de Denver. Ce n'est pas appliqué par le code d'application qui pourrait avoir des bugs — c'est appliqué par Postgres lui-même. Même si l'interface utilisateur admin avait un bug qui tentait de requêter les données d'un autre site, la base de données retournerait des résultats vides.
Qu'en est-il du SEO — chaque site reçoit-il son propre sitemap ?
Chaque page de site reçoit sa propre entrée dans un seul sitemap dynamique généré au moment de la build. Nous générons également des données structurées par site (JSON-LD) avec un schéma LocalBusiness, les géo-coordonnées, les heures d'exploitation, et les types spécifiques à l'industrie. Google traite chaque page /locations/[slug] comme une liste d'entreprise locale distincte, ce qui est exactement ce que vous voulez pour les classements du pack local.
Les sites peuvent-ils avoir leurs propres articles de blog tout en partageant du contenu au niveau du réseau ?
Oui — c'est le pattern location_id nullable encore une fois. Les articles de blog avec location_id = NULL apparaissent sur le flux de blog de chaque site. Les articles avec un location_id spécifique n'apparaissent que sur le flux de ce site. Un site à Miami peut publier un article sur un événement communautaire local tandis que l'équipe centrale publie une réflexion au niveau du réseau. Les deux apparaissent dans le flux blog de Miami ; seul l'article central apparaît partout ailleurs.
Combien coûte la maintenance en cours comparé à la gestion de 50 sites séparés ?
Avec cette architecture, il y a une seule base de code, un seul déploiement et un seul ensemble de dépendances à maintenir. L'infrastructure mensuelle coûte 75-125 $ selon l'échelle. Comparez à 50 installations WordPress : 1 500 $/mois juste pour l'hébergement, plus 10-20 heures par mois en mises à jour de plugin, correctifs de sécurité et dépannage du site qui s'est cassé après une mise à jour automatique. Nous avons vu les entreprises multi-site réduire leur budget annuel d'opérations web de 60-70% après migration vers ce pattern.