Arquitectura
Arquitectura SaaS multiinquilino: lo que los CTO necesitan saber
Utilice tablas compartidas con seguridad a nivel de fila (RLS) para más de 5000 inquilinos o productos en etapa inicial; Cuesta entre $15 y $200 al mes en total. Utilice la base de datos por inquilino para industrias reguladas (fintech, atención médica) con menos de 100 inquilinos. Utilice la separación de esquemas para entre 50 y 5000 inquilinos que necesiten un aislamiento moderado. Esta decisión da forma a su estructura de costos, historia de cumplimiento y proceso de implementación durante años.
Estás creando un producto SaaS. Varios clientes lo utilizarán. Cada cliente espera que sus datos se mantengan privados, que sus configuraciones permanezcan separadas y que su experiencia se sienta como su propia plataforma. La pregunta no es si necesita multiinquilino. La cuestión es qué modelo elegir.
Esta decisión da forma al esquema de su base de datos, su proceso de implementación, su historial de cumplimiento y su estructura de costos durante años. Si lo hace mal, pasará seis meses migrando a un modelo diferente mientras su hoja de ruta se estanca.
Hay tres modelos que vale la pena considerar. Cada uno compensa el costo, el aislamiento y la complejidad operativa de diferentes maneras.
Los tres modelos de arquitectura multiinquilino
1. Base de datos por inquilino
Cada inquilino obtiene su propia base de datos. La capa de aplicación enruta las consultas a la base de datos correcta en función de un identificador de inquilino, que generalmente se resuelve desde el subdominio, la clave API o el reclamo JWT.
Este es el modelo de aislamiento más fuerte. Los datos del inquilino A se encuentran en una base de datos separada de los datos del inquilino B. No hay forma de que un error de consulta filtre filas entre inquilinos, porque las filas existen en diferentes bases de datos en diferentes conexiones.
Ventajas:
- Respetuoso con el cumplimiento. A los auditores les encanta escuchar "cada cliente tiene su propia base de datos". Los requisitos de SOC 2, HIPAA y residencia de datos se convierten en conversaciones sencillas.
- Copia de seguridad y restauración por inquilino. Cuando un inquilino le pide que regrese al estado de ayer, usted restaura una base de datos. Sin extracción quirúrgica de mesas compartidas.
- Sin riesgo de vecinos ruidosos. Un inquilino que ejecuta consultas analíticas costosas no puede degradar el rendimiento de otros inquilinos.
- Escalado por inquilino. Puede colocar inquilinos de alto valor en instancias de bases de datos más grandes.
Desventajas:
- Caro. Cada base de datos tiene un costo base: computación, almacenamiento, copias de seguridad, monitoreo. Con 10 inquilinos, esto es manejable. A 500, es una partida que incomoda a su director financiero.
- El infierno de la migración. Los cambios de esquema deben ejecutarse en cientos de bases de datos. Necesita herramientas para organizar las migraciones, realizar un seguimiento de qué bases de datos están actualizadas y manejar las fallas a mitad de una implementación.
- La agrupación de conexiones se vuelve complicada. Su servidor de aplicaciones necesita grupos de conexiones para cientos de bases de datos. Los límites de conexión se convierten en una restricción antes que la CPU o la memoria.
- Las consultas entre inquilinos son dolorosas. Los informes agregados, los análisis de toda la plataforma o los paneles de administración que muestran datos entre inquilinos requieren consultas de federación o un canal de análisis independiente.
La mejor para:Fintech, atención médica y SaaS empresarial con estrictos requisitos de residencia de datos. Si su contrato dice "los datos del cliente deben residir en una región específica de AWS" o "los datos deben poder eliminarse dentro de las 24 horas posteriores a la terminación del contrato", la base de datos por inquilino hace que ambas cosas sean trivialmente alcanzables.
2. Base de datos compartida, separación de esquemas.
Una base de datos, pero cada inquilino obtiene su propio esquema (espacio de nombres). En PostgreSQL, esto significa que cada inquilino tiene un conjunto separado de tablas bajo su propio nombre de esquema: tenant_abc.users, tenant_xyz.users. La aplicación establece search_path en cada conexión para enrutar consultas al esquema correcto.
Este es el término medio. Obtiene un mejor aislamiento que las tablas compartidas, a un costo menor que las bases de datos separadas.
Ventajas:
- Más barato que la base de datos por inquilino. Una instancia de base de datos, un grupo de conexiones, una pila de monitoreo.
- Aislamiento decente. Los esquemas proporcionan un límite de espacio de nombres. Una consulta mal configurada en un esquema no puede acceder a las tablas de otro esquema (suponiendo que haya configurado
search_pathcorrectamente). - La copia de seguridad por inquilino es posible a través de
pg_dump --schema. - Las migraciones son más fáciles que las de base de datos por inquilino. La migración se ejecuta una vez por esquema, pero todos los esquemas residen en la misma base de datos, por lo que las herramientas son más sencillas.
Desventajas:
- Deriva del esquema. Cuando las migraciones fallan en algunos esquemas pero tienen éxito en otros, terminas con inquilinos ejecutando diferentes versiones de tu esquema. Depurar esto es miserable.
- PostgreSQL no maneja bien miles de esquemas. Más allá de 5000 a 10 000 esquemas, experimentará una degradación del rendimiento en
pg_catalogbúsquedas, tiempos más lentospg_dumpy contención de vacío automático. - El mismo riesgo de vecino ruidoso que una base de datos compartida. La costosa consulta de un inquilino todavía compite por la misma CPU y E/S.
- El soporte de herramientas es inconsistente. Los ORM y los marcos de migración tienen distintos niveles de soporte para patrones de esquema por inquilino.
La mejor para:SaaS de mercado medio con entre 50 y 5000 inquilinos donde necesita un mejor aislamiento que la seguridad a nivel de fila, pero no puede justificar el costo de bases de datos separadas.
3. Compartió todo con seguridad a nivel de fila.
Todos los inquilinos comparten las mismas mesas. Una columna tenant_id en cada tabla identifica qué inquilino posee cada fila. Las políticas de seguridad a nivel de fila (RLS) en PostgreSQL exigen que las consultas solo puedan ver filas que pertenecen al inquilino actual.
Este es el modelo más común para productos B2B SaaS que no se venden a empresas reguladas.
Ventajas:
- Costo de infraestructura más económico. Una base de datos, un conjunto de tablas, un grupo de conexiones. Agregar un inquilino no cuesta infraestructura adicional.
- Migraciones más simples. Un esquema, una ejecución de migración. No estás orquestando nada en múltiples bases de datos o esquemas.
- Una ruta de código base. No hay lógica condicional para "con qué base de datos estoy hablando" o "qué esquema debo usar". La columna
tenant_ides parte del esquema y RLS se encarga del resto. - Los análisis entre inquilinos son triviales. Los paneles de administración y los informes de toda la plataforma consultan las mismas tablas con permisos elevados.
Desventajas:
- Los errores de consulta pueden filtrar datos. Si un desarrollador escribe una consulta que omite RLS (utilizando una conexión de superusuario u olvidando configurar la variable de sesión), se filtran datos del inquilino. Este es un riesgo a nivel de código, no una garantía a nivel de infraestructura.
- Las conversaciones sobre cumplimiento son más difíciles. "Todos los datos de los clientes se encuentran en las mismas tablas, separados por una columna" es más difícil de vender a los equipos de seguridad empresariales que "cada cliente tiene su propia base de datos".
- El riesgo de vecinos ruidosos es mayor aquí. Un inquilino que importa 10 millones de filas bloquea tablas que afectan a todos los inquilinos.
- La copia de seguridad y restauración por inquilino requiere extracción quirúrgica. No se puede restaurar un solo inquilino sin escribir herramientas personalizadas.
La mejor para:B2B SaaS por debajo del nivel empresarial. Productos con cientos o miles de inquilinos donde el costo de la infraestructura importa más que las certificaciones de cumplimiento.
tabla comparativa
| Factora | Base de datos por inquilino | Separación de esquemas | Compartido + RLS |
|---|---|---|---|
| Costo por inquilina | Alto (cómputo dedicado + almacenamiento) | Medio (cómputo compartido, esquemas separados) | Bajo (una mesa, una fila) |
| Aislamiento de datos | Más fuerte (bases de datos separadas) | Moderada (límites de esquema) | Más débil (columna + política) |
| Complejidad de la consulta | Bajo por inquilino, alto entre inquilinos | Bajo por inquilino, moderado entre inquilinos | Bajo (mismas tablas, RLS lo maneja) |
| Dificultad de migración | Difícil (N bases de datos para migrar) | Moderado (N esquemas, una base de datos) | Fácil (un esquema, una migración) |
| Preparación para el cumplimiento | Excelente (a las auditoras les encanta) | Buena (límite defendible) | Adecuado (requiere seguimiento de auditoría RLS) |
| Riesgo de vecino ruidoso | Ninguna | Presente | más alto |
| Costo de incorporación del inquilino | Aprovisionamiento requerido | Creación de esquemas + migración | Insertar una fila |
Cómo construimos DropTaxi en base de datos compartida multiinquilino
cuando construimosDropTaxi, un SaaS de reserva de taxis multiinquilino para operadores indios, elegimos el modelo de base de datos compartida con tenant_id columnas.
El razonamiento fue sencillo. Los operadores de taxis son pequeñas empresas. No tienen requisitos de cumplimiento que exijan aislamiento a nivel de base de datos. El número de inquilinos debía aumentar de 5 a más de 500 sin costos de infraestructura por inquilino. Y la incorporación tenía que ser instantánea: registrarse, configurar la marca, asignar un dominio y comenzar a funcionar. Sin implementación, sin aprovisionamiento, sin esperas.
Así es como se ve la arquitectura en la práctica:
Incorporación de inquilinos sin implementación.Agregar un nuevo inquilino significa insertar una fila en la tabla tenants con su marca, colores, URL del logotipo, dominio, tarifas y token del bot de Telegram. No se ejecuta ninguna canalización de CI. Ningún contenedor se reinicia. La siguiente solicitud a ese dominio resuelve el nuevo inquilino y presenta su sitio de marca.
Subdominios de marca a través de middleware.Cada solicitud HTTP entrante llega a una capa de middleware de Hono que lee el encabezado Host. El middleware consulta la base de datos (a través de Drizzle ORM en Turso) para encontrar el inquilino que coincida con ese dominio. Si encuentra una coincidencia, la configuración completa del inquilino se carga en el contexto de la solicitud. Si no es así, la solicitud recibe un 404. La capa Astro SSR luego representa páginas usando la marca del inquilino, por lo que los visitantes ven un sitio de reserva de taxis independiente en lugar de una plataforma genérica.
Despliegue único para todas las inquilinas.Una máquina Fly.io ejecuta toda la plataforma. Una base de datos. Una base de código. El único costo por inquilino es la configuración de DNS y las filas de la base de datos que almacenan su configuración.
Este modelo funciona porque el producto no sirve a industrias reguladas, la sensibilidad de los datos es baja (detalles de la reserva, no registros financieros) y la principal preocupación de escala es el recuento de inquilinos en lugar del volumen de datos por inquilino.
Patrones prácticos para sistemas multiinquilino
Independientemente del modelo que elija, estos patrones aparecen en los sistemas multiinquilino de producción.
Middleware de resolución de inquilinos
La resolución del inquilino debe ocurrir una vez, en el límite de su proceso de solicitud, y el inquilino resuelto debe propagarse a lo largo de todo el ciclo de vida de la solicitud. Estrategias de resolución comunes:
- Subdominio:
acme.yourapp.comse resuelve en el inquilinoacme. Analizar desde el encabezadoHost. - Dominio personalizado:
app.acme.comse asigna a un inquilino a través de una tabla de búsqueda de dominios. - Prefijo de ruta:
yourapp.com/acme/dashboardextrae el inquilino de la URL. Menos común en producción, pero útil durante el desarrollo. - Clave JWT/API:Para los productos API-first, el identificador de inquilino se encuentra en el token de autenticación. El middleware valida el token y extrae el reclamo del inquilino.
Almacene el inquilino resuelto en un contexto con alcance de solicitud (c.set() de Hono, req.tenant de Express o una variable local de subproceso en Go). No debería ser necesario ningún código posterior para volver a resolver el inquilino.
Agrupación de conexiones
En el modelo de base de datos por inquilino, la agrupación de conexiones se convierte en el primer cuello de botella que encontrará. Cada base de datos de inquilinos necesita su propio grupo y su servidor de aplicaciones tiene un número finito de conexiones que puede mantener abiertas.
Soluciones que funcionan en producción:
- PgBouncer por instancia de base de datoscon agrupación a nivel de transacción. Esto multiplexa muchas conexiones de aplicaciones a través de un número menor de conexiones de bases de datos.
- Inicialización diferida del grupo.No cree grupos de conexiones para inquilinos que no hayan recibido una solicitud en la última hora. Haga girar grupos a pedido y desaloje grupos inactivos con un TTL.
- Servicios de pool gestionadoscomo el agrupador de conexiones de Supabase o el controlador sin servidor de Neon, que manejan la administración del grupo fuera de su proceso de aplicación.
Para los modelos de bases de datos compartidas, un único grupo de conexiones funciona bien. El contexto del inquilino se establece en el nivel de sesión (SET app.current_tenant para RLS, SET search_path para separación de esquemas) en cada pago de conexión.
Almacenamiento en caché con ámbito de inquilino
Sus claves de caché necesitan un prefijo de inquilino. Si almacena en caché una clave de "datos del panel" sin limitarla a un inquilino, entregará los datos del panel del inquilino A al inquilino B. Esto suena obvio, pero es el error de inquilinos múltiples más común en producción.
Utilice un formato de clave como tenant:{tenant_id}:resource:{resource_type}:{resource_id}. Si usa Redis, considere bases de datos de Redis separadas por inquilino (0-15 están disponibles de forma predeterminada) para implementaciones más pequeñas o prefijos de clave para implementaciones más grandes.
Aislamiento del trabajo en segundo plano
Los trabajos en segundo plano (envíos de correo electrónico, generación de informes, importaciones de datos) necesitan que el contexto del inquilino se propague desde el sitio en cola al trabajador. Cuando ponga en cola un trabajo, incluya tenant_id en la carga útil del trabajo. El trabajador debe configurar el mismo contexto de inquilino que proporciona su middleware HTTP antes de procesar el trabajo.
Para proteger a los vecinos ruidosos en las colas de trabajos, utilice colas separadas o prioridades de cola por inquilino. Un inquilino que importa 100 000 registros no debería bloquear los correos electrónicos de bienvenida de otro inquilino. BullMQ, Sidekiq y Celery admiten colas con nombre que le permiten enrutar inquilinos de gran volumen a trabajadores dedicados.
Multiportal como variante multiinquilino
La tenencia múltiple no solo significa "la misma aplicación, diferentes clientes". También puede significar "misma plataforma, diferentes roles de usuario con portales separados". cuando construimosZestAMC, una plataforma multiportal basada en roles, la arquitectura compartía la misma base de datos y código base, pero exponía diferentes interfaces a diferentes tipos de usuarios: administradores, gerentes y agentes de campo. Cada portal tenía su propio enrutamiento, permisos e interfaz de usuario, pero todos se basaban en la misma capa de datos con alcance a nivel de fila.
Este es un patrón útil cuando sus "inquilinos" no son organizaciones separadas sino roles separados dentro de una organización. Las primitivas de arrendamiento múltiple (acceso a datos con alcance, middleware por rol, propagación de contexto) siguen siendo las mismas.
Marco de decisión: cómo elegir su modelo
El modelo correcto depende de tres variables: requisitos de cumplimiento, número esperado de inquilinos y presupuesto de infraestructura.
Comience con el cumplimiento.Si sus clientes pertenecen a industrias reguladas (finanzas, atención médica, gobierno) o si sus contratos incluyen cláusulas de residencia de datos, la base de datos por inquilino es la opción más segura. La prima de costo es una partida de los contratos empresariales que sus clientes esperan pagar.
Tenga en cuenta el recuento de inquilinos.Si espera tener menos de 100 inquilinos y cada inquilino genera ingresos significativos, la base de datos por inquilino es viable. Entre 100 y 5000, la separación de esquemas funciona si estás en PostgreSQL y puedes invertir en herramientas de migración. Por encima de 5000, las mesas compartidas con RLS son la opción pragmática. La economía de infraestructura de los otros modelos se desmorona cuando el número de inquilinos es elevado.
Consulta tu presupuesto.Si se encuentra en etapa previa a los ingresos o en la etapa inicial, las tablas compartidas con RLS le permiten realizar envíos más rápido y gastar menos. Puede migrar a un modelo de aislamiento más sólido más adelante, cuando los clientes empresariales lo soliciten (y paguen por él). La mayoría de los productos SaaS nunca necesitan realizar esa migración, porque la mayoría de los productos SaaS se venden a PYMES que se preocupan por las funciones, no por los modelos de aislamiento de bases de datos.
Considere el enfoque híbrido.Algunos productos ejecutan tablas compartidas para su nivel estándar y base de datos por inquilino para clientes empresariales. Esto supone una mayor complejidad operativa, pero le permite atender a ambos mercados sin forzar un solo modelo. Tanto Stripe como Notion utilizan variaciones de este patrón.
Errores comunes a evitar
- Elegir una base de datos por inquilino para un producto que tendrá miles de inquilinos.El costo operativo de administrar miles de bases de datos supera los beneficios del aislamiento. Si sus inquilinos son PYMES que pagan $50 al mes, no puede permitirse una infraestructura dedicada por inquilino.
- Olvidar el alcance de su conjunto de pruebas.Sus pruebas de integración deben ejecutarse con el contexto del inquilino. Si sus pruebas pasan sin configurar un inquilino, están probando una ruta de código que sus usuarios de producción no alcanzarán.
- No probar políticas RLS con consultas contradictorias.Escriba pruebas que intenten acceder a los datos del inquilino B mientras está autenticado como inquilino A. Ejecútelas en CI. Un conjunto de pruebas aprobado sin pruebas de acceso entre inquilinos genera una confianza falsa.
- La gestión de inquilinos del edificio como una ocurrencia de último momento.El aprovisionamiento, la configuración y el desaprovisionamiento de inquilinos son características del producto de primera clase. Cree las herramientas de administración junto con el producto, no después del lanzamiento.
- Omitir el registro consciente de los inquilinos.Cada línea de registro debe incluir el identificador del inquilino. Cuando estás depurando un problema de producción a las 2 a.m., "algo se rompió" es inútil. "El inquilino acme_corp alcanzó una restricción de clave externa en la tabla de pedidos" es procesable.
La versión corta
Elija base de datos por inquilino si el cumplimiento impulsa su arquitectura. Elija la separación de esquemas si necesita un aislamiento moderado a una escala moderada. Elija tablas compartidas con RLS si está optimizando la velocidad y el costo. Cree middleware de resolución de inquilinos desde el primer día. Alcance sus cachés, sus registros, sus trabajos en segundo plano y sus pruebas según el contexto del inquilino. Y no diseñe demasiado el modelo de aislamiento para su etapa actual; puede reforzar el aislamiento cuando sus clientes lo exijan y sus ingresos lo respalden.
Preguntas frecuentes
¿Cuál es la mejor arquitectura de base de datos multiinquilino para SaaS?
Depende de tres factores. Utilice la base de datos por inquilino para industrias reguladas (fintech, atención médica) con menos de 100 inquilinos. Utilice la separación de esquemas para entre 50 y 5000 inquilinos que necesiten un aislamiento moderado. Utilice tablas compartidas con seguridad a nivel de fila para más de 5000 inquilinos o productos en etapa inicial que optimicen la velocidad y el costo.
¿Qué es la seguridad a nivel de fila en SaaS multiinquilino?
La seguridad a nivel de fila (RLS) utiliza políticas de PostgreSQL para restringir las consultas de cada inquilino a sus propias filas. Una columna inquilino_id en cada tabla identifica la propiedad. RLS es el modelo más barato (una base de datos, cero costo de infraestructura por inquilino) y maneja más de 5000 inquilinos. El riesgo: una consulta mal configurada que pasa por alto RLS puede filtrar datos entre inquilinos.
¿Cuánto cuesta la base de datos por inquilino frente a la base de datos compartida?
La base de datos por inquilino agrega entre $15 y $100+ por inquilino por mes en computación, almacenamiento y copias de seguridad. Con 500 inquilinos, eso equivale a entre $ 7500 y $ 50 000 al mes solo en costos de base de datos. Las tablas compartidas con RLS ejecutan una base de datos por un total de entre $15 y $200 al mes. La separación de esquemas se encuentra en el medio en una instancia de base de datos, pero con una sobrecarga de migración por esquema.
¿Cómo se maneja la resolución de inquilinos en aplicaciones multiinquilino?
Resuelva el inquilino una vez que se encuentre en el borde de su canal de solicitudes mediante el análisis de subdominios, la búsqueda de dominio personalizada, el prefijo de ruta o las notificaciones JWT. Almacene el inquilino resuelto en el contexto del ámbito de la solicitud (Hono c.set(), Express req.tenant). Ningún código posterior debería volver a resolver el inquilino. Este patrón funciona en los tres modelos de aislamiento.
¿Puedo combinar modelos de aislamiento multiinquilino para diferentes niveles de clientes?
Sí. Ejecute tablas compartidas con RLS para su nivel estándar y base de datos por inquilino para clientes empresariales que requieren certificaciones de cumplimiento. Stripe y Notion utilizan variaciones de este enfoque híbrido. Agrega complejidad operativa pero le permite brindar servicios a las PYMES a bajo costo y al mismo tiempo cumplir con los requisitos de aislamiento de datos empresariales.
Lectura relacionada
Cuándo migrar de un monolito a microservicios (y cuándo no)
La mayoría de las startups adoptan microservicios demasiado pronto. La mayoría de las empresas esperan demasiado. Aquí se explica cómo saber cuándo su monolito se ha quedado pequeño y cómo migrar sin reescribirlo.
Supabase vs Firebase vs backend personalizado: cuál para tu startup
Supabase te ofrece Postgres gratis hasta 500 MB. Firebase llega a millones pero te encierra en el ecosistema de Google. Un backend personalizado cuesta entre 3000 y 8000 dólares por adelantado, pero le brinda control total.
Sin servidor versus contenedores: ¿qué arquitectura se adapta a su SaaS?
La tecnología sin servidor cuesta 0 dólares en el lanzamiento, pero se vuelve costosa a escala. Los contenedores cuestan más por adelantado pero siguen siendo predecibles. A continuación se explica cómo elegir la arquitectura adecuada para su producto SaaS.