Arquitectura software clínica multi-tenant · decisiones 2026
Construir software SaaS para clínicas privadas arrastra una restricción que no admite atajos · los datos de una clínica jamás pueden filtrarse a otra · ni en una consulta accidental · ni en un export masivo · ni en un bug de paginación. Lo que en cualquier SaaS B2B sería un incidente serio · en sanitario es notificación AEPD obligatoria en 72 horas · multa potencial RGPD seis cifras y pérdida irreversible de todos los clientes. La decisión arquitectónica fundacional de cualquier plataforma clínica es cómo separar tenants. Este artículo recorre las opciones reales · explica por qué hemos elegido Row-Level Security Postgres sobre alternativas y muestra trade-offs honestos con ejemplos código aplicables.
Qué es multi-tenancy y por qué importa en clínicas
- Multi-tenancy es la capacidad de una sola instancia de software de servir a múltiples clientes (tenants) garantizando aislamiento de datos · configuración y comportamiento entre ellos.
- El extremo opuesto es single-tenant · una instancia separada por cada cliente · operativamente carísimo de mantener y prácticamente inviable para llegar a 100+ clínicas con un equipo pequeño.
- En clínicas el aislamiento es restricción legal RGPD · no preferencia técnica · cualquier cross-tenant leak es brecha de seguridad notificable en 72 horas (Art. 33 RGPD).
- Multi-tenancy bien implementada combina lo mejor de ambos · coste compartido infraestructura · pero aislamiento de datos verificable.
Las 4 estrategias principales de aislamiento
- Database per tenant · cada cliente tiene su propia base de datos · aislamiento físico · backup independiente · pero coste operacional alto · migraciones costosas · uso eficiente recursos malo a escala pequeña.
- Schema per tenant · una base de datos compartida pero schema separado por cliente · aislamiento lógico fuerte pero el catálogo Postgres se infla rápido pasados los cientos de tenants · backup granular complejo.
- Row-Level Security (RLS) · una sola base · tablas compartidas con columna tenant_id · políticas Postgres garantizan que cada query solo ve filas propias · escalable y operacionalmente simple.
- Application-level filtering · la aplicación añade WHERE tenant_id en cada query · no recomendado · un solo bug del developer y hay leak · sin red de seguridad a nivel base.
Por qué hemos elegido Row-Level Security Postgres
- El motor Postgres aplica el filtro tenant_id en todas las queries automáticamente · imposible que un developer olvide añadir WHERE · imposible que un bug paginación filtre filas ajenas · la red de seguridad está al nivel más bajo posible.
- Supabase implementa RLS nativo · políticas declarativas SQL · auditables · revisables en pull request · evolucionables sin migraciones costosas.
- Coste operativo bajo · una sola base de datos · un solo backup · una sola migración para todos los tenants · escalable a miles de clínicas con recursos modestos.
- Auditoría externa simple · cualquier consultor puede revisar política RLS en SQL plano y verificar comportamiento con queries de prueba · no requiere comprensión profunda de toda la app.
- Trade-off conocido · todas las clínicas comparten recursos compute · un noisy neighbor puede impactar latencia · mitigable con monitoring y segregación de instancias por planes premium si fuera necesario.
Comparativa rápida de las 4 estrategias
| Estrategia | Aislamiento | Coste operativo | Escala práctica | Recomendación |
|---|---|---|---|---|
| DB per tenant | Físico fuerte | Alto | Decenas | Solo enterprise dedicado |
| Schema per tenant | Lógico fuerte | Medio | Cientos | Útil con tenants < 500 |
| Row-Level Security | Lógico fuerte declarativo | Bajo | Miles | Recomendable defecto SaaS clínicas |
| App-level filter | Frágil (depende código) | Bajo | Variable | Evitar en sanitario |
Ejemplo política RLS Postgres aplicado
Una política RLS Postgres típica para una tabla "patients" en SaaS clínicas. Cada paciente pertenece a una clínica · ninguna query puede ver pacientes de otra clínica.
- Activar RLS sobre la tabla ·
ALTER TABLE patients ENABLE ROW LEVEL SECURITY; - Forzar RLS también para el rol service · evita bypass accidental ·
ALTER TABLE patients FORCE ROW LEVEL SECURITY; - Crear política SELECT ·
CREATE POLICY patients_tenant_select ON patients FOR SELECT USING (clinic_id = current_setting('app.clinic_id')::uuid); - Crear política INSERT ·
CREATE POLICY patients_tenant_insert ON patients FOR INSERT WITH CHECK (clinic_id = current_setting('app.clinic_id')::uuid); - En cada conexión la aplicación setea la sesión ·
SELECT set_config('app.clinic_id', '[uuid-clinica]', true); - A partir de ahí cualquier query que la aplicación haga sobre patients automáticamente filtra por clinic_id · no hay forma de saltarse el filtro sin permiso explícito superuser.
Riesgos típicos de RLS y cómo mitigarlos
- Olvidar activar FORCE ROW LEVEL SECURITY · el rol superuser ignora RLS por defecto · si la aplicación se conecta con rol service privilegiado el filtro deja de aplicar · siempre FORCE en tablas con PII.
- Configurar mal current_setting() · si la aplicación no setea la variable de sesión la política puede comportarse de forma inesperada · política debe denegar acceso por defecto cuando current_setting es NULL.
- Performance impact en queries grandes · RLS añade WHERE implícito · índices en columna tenant_id son obligatorios · medir EXPLAIN ANALYZE de queries críticas regularmente.
- Backups y restores · cuidado con restore que preserve políticas RLS · validar tras cualquier operación admin.
- Joins entre tablas · cada tabla en el join debe tener su propia política coherente · políticas inconsistentes generan resultados confusos · auditar.
Testing cross-tenant leak en CI
- Tests automatizados que insertan datos en dos tenants distintos · cambian contexto sesión a uno · verifican que ninguna query con permisos de aplicación retorna datos del otro.
- Test del path service role · validar que rol privilegiado solo se usa donde absolutamente necesario (admin · migrations) · nunca en path request paciente.
- Fuzz testing con tenant_id aleatorio en sesión · comprobar que aplicación reacciona correctamente · no leak · errores controlados.
- Auditoría log de cada acceso que toque PII · tabla audit_log con tenant_id origen · facilita forense en incidente.
- Test de regresión por cada cambio de política RLS · políticas son código · revisión en pull request por al menos un par.
Ejemplos open-source de referencia
- Supabase docs · sección Row Level Security · documentación oficial con patrones reales.
- Cal.com · plataforma reservas open-source · aplica estrategia mixta application-level + Prisma middleware · útil ver el contraste de enfoque.
- Medplum · plataforma sanitaria open-source · usa estrategia Row-Level Security agresiva con FHIR · referencia útil para apps medical-grade.
- PostgreSQL docs · capítulo Row Security Policies · la fuente canónica · merece la pena leer al diseñar políticas no triviales.
Cuándo NO usar RLS Postgres
- Aplicación que no usa Postgres · MongoDB · DynamoDB tienen patrones equivalentes (Atlas App Services rules · IAM policy condition) pero el modelo es distinto.
- Necesidad de aislamiento físico verdadero por exigencia contractual de cliente enterprise · DB per tenant inevitable.
- Equipo developer sin experiencia SQL avanzado · RLS mal implementado es peor que RLS no implementado · formación previa imprescindible.
- Necesidad de queries cross-tenant frecuentes para analytics o admin · viable con RLS pero añade complejidad · evaluar trade-off.
Cómo encaja AI Empire
AI Empire usa Row-Level Security Postgres sobre Supabase como estrategia central de aislamiento · cada clínica es un tenant identificado por clinic_id · todas las tablas con PII tienen RLS activado y forzado · tests cross-tenant automáticos corren en cada pull request · sesión de aplicación setea clinic_id desde token JWT verificado y nunca confía en input del cliente. Esta decisión queda documentada como ADR (Decision Record) auditable. Para entender el resto de decisiones de stack revisa la guía tech stack clínica privada · para comprender cómo impacta esto en operativa diaria de la clínica revisa la guía operativa clínica privada · y para entender cómo se comunica garantía de aislamiento al cliente revisa la guía marketing clínica privada.
Próximo paso
Si tu equipo está evaluando arquitectura multi-tenant para un producto SaaS clínicas el paso útil inmediato es auditar el esquema actual de tu base de datos · identificar todas las tablas con PII · listar qué política de aislamiento aplica hoy y decidir si migrar a RLS o reforzar la estrategia existente. Pide una demo y vemos juntos cómo encaja esta arquitectura con tu necesidad operativa concreta sin imponer soluciones genéricas.
Disclaimer: este artículo es guía técnica · NO constituye asesoramiento jurídico específico sobre cumplimiento RGPD. La elección de arquitectura multi-tenant debe revisarse junto con evaluación de impacto privacidad (EIPD) cuando aplique · análisis de riesgo del Responsable del Tratamiento y contrato encargado del tratamiento con cualquier subencargado (Art. 28 RGPD). Los ejemplos de código SQL son ilustrativos · cualquier política RLS de producción debe revisarse por ingeniero senior y auditarse externamente antes de operar con datos reales. Las referencias a proyectos open-source (Supabase · Cal.com · Medplum) son orientativas y reflejan estado conocido a fecha publicación. AI Empire documenta su arquitectura en Architecture Decision Records públicos · cualquier interesado puede revisar la política RLS aplicada bajo NDA estándar.