Arquitectura SaaS multi-tenant como torre holográfica cian con núcleos de datos ámbar aislados para cada cliente
Software / APIsDesarrollo Web

Arquitectura SaaS Multi-Tenant: Modelos de Aislamiento

Codezone
Codezone Empresa de Desarrollo Web y Software a Medida

Una arquitectura SaaS multi-tenant sirve a muchos clientes desde una sola aplicación, y su decisión de fondo es dónde separas los datos de cada uno: base de datos compartida con `tenant_id`, esquema por cliente o base de datos por cliente. Se elige por el aislamiento que exigen tus clientes y por el coste que aguanta tu margen. Lo diseñamos en nuestro desarrollo de SaaS a medida en Madrid.

Equivocarse de modelo se paga dos veces. Si una consulta mal filtrada enseña las facturas de una empresa a otra, tienes un incidente con datos de terceros y un contrato en riesgo. Si eliges bases separadas sin automatizar despliegues, cada cambio de esquema se multiplica por el número de clientes.

Al terminar sabrás qué modelo de aislamiento encaja con tu producto; las etapas del proyecto completo están en la guía de fases para lanzar un producto SaaS.

Qué es una arquitectura SaaS multi-tenant

Una arquitectura SaaS multi-tenant es un diseño en el que una única aplicación da servicio a varios clientes (tenants), cada uno con sus propios datos y usuarios, compartiendo parte o toda la infraestructura. El tenant es la unidad de aislamiento: ningún usuario puede leer ni modificar datos de un tenant al que no pertenece.

Edificio holográfico cian dividido en compartimentos de cristal iluminados, cada uno con su propio núcleo de datos ámbar
Cada cliente vive en su compartimento, aunque el edificio sea el mismo

Tres modelos de aislamiento y cuándo elegir cada uno

Los modelos de aislamiento se diferencian en cuánta capa de datos comparte cada cliente con los demás.

  • Base compartida con tenant_id: Aislamiento: lógico, por filtro en cada consulta. Coste por cliente: el más bajo. Cuándo: muchos clientes pequeños con el mismo producto.
  • Esquema por cliente: Aislamiento: tablas propias en un esquema de la misma base, aunque en PostgreSQL un rol con permisos llega a todos. Cuándo: decenas de clientes, no miles, porque cada migración se repite por esquema.
  • Base de datos por cliente: Aislamiento: físico, con copias independientes. Coste: el más alto por cliente. Cuándo: contratos que exigen aislamiento físico, con un precio que lo cubra.
  • Modelo mixto: plan estándar compartido y plan enterprise dedicado, con el código preparado para ambos.
  • Ninguno: si el software solo lo usa tu empresa, no necesitas multi-tenant; compara antes software propio frente a suscripciones SaaS.

El aislamiento tampoco obliga a trocear el código: un monolito modular separado por dominios puede ser multi-tenant sin microservicios.

Fugas entre clientes: cómo se evitan en la base compartida

Una fuga entre clientes ocurre cuando una consulta devuelve filas de otro tenant, y en el modelo compartido basta con olvidar un filtro.

  • Tenant resuelto en el servidor: sale de la sesión autenticada, nunca de un parámetro del navegador.
  • Row Level Security como segunda barrera: la documentación de Row Level Security en PostgreSQL indica que, activada sin políticas, deniega todo, y que el propietario de la tabla la salta salvo con FORCE ROW LEVEL SECURITY, igual que los roles con BYPASSRLS.
  • Tests de aislamiento: cada despliegue crea dos tenants y comprueba que uno no ve los datos del otro.

Las demás defensas de la capa web están en los controles OWASP que protegen a cada tenant. Si construyes sobre Supabase, sus permisos son políticas RLS de PostgreSQL; lo comparamos en Supabase frente a un backend Node para tu SaaS.

Escudo holográfico cian que bloquea un flujo de partículas ámbar entre dos cámaras de datos sobre un suelo reflectante
La base de datos frena la fuga aunque la aplicación olvide el filtro

Personalización, vecino ruidoso y facturación por uso

Un SaaS multi-tenant personaliza por configuración, con la misma versión de código para todos los clientes.

  • Personalización: campos extra en una columna JSONB y funcionalidades activadas por tenant. La guía de Microsoft sobre datos en soluciones multitenant desaconseja cambios de esquema para un solo cliente. Un cliente corporativo puede exigir además SSO corporativo con Microsoft Entra ID por cliente.
  • Vecino ruidoso: un cliente con informes pesados degrada al resto. Se contiene con cuotas por tenant, colas aparte para trabajos largos o una base dedicada.
  • Facturación por uso: mide eventos de negocio por tenant, como documentos creados, en un almacén transaccional y no en logs que descartan muestras.
  • Migraciones: compatibles con la versión anterior y aplicadas por pipeline, registrando la versión de esquema de cada tenant como pide esa guía. Para el motor, compara PostgreSQL, MySQL o MongoDB para el núcleo del SaaS.
Llave holográfica ámbar que encaja en una cuadrícula de filas de cristal cian, iluminando solo una franja
Cada petición fija su tenant y la base de datos filtra el resto
CodeZone Pro Tip: esta política de Row Level Security aísla por tenant una tabla compartida en PostgreSQL y falla en cerrado si la aplicación no indica el cliente.
-- Tabla compartida: cada fila lleva el tenant al que pertenece
CREATE TABLE facturas (
  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  tenant_id  uuid NOT NULL,
  cliente    text NOT NULL,
  importe    numeric(12,2) NOT NULL CHECK (importe >= 0),
  emitida_en timestamptz NOT NULL DEFAULT now()
);
-- El tenant va primero en el índice: todas las consultas filtran por él
CREATE INDEX facturas_tenant_idx ON facturas (tenant_id, emitida_en);

ALTER TABLE facturas ENABLE ROW LEVEL SECURITY;
-- FORCE: el propietario de la tabla también queda sujeto a la política
ALTER TABLE facturas FORCE ROW LEVEL SECURITY;

-- NULLIF + missing_ok: si la app no fija el tenant, la condición da NULL
-- y la consulta devuelve cero filas en lugar de todas
CREATE POLICY aislamiento_por_tenant ON facturas
  USING (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid)
  WITH CHECK (tenant_id = NULLIF(current_setting('app.tenant_id', true), '')::uuid);

-- La aplicación se conecta con un rol sin BYPASSRLS que no es propietario
CREATE ROLE app_saas LOGIN NOBYPASSRLS;
GRANT SELECT, INSERT, UPDATE, DELETE ON facturas TO app_saas;

-- En cada petición: fijar el tenant solo para esta transacción (true = local)
BEGIN;
SELECT set_config('app.tenant_id', '7c9e6679-7425-40de-944b-e07fc1f90ae7', true);
INSERT INTO facturas (tenant_id, cliente, importe)
VALUES ('7c9e6679-7425-40de-944b-e07fc1f90ae7', 'Cliente A', 1200.00);
SELECT count(*) FROM facturas;
COMMIT;

Con esta política, un fallo en el código deja de ser una fuga: la consulta sin tenant devuelve cero filas y la base rechaza un INSERT con el tenant de otro cliente. El `NULLIF` cubre un caso habitual: tras una transacción, el ajuste queda como texto vacío y la conversión a uuid fallaría. Para tu negocio, es una respuesta comprobable cuando un cliente pregunta cómo proteges sus datos.

El Coste de Cambiar de Modelo con Clientes Dentro

Pasar de base compartida a bases dedicadas con cientos de clientes activos es una migración completa, con validación cliente a cliente. Restaurar los datos de un solo cliente en una base compartida también exige haberlo previsto en tu plan de copias y restauración por tenant. Decidir el modelo antes de la primera migración es una conversación de diseño; rehacerlo después es un proyecto con presupuesto propio.

Bloques holográficos cian que se separan de un núcleo común hacia cápsulas ámbar individuales entre esquirlas de vidrio
Separar clientes con el producto en marcha es un proyecto en sí mismo

Si estás diseñando un SaaS o ya tienes clientes dentro, revisamos contigo el modelo de datos de tu SaaS antes de que crezca la base.