Arquitetura Multi-Site para DSOs, Redes de Clínicas Veterinárias, Academias e Franquias
Sua DSO odontológica lança a unidade #50 e o tempo de deploy salta de quatro minutos para onze. Sua franquia de academias adiciona o site #127 e o build gera um erro de memória. Sua rede veterinária atualiza um endereço e 200 páginas são reconstruídas. Todo operador multi-localidade — grupos odontológicos, franquias de academia, redes de hotéis, redes de igrejas — bate no mesmo teto de arquitetura: o controle centralizado de marca quebra quando você tenta dar a cada unidade sua própria página SEO, seu próprio conteúdo, suas próprias meta tags. Uma atualização no CMS não deveria reimplementar 200 sites estáticos. Uma mudança de endereço não deveria retrigerar todo seu pipeline de build. Mas a maioria das configurações Next.js faz exatamente isso — porque tratam cada localidade como um projeto separado em vez de linhas dinâmicas em um único schema. Aqui está a arquitetura que resolve isso.
Eu construí esse padrão para grupos odontológicos, franquias de fitness, redes veterinárias e redes de restaurantes. Toda vez, começo com o mesmo schema de banco de dados, a mesma estrutura de rotas Next.js e o mesmo controle de acesso baseado em funções. O que muda é o seed data e os rótulos dos componentes. "Serviços" vira "Classes" em uma academia ou "Itens de Menu" em um restaurante. "Equipe" vira "Dentistas" ou "Instrutores" ou "Veterinários". A base embaixo? Idêntica.
Este artigo expõe o padrão de arquitetura multi-localidade universal uma vez, então mostra como se adapta a cinco indústrias completamente diferentes. Se você gerencia qualquer negócio multi-localidade — ou você é um desenvolvedor construindo para um — este é o blueprint.
Sumário
- O Problema Central que Todo Negócio Multi-Localidade Enfrenta
- O Schema de Banco de Dados Universal
- Arquitetura de Rotas Next.js
- Segurança em Nível de Linha e o Dashboard Admin
- Variação de Indústria 1: DSOs Odontológicas
- Variação de Indústria 2: Redes de Academias e Fitness
- Variação de Indústria 3: Grupos de Hotéis
- Variação de Indústria 4: Redes de Clínicas Veterinárias
- Variação de Indústria 5: Redes de Restaurantes
- Tabela de Comparação de Arquitetura
- Deploy e Performance em Escala
- Análise de Custos: O Que Isso Realmente Custa em 2026
- FAQ

O Problema Central que Todo Negócio Multi-Localidade Enfrenta
Sejamos diretos sobre o que geralmente acontece. Uma franquia ou negócio multi-localidade começa com um único website. Depois abre uma segunda unidade. Alguém coloca uma segunda instalação do WordPress. Quando há 15 unidades, você tem 15 sites WordPress separados, 15 temas diferentes (alguns estão três versões atrasados), 15 conjuntos diferentes de plugins, e zero controle centralizado.
O diretor de marketing quer atualizar o CTA principal da marca em todas as unidades. Isso é 15 logins, 15 edições e uma oração para que ninguém tenha quebrado seu template. O time de SEO quer saber quais unidades estão publicando conteúdo de blog e quais ficaram inativas por seis meses. Não há dashboard para isso — apenas uma planilha que alguém esqueceu de atualizar em março.
Este é o mesmo problema se você está gerenciando uma organização de suporte odontológico (DSO) com 50 práticas ou um grupo de restaurantes com 200 unidades. Os sintomas são idênticos:
- Desvio de marca. Unidades saem do padrão de marca porque ninguém está forçando consistência.
- Fragmentação de SEO. Nenhuma página de SEO local estruturada, nenhuma consistência de marcação de schema, nenhum sitemap centralizado.
- Caos administrativo. Cada unidade gerencia seu próprio site (malamente), ou a corporação lida com tudo (lentamente).
- Risco de deploy. Atualizar o site de uma unidade não deveria conseguir derrubar outro.
A solução não é um tema de CMS melhor. É uma arquitetura completamente diferente.
O Schema de Banco de Dados Universal
Tudo começa com uma tabela locations. Este é o âncora de todo o sistema. Eu uso Supabase como camada de banco de dados e autenticação porque dá a você Postgres, Row-Level Security, inscrições em tempo real, e um tier gratuito generoso — mas o schema funciona com qualquer banco de dados relacional.
Aqui está o schema principal:
-- A tabela âncora. Cada peça de conteúdo específico de localidade
-- referencia isso 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()
);
-- Tabelas de conteúdo seguem o MESMO padrão:
-- location_id é NULLABLE.
-- NULL = compartilhado em todas as unidades
-- Um valor = específico dessa unidade
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
);
O padrão location_id nullable é a percepção-chave. Quando um post de blog tem location_id = NULL, é um artigo em toda a rede ("5 Dicas para Dentes Saudáveis" compartilhado em todas as 50 clínicas odontológicas). Quando location_id tem um valor, é específico dessa unidade ("Dra. Silva Entra em Nossa Clínica de Austin"). Mesma tabela, mesmos padrões de query, mas o conteúdo pode ser compartilhado ou localizado com uma única coluna.
A coluna metadata JSONB é onde campos específicos de indústria vivem. Uma unidade odontológica pode armazenar {"insurance_accepted": ["Delta Dental", "Cigna"], "parking_info": "Lote gratuito atrás do prédio"}. Uma academia armazena {"equipment": ["squat racks", "rowing machines"], "peak_hours": "5-7 PM weekdays"}. Nenhuma migração de schema necessária — apenas diferentes formas de JSON.
Arquitetura de Rotas Next.js
O Next.js App Router mapeia limpamente para este modelo de dados. Aqui está a estrutura de rotas que funciona para toda indústria:
app/
├── page.tsx # Homepage
├── locations/
│ ├── page.tsx # Location finder (mapa + geo-search)
│ └── [slug]/
│ ├── page.tsx # Página de detalhe da unidade
│ ├── staff/page.tsx # Listagem de equipe para unidade
│ └── services/page.tsx # Serviços para unidade
├── services/
│ └── [service]/page.tsx # Descrição de serviço compartilhado
├── blog/
│ ├── page.tsx # Todos os posts de blog
│ └── [post]/page.tsx # Post de blog individual
├── about/page.tsx
└── contact/page.tsx
A página de detalhe da unidade (/locations/[slug]) é onde a mágica acontece. Uma única chamada generateStaticParams consulta cada unidade ativa e pré-renderiza todas elas no tempo de 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), // simplificado
supabase.from('services').select('*').or(`location_id.is.null,location_id.eq.${locationId}`),
supabase.from('testimonials').select('*').eq('is_approved', true),
])
// Renderizar página de unidade com todos os dados
// Esta é a mesma estrutura de componentes independente da indústria
}
A query de serviços usa aquele filtro or — pegue serviços onde location_id é null (serviços compartilhados) OU corresponde à unidade atual. Isto significa que uma DSO odontológica pode definir "Limpeza de Dentes" uma vez para todas as unidades, depois adicionar "Invisalign" apenas para unidades que oferecem. Nenhuma duplicação.
Para a página de localizador de unidades, armazeno coordenadas lat/lng e uso a extensão PostGIS do Supabase para geo-queries:
-- Encontrar unidades dentro de 40 km de coordenadas do usuário
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;

Segurança em Nível de Linha e o Dashboard Admin
Este é onde a arquitetura realmente compensa. Políticas RLS do Supabase deixam você definir acesso a dados no nível do banco de dados — não no código da sua aplicação.
-- Gerentes de unidade podem ver apenas dados de sua própria unidade
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'
)
);
Admins de rede veem tudo. Gerentes de unidade veem apenas sua unidade. Isto se aplica a toda tabela — serviços, equipe, posts de blog, depoimentos, eventos. Um padrão de política, aplicado consistentemente.
O dashboard admin mostra métricas em nível de rede:
- Atualidade de conteúdo: Quais unidades não atualizaram seu blog em 30+ dias?
- Tráfego por unidade: Dados do Google Search Console agregados por slug de unidade
- Leads por unidade: Envios de formulário e requisições de reserva por unidade
- Conformidade de marca: Todas as unidades estão usando o logo, cores e texto de CTA aprovados?
Variação de Indústria 1: DSOs Odontológicas
Um website de DSO precisa parecer uma marca odontológica unificada enquanto deixa cada prática destacar seus provedores e especialidades únicos.
Serviços mapeiam para procedimentos odontológicos: limpezas, restaurações, coroas, implantes, Invisalign, atendimento odontológico de emergência. Alguns são universais (toda unidade faz limpezas), outros são específicos de unidade (apenas três unidades oferecem anestesia sedativa).
Equipe são dentistas, higienistas e gerentes de consultório. Cada um tem um perfil com credenciais (DDS, DMD), especialidades, educação e uma foto profissional. Pais escolhendo um dentista pediátrico querem saber quem estará tratando seu filho.
CTA é "Agendar uma Consulta". Isto se conecta a Calendly, NexHealth, ou um sistema de reservas customizado. O widget de reserva pré-seleciona a unidade baseado em qual página de unidade o usuário veio.
Alvos de SEO local: "dentista em [cidade]", "[procedimento] em [cidade]", "dentista de emergência [cidade] [estado]". Cada página de unidade obtém marcação de dados estruturados para schemas Dentist e LocalBusiness.
Metadata JSONB armazena: planos de seguro aceitos, informações de estacionamento, recursos de acessibilidade, idiomas falados, se aceitam pacientes novos.
Variação de Indústria 2: Redes de Academias e Fitness
Redes de academias trocam "serviços" por "classes" — mas o modelo de dados é o mesmo. Uma classe de yoga na Unidade A e uma classe de HIIT na Unidade B são apenas linhas na tabela de serviços com valores location_id diferentes.
Serviços são tipos de classe com dados de horário. Os metadados armazenam o horário semanal como JSON, atribuição de instrutor, limites de capacidade, e se drop-ins são permitidos.
Equipe são instrutores e instruintes com certificações (NASM, ACE, CrossFit L2), especialidades, e disponibilidade para reservas de treinamento pessoal.
CTA é "Junte-se Agora" — um checkout de inscrição Stripe que lida com tiers de membros e acesso entre unidades. Um membro que se inscrever na unidade do centro deveria conseguir fazer check-in na unidade dos subúrbios também.
Alvos de SEO local: "academia perto de mim", "classes de fitness [cidade]", "[tipo de classe] classes [cidade]", "personal trainer [cidade]".
Metadata JSONB armazena: lista de equipamentos, horário de classe, horários de pico, comodidades (sauna, piscina, creche), disponibilidade de estacionamento gratuito.
Variação de Indústria 3: Grupos de Hotéis
Grupos de hotéis boutique e cadeias de hotéis independentes se beneficiam enormemente desse padrão — especialmente porque possibilita reservas diretas que bypas taxas de OTA (tipicamente 15-25% por reserva no Booking.com ou Expedia).
Serviços se tornam tipos de quarto: Quarto Standard, Suíte King, Penthouse. Cada um obtém fotos, listas de comodidades, metragem quadrada, e preço base. Preços específicos de unidade vivem nos metadados ou uma tabela de rates separada com faixas de data.
Equipe é mais leve aqui — talvez um gerente geral destacado ou concierge para a narrativa de marca.
CTA é "Reserve Direto" — o padrão FME (Find, Match, Engage) que dá aos hóspedes uma razão para reservar no site do próprio hotel em vez de uma OTA. Tipicamente uma "garantia de melhor preço" ou upgrade complementar.
Alvos de SEO local: "hotéis em [cidade]", "[nome do hotel] avaliações", "hotel boutique [bairro] [cidade]", "hotéis perto de [ponto de referência]".
Metadata JSONB armazena: comodidades (piscina, spa, restaurante, academia, carregamento de EV), atrações próximas, calendário de eventos locais, horários de check-in/check-out, política de animais de estimação.
Variação de Indústria 4: Redes de Clínicas Veterinárias
Redes veterinárias estão crescendo rápido em 2026 — consolidação em medicina veterinária espelha o que aconteceu com DSOs odontológicas uma década atrás. A mesma arquitetura multi-localidade se aplica perfeitamente.
Serviços são serviços de cuidados de animais de estimação: exames de bem-estar, vacinações, limpeza dentária, cirurgia, atendimento de emergência, hospedagem, grooming. Algumas unidades oferecem cuidados de animais exóticos; a maioria não.
Equipe são veterinários com expertise em espécies (pequenos animais, equino, exótico), certificações de conselho, e educação.
CTA é "Agendar uma Consulta" com uma reviravolta — o formulário de intake deveria capturar informações de animais de estimação (espécie, raça, idade, motivo da visita) para rotear a consulta corretamente.
Alvos de SEO local: "veterinário em [cidade]", "vet de emergência [cidade]", "[espécie] vet [cidade]", "limpeza dentária de animais de estimação [cidade]".
Metadata JSONB armazena: espécies aceitas, horários de emergência (se diferentes dos horários regulares), capacidade de hospedagem, se possuem laboratório e imaging no local.
Variação de Indústria 5: Redes de Restaurantes
Serviços se tornam seções de menu: entradas, pratos principais, sobremesas, bebidas. A coisa crítica aqui é que preços podem variar por unidade. Um hambúrguer custa $14 em Austin e $19 em Manhattan. A coluna metadata lida com isto com overrides de preço específicos de unidade.
Equipe são chefs destacados ou pit masters — isto funciona melhor para marcas onde as pessoas por trás da comida são parte da história.
CTA é "Pedir Online" — um link com consciência de localidade que roteia para o sistema de pedidos online correto (Toast, Square, ChowNow, ou custom) para a unidade mais próxima do usuário.
Alvos de SEO local: "[nome do restaurante] [cidade] menu", "restaurantes perto de mim", "restaurante [tipo de culinária] [cidade]", "[nome do restaurante] horários".
Metadata JSONB armazena: raio de entrega, disponibilidade de reservas (com link do OpenTable ou Resy), detalhes de estacionamento, capacidade de jantar privado, horários de happy hour.
Tabela de Comparação de Arquitetura
| Componente | DSO Odontológico | Rede de Academia | Grupo de Hotel | Rede Veterinária | Restaurante | |-----------|-----------|-----------|-------------|-----------|------------|| | Rótulo "Serviços" | Procedimentos | Classes | Tipos de Quarto | Serviços de Animais de Estimação | Itens de Menu | | Rótulo "Equipe" | Dentistas | Instrutores | Gerenciamento | Veterinários | Chefs | | CTA Primário | Agendar Consulta | Ingressar Membros | Reservar Quarto | Agendar Consulta | Pedir Online | | Integração de Reserva | NexHealth, Calendly | Inscrições Stripe | Custom / Cloudbeds | Custom + intake de animais de estimação | Toast, Square | | Dados Locais Chave | Seguros, estacionamento | Horário, equipamento | Comodidades, atrações | Espécies, horários de emergência | Preço de menu, entrega | | Palavra-chave SEO Primária | "dentista em [cidade]" | "academia perto de mim" | "hotéis em [cidade]" | "vet em [cidade]" | "[marca] [cidade] menu" | | Marcação de Schema | Dentist, LocalBusiness | SportsActivityLocation | Hotel, LodgingBusiness | VeterinaryCare | Restaurant, Menu | | Tabelas de DB Alteradas | 0 | 0 | 0 | 0 | 0 |
Aquela última linha é o ponto. Zero tabelas de banco de dados mudam entre indústrias. Você está usando as mesmas tabelas locations, services, staff, blog_posts, testimonials, e events. Os rótulos na UI mudam. As formas de metadados mudam. A arquitetura não.
Deploy e Performance em Escala
Nós deployamos isto no Vercel com ISR (Incremental Static Regeneration). Cada página de unidade é gerada estaticamente no tempo de build e revalida a cada 60 segundos. Para uma rede de 200 unidades, isto é 200 páginas HTML estáticas que carregam em menos de 1 segundo em qualquer dispositivo.
Os números importam. Aqui está o que normalmente vemos:
- Tempo de build para 200 unidades: ~45 segundos no Vercel Pro
- TTFB por página de unidade: < 50ms (servida de CDN edge)
- Pontuações Lighthouse: 95+ em todo o espectro
- Revalidação ISR: Stale-while-revalidate de 60 segundos significa atualizações de conteúdo aparecem dentro de um minuto sem um rebuild completo
Adicionar uma nova unidade é um insert no banco de dados mais uma chamada de revalidação on-demand opcional. Nenhum novo deploy necessário. A função generateStaticParams pega novas unidades no próximo build ou ciclo de ISR.
// API route para disparar revalidação quando uma unidade é adicionada/atualizada
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 })
}
Análise de Custos: O Que Isso Realmente Custa em 2026
Vamos falar números reais. Esta é uma pergunta comum que recebemos durante conversas de preços.
| Componente | Custo Mensal (50 unidades) | Custo Mensal (200 unidades) |
|---|---|---|
| Supabase Pro | $25 | $25 (mesmo tier lida com ambos) |
| Vercel Pro | $20 | $20 |
| Bandwidth Vercel (overage) | ~$0 | ~$40 |
| Domínio + DNS (Cloudflare) | $0 | $0 |
| Image CDN (Cloudflare R2) | ~$5 | ~$15 |
| Monitoramento (Sentry) | $26 | $26 |
| Total de infraestrutura | ~$76/mês | ~$126/mês |
Compare isso com 50 sites WordPress separados a ~$30/mês cada para hospedagem gerenciada — isto é $1.500/mês antes de você nem pensar em manutenção, licenças de plugin, ou a pessoa que tem que manter todos atualizados.
O investimento em desenvolvimento é maior upfront — normalmente cotamos builds multi-localidade na faixa de $30K-$80K dependendo da complexidade — mas o custo operacional contínuo é uma fração da alternativa WordPress multisite. E você não está pagando $500/mês por unidade para algum vendor de website de franquia que o bloqueia em sua plataforma.
Para times interessados em explorar integrações de CMS headless ou considerando Astro em vez de Next.js para builds estáticos ainda mais rápidos, a mesma arquitetura de banco de dados se aplica. O framework frontend é swappable; o modelo de dados não.
FAQ
Esta arquitetura pode lidar com unidades em fusos horários diferentes?
Absolutamente. A coluna hours JSONB armazena horários de operação de cada unidade em seu fuso horário local. Incluímos um campo timezone (ex: "America/Chicago") nos metadados de unidade e usamos isso para qualquer exibição sensível ao tempo como badges "Aberto Agora". Todos os timestamps no banco de dados são armazenados como UTC e convertidos no frontend.
Como você lida com unidades que oferecem serviços diferentes?
Este é o padrão location_id nullable em ação. Serviços com location_id = NULL são compartilhados entre todas as unidades — aparecem em cada página de unidade. Serviços com um location_id específico aparecem apenas para essa unidade. Você também pode usar uma tabela de junção (location_services) para relacionamentos muitos-para-muitos se serviços compartilhados precisarem de overrides por-unidade como preço customizado ou disponibilidade.
O que acontece quando uma nova unidade abre?
Um admin de rede adiciona a unidade via dashboard. Isto cria uma linha na tabela locations, dispara um webhook que desencadeia revalidação de ISR, e a página de nova unidade está em produção dentro de 60 segundos. Nenhum desenvolvedor necessário, nenhum deploy, nenhuma mudança de DNS. A unidade herda todos os serviços e conteúdo compartilhados imediatamente.
Isto é melhor que WordPress Multisite para franquias?
Para a maioria dos negócios multi-localidade, sim. WordPress Multisite foi a resposta padrão por uma década, mas tem problemas reais: uma vulnerabilidade de plugin único pode derrubar toda a rede, performance degrada conforme você adiciona sites, e você precisa de um sysadmin dedicado para mantê-lo saudável. Esta arquitetura headless dá a você performance de site estático, segurança em nível de banco de dados, e zero risco de runtime compartilhado entre unidades.
Como gerentes de unidade editam seu próprio conteúdo sem quebrar outras unidades?
Row-Level Security no nível do banco de dados garante que um gerente de unidade em Austin literalmente não possa ver ou modificar dados pertencentes à unidade de Denver. Não é forçado por código da aplicação que poderia ter bugs — é forçado pelo próprio Postgres. Mesmo que a UI admin tivesse um bug que tentasse consultar dados de outra unidade, o banco de dados retornaria resultados vazios.
E SEO — cada unidade tem seu próprio sitemap?
Cada página de unidade obtém sua própria entrada em um sitemap dinâmico único gerado no tempo de build. Também geramos dados estruturados por-unidade (JSON-LD) com schema LocalBusiness, geo-coordenadas, horários de operação, e tipos específicos de indústria. Google trata cada página /locations/[slug] como uma listagem de negócio local distinta, que é exatamente o que você quer para rankings de local pack.
Unidades podem ter seus próprios posts de blog enquanto compartilham conteúdo em toda rede?
Sim — este é novamente o padrão location_id nullable. Posts de blog com location_id = NULL aparecem no feed de blog de cada unidade. Posts com um location_id específico aparecem apenas no feed dessa unidade. Uma unidade em Miami pode publicar um post sobre um evento comunitário local enquanto o time corporativo publica thought leadership em toda rede. Ambos aparecem no feed de blog de Miami; apenas o post corporativo aparece em todos os outros lugares.
Quanto custa manutenção contínua comparado a gerenciar 50 sites separados?
Com esta arquitetura, há uma base de código, um deploy, e um conjunto de dependências para manter. Infraestrutura mensal corre $75-$125 dependendo da escala. Compre isso com 50 instalações WordPress: $1.500/mês apenas em hospedagem, mais 10-20 horas por mês em atualizações de plugin, patches de segurança, e troubleshooting da uma unidade que quebrou após um auto-update. Vimos negócios multi-localidade cortar seu orçamento anual de operações web por 60-70% após migrar para este padrão.