La arquitectura de base de datos multi-tenant es un diseño para almacenar muchos clientes en una capa de datos, aislados por fila, esquema o base separada. Esos tres niveles de aislamiento son todo el espacio de decisión — el resto del tema es consecuencia: cómo corren las migraciones, cuánto cuesta el restore, dónde vive el noisy neighbor (vecino ruidoso) y hasta dónde escala cada patrón.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Los tres patrones | Schema compartido (tenant ID por fila) · schema por tenant · base por tenant |
| Vocabulario de nube | Los mismos tres: pool · bridge · silo |
| Elección por defecto | Schema compartido, a menos que compliance o escala fuercen el aislamiento |
| Techos prácticos | Silo: ~cientos de tenants · bridge: ~1.000 · pool: millones (con sharding: sin límite) |
| La regla de hierro | Aplica el aislamiento en la base, no en cada consulta |
Los tres patrones, en SQL
-- Patrón 1 · Schema compartido ("pool"): un conjunto de tablas, tenant en cada fila
CREATE TABLE invoices (
tenant_id uuid NOT NULL,
id bigint GENERATED ALWAYS AS IDENTITY,
total numeric(10,2),
PRIMARY KEY (tenant_id, id) -- el tenant lidera: listo para índice y shard
);
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_rows ON invoices
USING (tenant_id = current_setting('app.tenant')::uuid);
-- Patrón 2 · Schema por tenant ("bridge"): una base, un namespace para cada uno
CREATE SCHEMA tenant_acme; -- mismas tablas, repetidas por tenant
-- Patrón 3 · Base de datos por tenant ("silo"): separación física completa
CREATE DATABASE tenant_acme; -- aislamiento más fuerte; N de todo
En un backend gestionado la misma garantía se expresa sin SQL: el control de acceso vive en los datos mismos, así que el aislamiento vale en toda ruta — API, dashboard o SDK:
// JavaScript / Node.js — Back4app JS SDK
// The same query for every tenant — ACLs scope results server-side
const query = new Parse.Query('Invoice');
const invoices = await query.find({ sessionToken: user.getSessionToken() });
// Only rows this tenant's role can read come back. No WHERE clause to forget. // Flutter / Dart — Back4app Flutter SDK
// The same query for every tenant — ACLs scope results server-side
final query = QueryBuilder<ParseObject>(ParseObject('Invoice'));
final response = await query.query();
// The session's role decides which rows exist, before results leave the server
if (response.success) {
print('${response.results?.length} invoices visible to this tenant');
} // iOS / Swift — Back4app Swift SDK
// The same query for every tenant — ACLs scope results server-side
let query = Invoice.query()
query.find { result in
if case .success(let invoices) = result {
print("\(invoices.count) invoices visible to this tenant")
}
} // Android / Kotlin — Back4app Android SDK
// The same query for every tenant — ACLs scope results server-side
val query = ParseQuery.getQuery<ParseObject>("Invoice")
query.findInBackground { invoices, e ->
if (e == null) {
Log.d("Billing", "${invoices.size} invoices visible to this tenant")
}
} Schema compartido vs. schema por tenant vs. base por tenant
| Dimensión | Schema compartido | Schema por tenant | Base por tenant |
|---|---|---|---|
| Aislamiento | Lógico, por fila | Namespace | Físico |
| Techo de tenants | Millones | ~Cientos–1.000 | Decenas–pocos cientos |
| Costo por tenant | El más bajo | Intermedio | El más alto |
| Migraciones | Corren una vez, tocan a todos | Corren × N, orquestadas | Corren × N, orquestadas |
| Restore por tenant | Difícil (copia selectiva) | Moderado | Trivial (restaurar una base) |
| Noisy neighbor | El más expuesto | Contenido en parte | Eliminado |
| Personalización por tenant | La más difícil | Posible por schema | La más fácil |
| Onboarding de un tenant | Insertar una fila | Crear un schema | Aprovisionar una base |
Los detalles traicioneros
- La indexación es tenant-first.
tenant_idva en cada tabla — incluso donde los joins lo hacen parecer redundante — y lidera cada índice compuesto:(tenant_id, created_at), no al revés. Toda consulta real está acotada a un tenant; los índices también deberían estarlo. - Las migraciones se multiplican con el aislamiento. La flexibilidad del silo cuesta una capa de orquestación: seguimiento de versiones por tenant, lógica de reintentos, detección de drift. Los equipos subestiman esta línea más que ninguna otra de la tabla.
- La seguridad a nivel de fila tiene letra chica operativa. Las políticas dependen de ajustes por conexión, que interactúan con los modos de connection pooling — define el tenant por transacción y prueba el pooler. Audita también qué roles hacen bypass de RLS; las rutas de superusuario son el agujero clásico.
- Aritmética de conexiones. Un pool por base-de-tenant agota conexiones rápido; los diseños de schema compartido comparten un solo pool — otra ventaja silenciosa del patrón pool, que solo aparece a escala.
- La asimetría del restore guía el tiering. “¿Pueden restaurar solo lo nuestro a ayer?” es pregunta de contrato enterprise; si la respuesta debe ser sí, ese tenant pertenece a un silo — lo que lleva directo al híbrido.
Escalado y el estado final híbrido
El camino de crecimiento del patrón pool es el sharding por tenant: varias bases de schema compartido, cada una con una porción de los tenants, y un catálogo que mapea tenant → shard (el open-source Citus construyó esto dentro de PostgreSQL, incluido mover un tenant caliente a su propio nodo). Combinado con tiering, esto produce la arquitectura a la que converge la mayoría del SaaS maduro: el tier gratuito en pool, los tenants medianos en pool entre shards, y los pocos tenants regulados o enormes en silos — con tenant_id mantenido en todo esquema, en todas partes, para que cualquier tenant pueda moverse entre tiers sin una remodelación. La movilidad de tenants es la propiedad que hay que diseñar desde el primer día; retrofitearla es casi imposible.
Casos de uso comunes
- SaaS B2B. El caso que define el tema: cada workspace, organización o equipo de tu producto es un tenant en uno de estos patrones.
- Productos freemium a escala. Miles de pequeños tenants gratuitos en pool a costo marginal cercano a cero — la economía que hace posibles los tiers gratuitos.
- Verticales reguladas. Clientes de salud, finanzas y legal que exigen silos por contrato — servidos desde el mismo código base vía el híbrido.
- Agencias y plataformas. Una aplicación sirviendo a muchas organizaciones cliente, cada una con su propia frontera.
- Plataformas internas multi-equipo. Departamentos como tenants sobre herramientas compartidas — mismos patrones, modelo de amenaza más amigable.
¿Qué patrón deberías elegir? Matriz de decisión
| Elige schema compartido cuando… | Elige schema por tenant cuando… | Elige base por tenant cuando… |
|---|---|---|
| Los tenants son muchos y pequeños | Los tenants se cuentan por cientos | Los tenants son pocos y grandes |
| El costo por tenant debe acercarse a cero | Un aislamiento moderado vale algo de operación | Compliance exige separación física |
| Una migración debe actualizar a todos | Se necesitan ajustes de schema por tenant | El restore por tenant es contractual |
| Signup self-service, onboarding instantáneo | Los tenants entran a ritmo humano | Cada tenant justifica el aprovisionamiento |
| Harás sharding cuando crezcas | Limitarás la cantidad de tenants | Automatizarás migraciones × N |
Y la meta-respuesta: elige por tier, no por empresa — los diseños híbridos ponen a cada tenant en el patrón más barato que satisface sus requisitos, y los mueven cuando los requisitos cambian.
Limitaciones y trade-offs
- Schema compartido: la necesidad más fuerte de aislamiento aplicado por la base — un filtro perdido es una brecha, y por eso RLS o ACLs en la capa de datos son innegociables, no un hardening opcional.
- Schema por tenant: el punto medio incómodo — orquestación de migraciones como la del silo, aislamiento más débil que el del silo, y límites de metadatos de la base llegando sorprendentemente temprano.
- Base por tenant: todo × N — migraciones, backups, monitoreo, conexiones, costo — además de que la analítica entre tenants se vuelve un proyecto de ingeniería de datos.
- Los tres: el contexto de tenant lo invade todo (consultas, cachés, jobs, logs), y los movimientos entre patrones salen caros sin disciplina de IDs desde el primer día: identificadores globalmente únicos y columnas
tenant_iden todas partes, hasta en los silos.
Datos multi-tenant en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. Su respuesta a la regla de hierro de este artículo — aplica el aislamiento en la base — son ACLs por objeto, roles por tenant y permisos a nivel de clase, verificados por la plataforma en cada solicitud desde toda superficie, como muestran las pestañas de código de arriba. Es el patrón de schema compartido con el riesgo de la cláusula WHERE removido; el análisis de ingeniería sobre seguridad a nivel de fila recorre el patrón completo sobre una base de documentos, y la vista a nivel de hosting del mismo tema cubre la capa de infraestructura por encima.
Preguntas frecuentes
¿Cuáles son los tres patrones de base de datos multi-tenant?
Schema compartido — un conjunto de tablas, cada fila cargando un identificador de tenant; schema por tenant — una base, un namespace de tablas separado por cliente; y base de datos por tenant — separación física completa. Las guías de arquitectura en la nube llaman a los mismos tres pool, bridge y silo. Los sistemas reales cada vez más los mezclan, distribuyendo tenants entre patrones según tamaño y necesidades de compliance.
¿Base compartida o base por tenant — cuál elegir?
El default de consenso es schema compartido, a menos que algo fuerce el aislamiento: requisitos regulatorios, separación contractual de datos, personalización pesada por tenant o tenants tan grandes que necesitan recursos propios. El schema compartido maximiza la densidad y minimiza la operación; las bases por tenant maximizan el aislamiento y multiplican todo lo demás — migraciones, backups, conexiones, costo.
¿Cuántos tenants aguanta cada patrón?
Los techos prácticos de la experiencia en producción: base por tenant corre cómoda hasta decenas o pocos cientos de tenants antes de que la operación domine; schema por tenant llega a los cientos, hasta cerca de mil, antes de que los metadatos y la orquestación de migraciones se tensen; el schema compartido escala a miles o millones de tenants, y el sharding por tenant lo extiende efectivamente sin límite.
¿Cómo impedir que un tenant vea los datos de otro?
Defensa en profundidad, nunca una cláusula WHERE sola. El alcance de tenant en la aplicación (middleware o filtros del ORM) es la primera capa; la seguridad a nivel de fila aplicada por la base es la segunda — políticas que filtran cada consulta por el tenant actual, sin importar lo que la aplicación olvidó. En diseños de schema compartido, un solo filtro perdido es una brecha entre tenants — por eso la propia base debe aplicar la frontera.
¿Cómo funcionan las migraciones de esquema en cada patrón?
En el schema compartido, una migración actualiza a todos los tenants a la vez — simple, con un radio de daño a la altura. En schema o base por tenant, la misma migración debe correr una vez por tenant: cientos de ejecuciones que piden orquestación, seguimiento de versiones y detección de drift, ya que una ejecución fallida deja a un tenant en un esquema más viejo. El tooling de migraciones es el impuesto oculto del aislamiento.
¿Se pueden restaurar los datos de un solo tenant?
En base por tenant, trivialmente — restaura esa base a cualquier punto en el tiempo sin tocar a nadie más. En schema compartido es genuinamente difícil: el backup contiene a todos, así que restauras en una instancia paralela y copias selectivamente las filas del tenant de vuelta. El restore por tenant es uno de los argumentos prácticos más fuertes que las empresas hacen por tiers de aislamiento.
¿Cómo indexar una base multi-tenant de schema compartido?
Pon el identificador de tenant en cada tabla — incluso donde parece redundante — y lidera tus índices compuestos con él, para que cada patrón de consulta se vuelva tenant-first. Hacer de la columna de tenant la parte inicial de la clave primaria también deja los datos preordenados para el sharding por tenant más adelante, que es el camino de crecimiento estándar.
¿Qué es el sharding por tenant?
Dividir un diseño de schema compartido entre varias bases, con todas las filas de un tenant viviendo en exactamente un shard y un catálogo que mapea tenants a shards. Preserva la densidad del schema compartido mientras acota el tamaño de cualquier base individual, y permite mover tenants calientes a shards más tranquilos. El precio es el catálogo, el tooling de rebalanceo y la pérdida de las consultas triviales entre tenants.