Arquitetura Multi-Site para DSOs, Redes de Clínicas Veterinárias, Academias e Franquias
Uma DSO odontológica com 50 consultórios tem o mesmo problema de arquitetura de site que uma rede de academias com 200 unidades, um grupo hoteleiro com 30 propriedades e uma rede de igrejas com 15 campi. Todos precisam de: controle centralizado de marca, conteúdo localizado por unidade, um painel administrativo único, páginas de SEO por local, e uma implantação que atualiza tudo simultaneamente sem quebrar nada. A arquitetura é idêntica. O conteúdo é diferente.
Construí esse padrão para grupos odontológicos, franquias de fitness, redes veterinárias e cadeias de restaurantes. Todas as vezes, começo com o mesmo schema de banco de dados, a mesma estrutura de rotas do Next.js e o mesmo controle de acesso baseado em funções. O que muda é o seed data e os rótulos dos componentes. "Services" vira "Classes" em uma academia ou "Menu Items" em um restaurante. "Staff" vira "Dentists" ou "Trainers" ou "Veterinarians." A infraestrutura subjacente? Idêntica.
Este artigo apresenta o padrão universal de arquitetura multi-local uma vez, depois mostra como ele se adapta a cinco indústrias completamente diferentes. Se você gerencia qualquer tipo de negócio multi-local — ou é um desenvolvedor construindo para um — este é o blueprint.
Sumário
- O Problema Central Que Todo Negócio Multi-Local Enfrenta
- O Schema de Banco de Dados Universal
- Arquitetura de Rotas do Next.js
- Segurança em Nível de Linha e o Painel Administrativo
- Variação da Indústria 1: DSOs Odontológicas
- Variação da Indústria 2: Academias e Cadeias de Fitness
- Variação da Indústria 3: Grupos Hoteleiros
- Variação da Indústria 4: Redes de Clínicas Veterinárias
- Variação da Indústria 5: Cadeias de Restaurantes
- A Tabela de Comparação de Arquitetura
- Implantação e Performance em Larga Escala
- Detalhamento de Custos: O Que Isso Realmente Custa em 2025
- FAQ

O Problema Central Que Todo Negócio Multi-Local Enfrenta
Vamos ser francos sobre o que normalmente acontece. Uma franquia ou negócio multi-local começa com um único site. Depois abre uma segunda unidade. Alguém cria uma segunda instalação do WordPress. Quando há 15 unidades, você tem 15 sites WordPress separados, 15 temas diferentes (alguns três versões atrasados), 15 conjuntos diferentes de plugins, e zero controle centralizado.
O diretor de marketing quer atualizar o CTA primário da marca em todas as unidades. São 15 logins, 15 edições e uma oração para que ninguém tenha quebrado seu template. O time de SEO quer ver quais unidades estão publicando conteúdo de blog e quais ficaram mudas 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ê é uma organização de suporte odontológico (DSO) gerenciando 50 consultórios ou um grupo de restaurantes com 200 unidades. Os sintomas são idênticos:
- Variação de marca. Unidades saem do padrão da marca porque ninguém está forçando consistência.
- Fragmentação de SEO. Sem páginas de SEO local estruturado, sem consistência de marcação de schema, sem sitemap centralizado.
- Caos administrativo. Cada unidade gerencia seu próprio site (mal), ou a corporação cuida de tudo (lentamente).
- Risco de implantação. Atualizar o site de uma unidade não deveria conseguir derrubar o de outra.
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 a camada de banco de dados e autenticação porque oferece Postgres, Row-Level Security, inscrições em tempo real e um plano gratuito generoso — mas o schema funciona com qualquer banco de dados relacional.
Aqui está o schema central:
-- A tabela âncora. Cada conteúdo específico de uma unidade
-- referencia isto 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 para essa 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 é o insight-chave. Quando um post de blog tem location_id = NULL, é um artigo de rede ("5 Dicas para Dentes Saudáveis" compartilhado em todos os 50 consultórios odontológicos). Quando location_id tem um valor, é específico para essa unidade ("Dr. Smith Se Junta à Nossa Prática em 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 vivem os campos específicos da indústria. Uma unidade odontológica pode armazenar {"insurance_accepted": ["Delta Dental", "Cigna"], "parking_info": "Free lot behind building"}. Uma academia armazena {"equipment": ["squat racks", "rowing machines"], "peak_hours": "5-7 PM weekdays"}. Nenhuma migração de schema necessária — apenas formas JSON diferentes.
Arquitetura de Rotas do Next.js
O Next.js App Router mapeia limpa e claramente para este modelo de dados. Aqui está a estrutura de rota que funciona para cada indústria:
app/
├── page.tsx # Homepage
├── locations/
│ ├── page.tsx # Localizador de unidades (mapa + geo-busca)
│ └── [slug]/
│ ├── page.tsx # Página de detalhe da unidade
│ ├── staff/page.tsx # Listagem de staff para a unidade
│ └── services/page.tsx # Serviços para a unidade
├── services/
│ └── [service]/page.tsx # Descrição de serviço compartilhado
├── blog/
│ ├── page.tsx # Todos os posts de blog
│ └── [post]/page.tsx # Post individual de blog
├── 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 todas as unidades ativas 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 componente independentemente da indústria
}
A query de serviços usa esse filtro or — pega serviços onde location_id é nulo (serviços compartilhados) OU corresponde à unidade atual. Isso significa que uma DSO odontológica pode definir "Limpeza de Dentes" uma vez para todas as unidades, depois adicionar "Invisalign" apenas para unidades que o oferecem. Sem duplicação.
Para a página localizador de unidades, armazeno coordenadas lat/lng e uso a extensão PostGIS do Supabase para geo-queries:
-- Encontrar unidades dentro de 25 milhas das 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 Painel Administrativo
É aqui que a arquitetura realmente compensa. As 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.
-- Gerenciadores de unidades podem ver apenas os 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'
)
);
Administradores de rede veem tudo. Gerenciadores de unidades veem apenas sua unidade. Isto se aplica a cada tabela — serviços, staff, posts de blog, depoimentos, eventos. Um padrão de política, aplicado consistentemente.
O painel administrativo mostra métricas de nível de rede:
- Atualização 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ários e solicitações de agendamento por unidade
- Conformidade de marca: Todas as unidades estão usando o logo aprovado, cores e texto de CTA?
Variação da Indústria 1: DSOs Odontológicas
Um site de DSO precisa parecer uma marca odontológica unificada enquanto deixa cada prática destacar seus provedores e especialidades únicos.
Services 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 da unidade (apenas três unidades oferecem odontologia com sedação).
Staff são dentistas, higienistas dentárias e gerenciadores de consultório. Cada um recebe um perfil com credenciais (DDS, DMD), especialidades, educação e uma foto profissional. Pais escolhendo um dentista pediátrico querem ver quem estará tratando seu filho.
CTA é "Agendar uma Consulta." Isto se conecta ao Calendly, NexHealth ou um sistema de agendamento customizado. O widget de agendamento 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 recebe marcação de dados estruturados para schemas Dentist e LocalBusiness.
Metadata JSONB armazena: planos de seguro aceitos, informações de estacionamento, características de acessibilidade, idiomas falados, se aceitam novos pacientes.
Variação da Indústria 2: Academias e Cadeias de Fitness
Academias trocam "services" por "classes" — mas o modelo de dados é o mesmo. Uma aula de yoga na Unidade A e uma aula de HIIT na Unidade B são apenas linhas na tabela services com diferentes valores de location_id.
Services são tipos de classe com dados de agenda. Os metadados armazenam a agenda semanal como JSON, atribuição de instrutor, limites de capacidade e se drop-ins são permitidos.
Staff são treinadores e instrutores com certificações (NASM, ACE, CrossFit L2), especialidades e disponibilidade para agendamentos de treinamento pessoal.
CTA é "Junte-se Agora" — um checkout de inscrição do Stripe que lida com diferentes níveis de membros e acesso entre unidades. Um membro que se inscreve na unidade no centro da cidade deve conseguir fazer check-in na unidade no subúrbio também.
Alvos de SEO Local: "academia perto de mim", "aulas de fitness [cidade]", "aulas de [tipo de classe] [cidade]", "personal trainer [cidade]".
Metadata JSONB armazena: lista de equipamentos, agenda de aulas, horários de pico, comodidades (sauna, piscina, creche), disponibilidade de estacionamento gratuito.
Variação da Indústria 3: Grupos Hoteleiros
Grupos hoteleiros boutique e cadeias de hotéis independentes se beneficiam enormemente deste padrão — especialmente porque permite reservas diretas que contornam taxas de OTA (tipicamente 15-25% por reserva em Booking.com ou Expedia).
Services se tornam tipos de quartos: Quarto Padrão, Suite King, Penthouse. Cada um recebe fotos, listas de comodidades, metragem quadrada e preço base. Preços específicos da unidade vivem nos metadados ou em uma tabela de tarifas separada com intervalos de datas.
Staff é mais leve aqui — talvez um gerente geral em destaque ou concierge para a narrativa da marca.
CTA é "Reservar 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 tarifa" ou upgrade cortesia.
Alvos de SEO Local: "hotéis em [cidade]", "avaliações [nome do hotel]", "hotel boutique [bairro] [cidade]", "hotéis perto de [marco histórico]".
Metadata JSONB armazena: comodidades (piscina, spa, restaurante, academia, carregamento 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 da Indústria 4: Redes de Clínicas Veterinárias
Redes veterinárias estão crescendo rapidamente em 2025 — consolidação em medicina veterinária espelha o que aconteceu com DSOs odontológicas uma década atrás. A mesma arquitetura multi-local se aplica perfeitamente.
Services são serviços de cuidados com animais: exames de bem-estar, vacinações, limpeza dental, cirurgia, atendimento de emergência, hospedagem, banho e tosa. Algumas unidades oferecem cuidados com animais exóticos; a maioria não.
Staff são veterinários com expertise em espécies (pequenos animais, equinos, exóticos), certificações de conselho e educação.
CTA é "Agendar uma Consulta" com um detalhe — o formulário de entrada deve capturar informações do animal (espécie, raça, idade, motivo da visita) para rotear a consulta corretamente.
Alvos de SEO Local: "veterinário em [cidade]", "veterinário de emergência [cidade]", "veterinário de [espécie] [cidade]", "limpeza dental para animais [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 imagem no local.
Variação da Indústria 5: Cadeias de Restaurantes
Services se tornam seções de menu: entradas, pratos principais, sobremesas, bebidas. A coisa crítica aqui é que os preços podem variar por unidade. Um hambúrguer custa $14 em Austin e $19 em Manhattan. A coluna metadata lida com isso com overrides de preço específicos da unidade.
Staff são chefs ou pit masters em destaque — isso funciona melhor para marcas onde as pessoas por trás da comida são parte da história.
CTA é "Peça Online" — um link ciente da localização que roteia para o sistema correto de pedidos online (Toast, Square, ChowNow ou customizado) para a unidade mais próxima do usuário.
Alvos de SEO Local: "menu [nome do restaurante] [cidade]", "restaurantes perto de mim", "restaurante [tipo de culinária] [cidade]", "horários [nome do restaurante]".
Metadata JSONB armazena: raio de entrega, disponibilidade de reserva (com link OpenTable ou Resy), detalhes de estacionamento, capacidade de jantar privado, horários de happy hour.
A Tabela de Comparação de Arquitetura
| Componente | DSO Odontológica | Cadeia de Academia | Grupo Hoteleiro | Rede Veterinária | Restaurante |
|---|---|---|---|---|---|
| Rótulo "Services" | Procedimentos | Aulas | Tipos de Quarto | Serviços para Animais | Itens de Menu |
| Rótulo "Staff" | Dentistas | Treinadores | Gerência | Veterinários | Chefs |
| CTA Primário | Agendar Consulta | Associar-se | Reservar Quarto | Agendar Consulta | Pedir Online |
| Integração de Agendamento | NexHealth, Calendly | Stripe Subscriptions | Custom / Cloudbeds | Custom + entrada de animal | Toast, Square |
| Dados locais-chave | Seguro, estacionamento | Agenda, equipamento | Comodidades, atrações | Espécie, hrs emergência | Preço do menu, entrega |
| Palavra-chave SEO Primária | "dentista em [cidade]" | "academia perto de mim" | "hotéis em [cidade]" | "veterinário 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. Nenhuma tabela de banco de dados muda 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 metadata mudam. A arquitetura não.
Implantação e Performance em Larga Escala
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 cadeia de 200 unidades, são 200 páginas HTML estáticas que carregam em menos de 1 segundo em qualquer dispositivo.
Os números importam. Aqui está o que típicamente vemos:
- Tempo de build para 200 unidades: ~45 segundos no Vercel Pro
- TTFB por página de unidade: < 50ms (servido do CDN edge)
- Pontuações de Lighthouse: 95+ em todos os aspectos
- Revalidação ISR: 60 segundos de stale-while-revalidate significa que atualizações de conteúdo aparecem em menos de um minuto sem um build completo
Adicionar uma nova unidade é uma inserção no banco de dados mais uma chamada de revalidação sob demanda opcional. Nenhum novo deployment necessário. A função generateStaticParams pega novas unidades no próximo build ou ciclo ISR.
// Rota de API 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 })
}
Detalhamento de Custos: O Que Isso Realmente Custa em 2025
Vamos falar números reais. Esta é uma pergunta comum que recebemos durante conversas de precificação.
| Componente | Custo Mensal (50 unidades) | Custo Mensal (200 unidades) |
|---|---|---|
| Supabase Pro | $25 | $25 (mesmo plano cobre ambos) |
| Vercel Pro | $20 | $20 |
| Vercel Bandwidth (excedente) | ~$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 com 50 sites WordPress separados a ~$30/mês cada — são $1.500/mês antes mesmo de pensar em manutenção, licenças de plugins ou a pessoa que tem que mantê-los todos atualizados.
O investimento em desenvolvimento é maior antecipadamente — tipicamente cotamos builds multi-local entre $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 site de franquia que te prende à 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 é intercambiável; 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 operacionais de cada unidade em seu fuso horário local. Incluímos um campo timezone (por exemplo, "America/Chicago") nos metadados de localização e usamos isso para qualquer display sensível a tempo como badges "Open Now". 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?
Esse é o padrão location_id nullable em ação. Services com location_id = NULL são compartilhados em todas as unidades — aparecem na página de cada unidade. Services 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 aciona revalidação ISR e a página de nova unidade está ao vivo em 60 segundos. Nenhum desenvolvedor necessário, nenhum deployment, nenhuma mudança de DNS. A unidade herda imediatamente todos os serviços e conteúdo compartilhados.
Isto é melhor que WordPress Multisite para franquias?
Para a maioria dos negócios multi-local, sim. WordPress Multisite foi a resposta padrão por uma década, mas tem problemas reais: uma única vulnerabilidade de plugin pode derrubar a rede inteira, a performance degrada conforme você adiciona sites, e você precisa de um sysadmin dedicado para mantê-lo saudável. Esta arquitetura headless te dá performance de site estático, segurança em nível de banco de dados, e risco compartilhado zero entre unidades.
Como gerenciadores 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 gerenciador de unidade em Austin literalmente não consegue ver ou modificar dados pertencentes à unidade de Denver. Não é forçado por código de aplicação que poderia ter bugs — é forçado pelo Postgres em si. Mesmo se a UI do admin tivesse um bug que tentasse consultar dados de outra unidade, o banco de dados retornaria resultados vazios.
E quanto a SEO — cada unidade recebe seu próprio sitemap?
Cada página de unidade recebe sua própria entrada em um único sitemap dinâmico gerado no tempo de build. Também geramos dados estruturados por unidade (JSON-LD) com schema LocalBusiness, geo-coordenadas, horários operacionais e tipos específicos da 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 de rede?
Sim — esse é o padrão location_id nullable novamente. 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 daquela unidade. Uma unidade em Miami pode publicar um post sobre um evento comunitário local enquanto o time corporativo publica thought leadership de 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 com gerenciar 50 sites separados?
Com esta arquitetura, há uma base de código, um deployment e um conjunto de dependências para manter. Infraestrutura mensal custa $75-$125 dependendo da escala. Compare com 50 installs WordPress: $1.500/mês em hosting sozinho, mais 10-20 horas por mês em atualizações de plugin, patches de segurança e troubleshooting da unidade que quebrou depois de uma auto-atualização. Vimos negócios multi-local cortarem seu orçamento anual de operações web em 60-70% depois de migrar para este padrão.