Una DSO dental con 50 prácticas tiene el mismo problema de arquitectura web que una cadena de gimnasios con 200 ubicaciones, un grupo hotelero con 30 propiedades y una red de iglesias con 15 campus. Todos necesitan: control de marca centralizado, contenido localizado por ubicación, un panel de administración único, páginas SEO por ubicación, y un despliegue que actualiza todo simultáneamente sin romper nada. La arquitectura es idéntica. El contenido es diferente.

He construido este patrón para grupos dentales, franquicias de fitness, redes veterinarias y cadenas de restaurantes. Cada vez, empiezo con el mismo esquema de base de datos, la misma estructura de rutas de Next.js, y el mismo control de acceso basado en roles. Lo que cambia es la data de semilla y las etiquetas de componentes. "Services" se convierte en "Classes" en un gimnasio o "Menu Items" en un restaurante. "Staff" se convierte en "Dentists" o "Trainers" o "Veterinarians." ¿La plomería subyacente? Idéntica.

Este artículo establece el patrón arquitectónico universal de multi-ubicación una sola vez, luego muestra cómo se adapta a cinco industrias completamente diferentes. Si diriges cualquier tipo de negocio multi-ubicación — o eres un desarrollador construyendo para uno — este es el plano.

Tabla de Contenidos

Multi-Site Architecture for DSOs, Vet Chains, Gyms & Franchises

El Problema Central que Enfrenta Cada Negocio Multi-ubicación

Seamos francos sobre lo que normalmente sucede. Una franquicia o negocio multi-ubicación comienza con un sitio web único. Luego abren una segunda ubicación. Alguien configura una segunda instalación de WordPress. Para cuando hay 15 ubicaciones, tienes 15 sitios de WordPress separados, 15 temas diferentes (algunos están tres versiones atrás), 15 conjuntos diferentes de plugins, y cero control centralizado.

El director de marketing quiere actualizar el CTA principal de la marca en todas las ubicaciones. Son 15 inicios de sesión, 15 ediciones, y una plegaria de que nadie haya roto su plantilla. El equipo de SEO quiere ver qué ubicaciones están publicando contenido de blog y cuáles han estado oscuras durante seis meses. No hay un panel para eso — solo una hoja de cálculo que alguien olvidó actualizar en marzo.

Este es el mismo problema si eres una organización de soporte dental (DSO) administrando 50 prácticas o un grupo de restaurantes con 200 ubicaciones. Los síntomas son idénticos:

  • Deriva de marca. Las ubicaciones se desalinean de la marca porque nadie está haciendo cumplir la consistencia.
  • Fragmentación de SEO. Sin páginas de SEO local estructurado, sin consistencia de marcado de esquema, sin mapa del sitio centralizado.
  • Caos administrativo. Cada ubicación administra su propio sitio (mal), o la corporativa maneja todo (lentamente).
  • Riesgo de despliegue. Actualizar el sitio de una ubicación no debería poder derribar el de otra.

La solución no es un mejor tema de CMS. Es una arquitectura completamente diferente.

El Esquema de Base de Datos Universal

Todo comienza con una tabla locations. Este es el ancla para todo el sistema. Uso Supabase como capa de base de datos y autenticación porque te da Postgres, Seguridad a Nivel de Fila, suscripciones en tiempo real, y un nivel gratuito generoso — pero el esquema funciona con cualquier base de datos relacional.

Aquí está el esquema central:

-- La tabla ancla. Cada pieza de contenido específico de ubicación
-- hace referencia a esto a través de 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()
);

-- Las tablas de contenido siguen el MISMO patrón:
-- location_id es NULLABLE.
-- NULL = compartido en todas las ubicaciones
-- Un valor = específico para esa ubicación

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
);

El patrón de location_id nullable es la percepción clave. Cuando un artículo de blog tiene location_id = NULL, es un artículo de toda la red ("5 Consejos para Dientes Saludables" compartido en los 50 consultorios dentales). Cuando location_id tiene un valor, es específico de esa ubicación ("El Dr. Smith se Unió a Nuestra Práctica en Austin"). Misma tabla, mismos patrones de consulta, pero el contenido puede ser compartido o localizado con una sola columna.

La columna metadata JSONB es donde viven los campos específicos de la industria. Una ubicación dental podría almacenar {"insurance_accepted": ["Delta Dental", "Cigna"], "parking_info": "Free lot behind building"}. Un gimnasio almacena {"equipment": ["squat racks", "rowing machines"], "peak_hours": "5-7 PM weekdays"}. Sin migración de esquema necesaria — solo diferentes formas JSON.

Arquitectura de Rutas Next.js

El App Router de Next.js se mapea limpiamente a este modelo de datos. Aquí está la estructura de rutas que funciona para cada industria:

app/
├── page.tsx                          # Página de inicio
├── locations/
│   ├── page.tsx                      # Buscador de ubicaciones (mapa + búsqueda geo)
│   └── [slug]/
│       ├── page.tsx                  # Página de detalle de ubicación
│       ├── staff/page.tsx            # Listado de personal para ubicación
│       └── services/page.tsx         # Servicios para ubicación
├── services/
│   └── [service]/page.tsx            # Descripción de servicio compartido
├── blog/
│   ├── page.tsx                      # Todos los artículos de blog
│   └── [post]/page.tsx               # Artículo de blog individual
├── about/page.tsx
└── contact/page.tsx

La página de detalle de ubicación (/locations/[slug]) es donde ocurre la magia. Una única llamada a generateStaticParams consulta cada ubicación activa y pre-renderiza todas ellas en tiempo de construcción:

// 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 ubicación con todos los datos
  // Esta es la misma estructura de componentes independientemente de la industria
}

La consulta de servicios usa ese filtro or — obtener servicios donde location_id es nulo (servicios compartidos) O coincide con la ubicación actual. Esto significa que una DSO dental puede definir "Limpieza de Dientes" una sola vez para todas las ubicaciones, luego agregar "Invisalign" solo para ubicaciones que lo ofrecen. Sin duplicación.

Para la página del buscador de ubicaciones, almaceno coordenadas lat/lng y uso la extensión PostGIS de Supabase para consultas geo:

-- Encontrar ubicaciones dentro de 25 millas de las coordenadas del usuario
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;

Multi-Site Architecture for DSOs, Vet Chains, Gyms & Franchises - architecture

Seguridad a Nivel de Fila y el Panel de Administración

Aquí es donde la arquitectura realmente da sus frutos. Las políticas RLS de Supabase te permiten definir el acceso a datos a nivel de base de datos — no en tu código de aplicación.

-- Los gerentes de ubicación solo pueden ver los datos de su propia ubicación
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'
    )
  );

Los administradores de red lo ven todo. Los gerentes de ubicación ven solo su ubicación. Esto se aplica a cada tabla — servicios, personal, artículos de blog, testimoniales, eventos. Un patrón de política, aplicado consistentemente.

El panel de administración muestra métricas a nivel de red:

  • Frescura del contenido: ¿Qué ubicaciones no han actualizado su blog en 30+ días?
  • Tráfico por ubicación: Datos de Google Search Console agregados por slug de ubicación
  • Leads por ubicación: Envíos de formularios y solicitudes de reserva por ubicación
  • Cumplimiento de marca: ¿Están todas las ubicaciones usando el logo, colores y texto de CTA aprobados?

Variación de Industria 1: DSOs Dentales

Un sitio web de DSO necesita sentirse como una marca dental unificada mientras permite que cada práctica destaque sus proveedores y especialidades únicos.

Services se mapean a procedimientos dentales: limpiezas, empastes, coronas, implantes, Invisalign, cuidado dental de emergencia. Algunos son universales (cada ubicación hace limpiezas), otros son específicos de ubicación (solo tres ubicaciones ofrecen odontología con sedación).

Staff son dentistas, higienistas y gerentes de oficina. Cada uno obtiene un perfil con credenciales (DDS, DMD), especialidades, educación y una foto profesional. Los padres que eligen un dentista pediátrico quieren ver quién tratará a su hijo.

CTA es "Reservar una Cita". Esto se conecta a Calendly, NexHealth, o un sistema de reservas personalizado. El widget de reservas pre-selecciona la ubicación basándose en cuál página de ubicación vino el usuario.

Objetivos de SEO local: "dentist in [city]", "[procedure] in [city]", "emergency dentist [city] [state]". Cada página de ubicación obtiene marcado de datos estructurados para esquemas Dentist y LocalBusiness.

Metadata JSONB almacena: planes de seguros aceptados, información de estacionamiento, características de accesibilidad, idiomas hablados, si aceptan pacientes nuevos.

Variación de Industria 2: Cadenas de Gimnasios y Fitness

Las cadenas de gimnasios cambian "services" por "classes" — pero el modelo de datos es el mismo. Una clase de yoga en la Ubicación A y una clase de HIIT en la Ubicación B son solo filas en la tabla de servicios con diferentes valores de location_id.

Services son tipos de clases con datos de horario. El metadata almacena el horario semanal como JSON, asignación de instructor, límites de capacidad, y si se permiten visitas sin suscripción.

Staff son entrenadores e instructores con certificaciones (NASM, ACE, CrossFit L2), especialidades, y disponibilidad para reservas de entrenamiento personal.

CTA es "Únete Ahora" — un checkout de suscripción a Stripe que maneja niveles de membresía y acceso entre ubicaciones. Un miembro que se registra en la ubicación del centro debe poder hacer check-in en la ubicación suburbana también.

Objetivos de SEO local: "gym near me", "fitness classes [city]", "[class type] classes [city]", "personal trainer [city]".

Metadata JSONB almacena: lista de equipos, horario de clases, horas pico, amenidades (sauna, piscina, cuidado de niños), disponibilidad de estacionamiento gratuito.

Variación de Industria 3: Grupos Hoteleros

Los grupos hoteleros boutique y las cadenas hoteleras independientes se benefician enormemente de este patrón — especialmente porque permite reservas directas que evitan las tarifas de comisión de OTA (típicamente 15-25% por reserva en Booking.com o Expedia).

Services se convierten en tipos de habitación: Habitación Estándar, Suite King, Penthouse. Cada una obtiene fotos, listas de amenidades, metraje cuadrado, y precios base. Los precios específicos de ubicación viven en el metadata o una tabla de tarifas separada con rangos de fechas.

Staff es más ligero aquí — quizás un gerente general destacado o conserje para la narrativa de la marca.

CTA es "Reservar Directamente" — el patrón FME (Find, Match, Engage) que da a los huéspedes una razón para reservar en el sitio del hotel en lugar de un OTA. Típicamente una "garantía de mejor tarifa" o mejora cortesía.

Objetivos de SEO local: "hotels in [city]", "[hotel name] reviews", "boutique hotel [neighborhood] [city]", "hotels near [landmark]".

Metadata JSONB almacena: amenidades (piscina, spa, restaurante, gimnasio, carga de EV), atracciones cercanas, calendario de eventos locales, horarios de check-in/check-out, política de mascotas.

Variación de Industria 4: Cadenas de Clínicas Veterinarias

Las cadenas veterinarias están creciendo rápidamente en 2025 — la consolidación en medicina veterinaria refleja lo que sucedió con las DSOs dentales hace una década. La misma arquitectura multi-ubicación se aplica perfectamente.

Services son servicios de cuidado de mascotas: exámenes de bienestar, vacunas, limpieza dental, cirugía, cuidado de emergencia, hospedaje, aseo. Algunas ubicaciones ofrecen cuidado de mascotas exóticas; la mayoría no.

Staff son veterinarios con experiencia en especies (pequeños animales, equinos, exóticos), certificaciones de junta educativa.

CTA es "Reservar una Cita" con un giro — el formulario de ingreso debe capturar información de la mascota (especie, raza, edad, motivo de la visita) para enrutar la cita correctamente.

Objetivos de SEO local: "veterinarian in [city]", "emergency vet [city]", "[species] vet [city]", "pet dental cleaning [city]".

Metadata JSONB almacena: especies aceptadas, horarios de emergencia (si son diferentes de horarios regulares), capacidad de hospedaje, si tienen laboratorio y diagnóstico por imágenes en el sitio.

Variación de Industria 5: Cadenas de Restaurantes

Services se convierten en secciones de menú: aperitivos, platos principales, postres, bebidas. La cosa crítica aquí es que los precios pueden variar por ubicación. Una hamburguesa cuesta $14 en Austin y $19 en Manhattan. La columna metadata maneja esto con sobrescrituras de precios específicas de ubicación.

Staff son chefs o pit masters destacados — esto funciona mejor para marcas donde las personas detrás de la comida son parte de la historia.

CTA es "Ordenar en Línea" — un enlace consciente de ubicación que enruta al sistema de pedidos en línea correcto (Toast, Square, ChowNow, o personalizado) para la ubicación más cercana del usuario.

Objetivos de SEO local: "[restaurant name] [city] menu", "restaurants near me", "[cuisine type] restaurant [city]", "[restaurant name] hours".

Metadata JSONB almacena: radio de entrega, disponibilidad de reservas (con enlace de OpenTable o Resy), detalles de estacionamiento, capacidad de comedor privado, horarios de happy hour.

Tabla de Comparación de Arquitectura

Componente DSO Dental Cadena de Gimnasios Grupo Hotelero Cadena Veterinaria Restaurante
Etiqueta de "Services" Procedimientos Clases Tipos de Habitación Servicios para Mascotas Artículos de Menú
Etiqueta de "Staff" Dentistas Entrenadores Administración Veterinarios Chefs
CTA Principal Reservar Cita Unirse a Membresía Reservar Habitación Reservar Cita Ordenar en Línea
Integración de Reservas NexHealth, Calendly Suscripciones Stripe Personalizada / Cloudbeds Personalizada + ingreso de mascota Toast, Square
Datos locales clave Seguros, estacionamiento Horario, equipos Amenidades, atracciones Especies, hrs de emergencia Precios de menú, entrega
Palabra clave SEO principal "dentist in [city]" "gym near me" "hotels in [city]" "vet in [city]" "[brand] [city] menu"
Marcado de esquema Dentist, LocalBusiness SportsActivityLocation Hotel, LodgingBusiness VeterinaryCare Restaurant, Menu
Tablas DB cambiadas 0 0 0 0 0

Esa última fila es el punto. Cero tablas de base de datos cambian entre industrias. Estás usando las mismas tablas locations, services, staff, blog_posts, testimonials, y events. Las etiquetas en la UI cambian. Las formas de metadata cambian. La arquitectura no.

Despliegue y Rendimiento a Escala

Desplegamos esto en Vercel con ISR (Regeneración Estática Incremental). Cada página de ubicación se genera estáticamente en tiempo de construcción y se revalida cada 60 segundos. Para una cadena de 200 ubicaciones, son 200 páginas HTML estáticas que cargan en menos de 1 segundo en cualquier dispositivo.

Los números importan. Aquí está lo que típicamente vemos:

  • Tiempo de construcción para 200 ubicaciones: ~45 segundos en Vercel Pro
  • TTFB por página de ubicación: < 50ms (servido desde CDN de borde)
  • Puntuaciones de Lighthouse: 95+ en toda la línea
  • Revalidación ISR: 60 segundos de stale-while-revalidate significa que las actualizaciones de contenido aparecen dentro de un minuto sin una reconstrucción completa

Agregar una nueva ubicación es una inserción de base de datos más una llamada opcional de revalidación bajo demanda. Sin nuevo despliegue necesario. La función generateStaticParams detecta nuevas ubicaciones en la siguiente construcción o ciclo ISR.

// Ruta API para activar revalidación cuando se agrega/actualiza una ubicación
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 })
}

Desglose de Costos: Lo que Realmente Cuesta en 2025

Hablemos de números reales. Esta es una pregunta común que recibimos durante conversaciones de precios.

Componente Costo Mensual (50 ubicaciones) Costo Mensual (200 ubicaciones)
Supabase Pro $25 $25 (el mismo nivel maneja ambos)
Vercel Pro $20 $20
Ancho de banda de Vercel (exceso) ~$0 ~$40
Dominio + DNS (Cloudflare) $0 $0
CDN de Imagen (Cloudflare R2) ~$5 ~$15
Monitoreo (Sentry) $26 $26
Infraestructura total ~$76/mes ~$126/mes

Compara eso con 50 sitios de WordPress separados a ~$30/mes cada uno para hospedaje administrado — son $1,500/mes antes incluso de pensar en mantenimiento, licencias de plugins, o la persona que tiene que mantenerlos todos actualizados.

La inversión de desarrollo es más alta por adelantado — típicamente cotizamos construcciones multi-ubicación en el rango de $30K-$80K dependiendo de la complejidad — pero el costo operativo continuo es una fracción de la alternativa multisitio de WordPress. Y no estás pagando $500/mes por ubicación a algún vendedor de sitios web de franquicia que te bloquea en su plataforma.

Para equipos interesados en explorar integraciones de CMS headless o considerar Astro en lugar de Next.js para construcciones estáticas aún más rápidas, la misma arquitectura de base de datos se aplica. El marco frontend es intercambiable; el modelo de datos no.

Preguntas Frecuentes

¿Puede esta arquitectura manejar ubicaciones en diferentes zonas horarias? Absolutamente. La columna hours JSONB almacena el horario de operación de cada ubicación en su zona horaria local. Incluimos un campo timezone (ej: "America/Chicago") en el metadata de ubicación y lo usamos para cualquier pantalla sensible al tiempo como insignias "Open Now". Todos los timestamps en la base de datos se almacenan como UTC y se convierten en el frontend.

¿Cómo manejas ubicaciones que ofrecen servicios diferentes? Ese es el patrón de location_id nullable en acción. Los servicios con location_id = NULL son compartidos en todas las ubicaciones — aparecen en la página de cada ubicación. Los servicios con un location_id específico solo aparecen para esa ubicación. También puedes usar una tabla de unión (location_services) para relaciones muchos-a-muchos si servicios compartidos necesitan sobrescrituras por ubicación como precios personalizados o disponibilidad.

¿Qué sucede cuando abre una nueva ubicación? Un administrador de red agrega la ubicación a través del panel. Esto crea una fila en la tabla locations, dispara un webhook que activa la revalidación ISR, y la nueva página de ubicación está en vivo dentro de 60 segundos. Sin desarrollador necesario, sin despliegue, sin cambios de DNS. La ubicación hereda todos los servicios y contenido compartido inmediatamente.

¿Esto es mejor que WordPress Multisite para franquicias? Para la mayoría de negocios multi-ubicación, sí. WordPress Multisite fue la respuesta preferida durante una década, pero tiene problemas reales: una única vulnerabilidad de plugin puede derribar toda la red, el rendimiento se degrada a medida que agregas sitios, y necesitas un sysadmin dedicado para mantenerlo saludable. Esta arquitectura headless te da rendimiento de sitio estático, seguridad a nivel de base de datos, y cero riesgo de runtime compartido entre ubicaciones.

¿Cómo editan los gerentes de ubicación su propio contenido sin romper otras ubicaciones? La Seguridad a Nivel de Fila a nivel de base de datos asegura que un gerente de ubicación en Austin literalmente no puede ver o modificar datos que pertenecen a la ubicación de Denver. No se aplica por código de aplicación que podría tener bugs — se aplica por el mismo Postgres. Incluso si la UI de administración tuviera un bug que intentara consultar datos de otra ubicación, la base de datos devolvería resultados vacíos.

¿Qué hay sobre SEO — obtiene cada ubicación su propio mapa del sitio? Cada página de ubicación obtiene su propia entrada en un mapa del sitio dinámico único generado en tiempo de construcción. También generamos datos estructurados por ubicación (JSON-LD) con esquema LocalBusiness, geo-coordenadas, horarios de operación, y tipos específicos de industria. Google trata cada página /locations/[slug] como un listado de negocio local distinto, que es exactamente lo que quieres para clasificaciones de paquete local.

¿Pueden las ubicaciones tener sus propios artículos de blog mientras comparten contenido de toda la red? Sí — ese es el patrón de location_id nullable nuevamente. Los artículos de blog con location_id = NULL aparecen en cada feed de blog de ubicación. Los artículos con un location_id específico aparecen solo en el feed de esa ubicación. Una ubicación en Miami puede publicar un artículo sobre un evento comunitario local mientras el equipo corporativo publica pensamiento de liderazgo de toda la red. Ambos aparecen en el feed de blog de Miami; solo el artículo corporativo aparece en todas partes.

¿Cuánto cuesta el mantenimiento continuo en comparación con administrar 50 sitios web separados? Con esta arquitectura, hay una base de código, un despliegue, y un conjunto de dependencias para mantener. La infraestructura mensual corre $75-$125 dependiendo de la escala. Compara eso con 50 instalaciones de WordPress: $1,500/mes solo en hospedaje, más 10-20 horas por mes en actualizaciones de plugins, parches de seguridad, y solucionar la ubicación que se rompió después de una actualización automática. Hemos visto negocios multi-ubicación cortar su presupuesto anual de operaciones web un 60-70% después de migrar a este patrón.