Aislamiento multi-tenant impuesto por la base de datos, no por el código
El aislamiento entre municipios lo impone Postgres con RLS, no el código de aplicación
El Desafío
- Un municipio nunca ve al otro: cada tabla operativa lleva su ayuntamiento y FORCE ROW LEVEL SECURITY; pedir un recurso ajeno por ID responde 404, nunca 403
- Tenant dentro de la transacción: se fija con SET LOCAL, nunca con un SET suelto — el pooler recicla conexiones y el siguiente request podría ser de otro municipio
- Rejas automáticas, no disciplina: una tabla nueva sin prueba de aislamiento no entra, y un test de esquema recorre Postgres y tumba el build si encuentra una tabla de municipio sin RLS activo
- Límites de capa verificados: ui → application → domain ← infrastructure, con el dominio sin importar nada; dependency-cruiser lo revisa en pre-push y en CI
Arquitectura de la Solución
Resolución por subdominio
El municipio se resuelve desde el Host en el proxy de Next.js, antes que la sesión. El login corre dentro de la misma capa de tenancy que cualquier otra tabla.
Capa de tenancy
Todo acceso a datos pasa por withTenant: abre transacción, fija el tenant con SET LOCAL y deja que la política RLS haga el filtrado. No hay conexión directa a la base.
Base de datos
PostgreSQL con Drizzle ORM, elegido por RLS y por el control explícito de la transacción. Roles separados para migración y aplicación: ninguno es superusuario ni puede saltarse RLS.
Calidad
Especificación primero (SDD) y TDD estricto: pruebas unitarias y de aislamiento en Vitest, E2E en Playwright, Biome para lint y formato, dependency-cruiser para los límites de capa.
Resultados
RLS
Aislamiento en la base, no en el código
SDD + TDD
Especificación escrita antes del código, prueba antes de la implementación
0 BYPASSRLS
Ningún rol de la aplicación puede saltarse la política