Arquitectura Multi-Tenancy
Diseña aplicaciones multi-tenant con bases de datos compartidas o aisladas, routing tenant-aware y estrategias de aislamiento de datos.
Visión General
La multi-tenancy es una arquitectura donde una única instancia de software sirve a múltiples clientes (tenants) manteniendo sus datos y configuración aislados. El compromiso es entre simplicidad operativa (todo compartido) y aislamiento de datos (todo separado). Elegir el modelo correcto afecta la escalabilidad, seguridad y cumplimiento.
Cuándo Usar
Usa este recurso cuando:
- Construyes aplicaciones SaaS que sirven a múltiples organizaciones
- Debes cumplir requisitos de compliance (SOC 2, HIPAA) que exigen segregación de datos. Consulta Checklist de Seguridad de APIs para saber lo que funciona en compliance.
- Optimizas costos de infraestructura compartiendo compute entre tenants
- Escalas de cientos a miles de tenants con rendimiento predecible
Solución
Base de Datos Compartida con Tenant ID (PostgreSQL)
-- Row-Level Security asegura aislamiento de tenant
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
user_id UUID NOT NULL,
amount DECIMAL(10,2) NOT NULL
);
-- Habilitar RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Política: tenants solo ven sus propios datos
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::UUID);
Middleware Tenant-Aware (Node.js)
function tenantMiddleware(req, res, next) {
const tenantId = req.headers['x-tenant-id'] || req.subdomain;
if (!tenantId) {
return res.status(400).json({ error: 'Tenant ID requerido' });
}
// Establecer contexto de tenant para esta request
req.tenantId = tenantId;
// Aplicar a conexión de base de datos
db.query("SET app.current_tenant = $1", [tenantId]);
next();
}
Migración Schema-por-Tenant
from sqlalchemy import create_engine, MetaData
def migrate_tenant_schema(tenant_id: str):
engine = create_engine("postgresql://user:pass@localhost/db")
with engine.begin() as conn:
conn.execute("CREATE SCHEMA IF NOT EXISTS tenant_{}".format(tenant_id))
# Ejecutar migraciones dentro del schema del tenant
metadata = MetaData(schema="tenant_{}".format(tenant_id))
metadata.create_all(conn)
Explicación
Tres modelos de multi-tenancy:
| Modelo | Aislamiento | Costo | Complejidad |
|---|---|---|---|
| BD Compartida + Tenant ID | Bajo (requiere RLS) | Más bajo | Baja |
| Schema-por-tenant | Medio | Medio | Media |
| Base de datos por tenant | Alto | Más alto | Alta |
Estrategias de resolución de tenant:
- Subdominio: tenant1.app.com, tenant2.app.com
- Path: app.com/tenant1/, app.com/tenant2/
- Header: X-Tenant-ID en requests de API
- JWT claim: tenant embebido en token de auth
Variantes
| Enfoque | Ideal Para | Compromiso |
|---|---|---|
| Todo compartido | SaaS inicial | Más simple; aislamiento más débil |
| Compute compartido, storage aislado | SaaS mid-market | Balance de costo y compliance |
| Totalmente aislado | Enterprise/regulado | Mayor costo; aislamiento más fuerte |
| Cell-based | Escala global | Shards de tenants entre regiones |
Lo que funciona
- Nunca confíes en tenant ID del input del usuario: Siempre resuélvelo desde el contexto autenticado
- Indexa tenant_id primero: Cada query filtra por tenant; hazlo la columna líder
- Usa connection pooling con cuidado: Schema-por-tenant requiere switching de schema en vivo
- Backup por tenant: Schema-por-tenant hace trivial pg_dump por schema
- Cuotas de recursos: Limita CPU, storage y rate de API por tenant para prevenir vecinos ruidosos
Errores Comunes
- Filtro de tenant faltante: Un WHERE tenant_id = $1 olvidado expone todos los datos del cliente
- Caching sin scope de tenant: Las cache keys compartidas filtran datos entre tenants
- Jobs en background sin contexto de tenant: Las tareas programadas deben ejecutarse para cada tenant por separado
- Schemas hard-coded: Mezclar datos de tenant en código de aplicación crea agujeros de seguridad
- Logging sin awareness de tenant: Depurar problemas en producción requiere filtrar logs por tenant
Errores Comunes Adicionales
-
Queries cross-tenant en analytics. Queries de reporting que agregan a través de tenants sin filtrar exponen datos. Usa warehouses de analytics separados por tenant o enforcea filtros de tenant_id en cada query de BI.
-
Secuencias compartidas y auto-increment. Usar una primary key
SERIALcompartida entre tenants en una base de datos compartida crea contención y filtra información de escala del tenant. Usa UUIDs:
-- Mal: secuencia compartida, contención
CREATE TABLE orders (id SERIAL PRIMARY KEY, tenant_id UUID, ...);
-- Bien: UUID, sin contención, sin fuga de información
CREATE TABLE orders (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID, ...);
- Ignorar el lifecycle del tenant en CI/CD. Los deployments que ejecutan migraciones de schema deben manejar todos los schemas de tenant. Una migración que funciona para un tenant puede fallar para otro con diferente volumen de datos. Testea migraciones contra el tenant más grande primero.
Preguntas frecuentes
¿Puedo migrar de BD compartida a schema-por-tenant más tarde?
Sí, pero requiere una migración mayor. Empieza con columnas tenant_id y RLS incluso si planeas dividir más tarde.
¿Cómo manejo customizaciones específicas por tenant?
Usa feature flags por tenant, configuración white-label, o UI metadata-driven. Evita branches de código separadas.
¿El GDPR afecta el diseño de multi-tenancy?
Sí. El derecho al olvido es más simple con schema-por-tenant (drop schema) que con tablas compartidas (borrar filas en muchas tablas).
Recursos Relacionados
Plantilla de Architecture Decision Record (ADR)
Una plantilla ligera para documentar decisiones arquitectónicas mayores, su contexto, opciones consideradas, y el razonamiento detras del enfoque elegido.
DocPlantilla de Documentación de Esquema de Base de Datos
Una plantilla para documentar esquemas de base de datos con relaciones entre entidades, definiciones de campos e historial de migraciones.
DocPlantilla de Engineering Handbook
Una plantilla para documentar la cultura del equipo, procesos de desarrollo, estandares tecnicos y practicas operacionales en un handbook unico y referenciable.
GuideGuía de Diseño de APIs REST
Una Referencia Detallada para diseñar APIs REST limpias, escalables y mantenibles.
GuideDomain-Driven Design (DDD): Guía Práctica
Aprende los fundamentos de DDD: bounded contexts, entidades, value objects, aggregates, y cómo modelar dominios de negocio complejos en código.
RecipeDiseñar un API Gateway Escalable para Microservicios
Construí un gateway de API que enrute requests, maneje autenticación, rate limiting, caching y traducción de protocolos entre clientes y microservicios backend.