--- id: "ADR-001" title: "Multi-tenancy con PostgreSQL Row-Level Security" type: "ADR" status: "Accepted" priority: "P0" supersedes: "N/A" superseded_by: "N/A" version: "1.0.0" created_date: "2026-01-07" updated_date: "2026-01-10" --- # ADR-001: Multi-tenancy con PostgreSQL Row-Level Security ## Metadata | Campo | Valor | |-------|-------| | ID | ADR-001 | | Estado | Accepted | | Fecha | 2026-01-10 | | Supersede | N/A | ## Contexto Template SaaS requiere soporte para múltiples organizaciones (tenants) en una misma instancia de la aplicación. Cada tenant debe tener sus datos completamente aislados de otros tenants por razones de seguridad y privacidad. ### Requisitos - Aislamiento completo de datos entre tenants - Escalabilidad para miles de tenants - Simplicidad operacional (un solo deployment) - Performance consistente - Facilidad de backup/restore por tenant ## Opciones Consideradas ### Opción 1: Database per Tenant **Descripción:** Cada tenant tiene su propia base de datos PostgreSQL. **Pros:** - Aislamiento total de datos - Fácil backup/restore individual - Sin riesgo de data leaks entre tenants **Contras:** - Complejidad operacional alta (miles de DBs) - Connection pooling complejo - Migraciones costosas (aplicar a cada DB) - Mayor costo de infraestructura ### Opción 2: Schema per Tenant **Descripción:** Cada tenant tiene su propio schema dentro de una base de datos. **Pros:** - Mejor aislamiento que shared tables - Backup/restore por schema posible - Un solo connection pool **Contras:** - Migraciones aún costosas - Límite práctico de schemas (~10,000) - Complejidad en queries cross-tenant ### Opción 3: Row-Level Security (RLS) ✓ **Descripción:** Todos los tenants comparten las mismas tablas, con políticas RLS que filtran por `tenant_id`. **Pros:** - Operacionalmente simple - Un solo set de tablas y migraciones - Escalable a millones de tenants - Performance optimizado con índices - Queries simples y consistentes **Contras:** - Riesgo de data leaks si RLS mal configurado - Requiere disciplina en desarrollo - Backup individual más complejo ## Decisión **Elegimos Row-Level Security (RLS)** por las siguientes razones: 1. **Simplicidad Operacional:** Un solo deployment, una base de datos, un set de migraciones 2. **Escalabilidad:** Soporta desde 1 hasta millones de tenants sin cambios arquitectónicos 3. **Performance:** PostgreSQL RLS es eficiente y se beneficia de índices 4. **Costo:** Menor infraestructura requerida ## Implementación ### Estructura de Tablas Todas las tablas multi-tenant incluyen: ```sql tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ``` ### Políticas RLS ```sql -- Ejemplo para tabla users ALTER TABLE users.users ENABLE ROW LEVEL SECURITY; CREATE POLICY users_tenant_isolation ON users.users USING (tenant_id = current_setting('app.current_tenant_id')::uuid); ``` ### Context Setting El `tenant_id` se establece en cada request: ```sql SET app.current_tenant_id = 'uuid-del-tenant'; ``` ### Middleware NestJS ```typescript @Injectable() export class TenantContextInterceptor implements NestInterceptor { async intercept(context: ExecutionContext, next: CallHandler) { const request = context.switchToHttp().getRequest(); const tenantId = request.user?.tenant_id; await this.dataSource.query( `SET app.current_tenant_id = '${tenantId}'` ); return next.handle(); } } ``` ## Consecuencias ### Positivas - Código más simple y mantenible - Un solo esquema de datos - Migraciones atómicas - Queries sin WHERE tenant_id manual - Escalabilidad lineal ### Negativas - Requiere testing exhaustivo de RLS - Datos de todos los tenants en mismas tablas - Backup por tenant requiere queries filtradas - Posible contención en tablas muy grandes ### Mitigaciones - Tests automatizados de aislamiento - Auditoría de queries en desarrollo - Particionamiento por tenant_id si necesario - Monitoreo de performance por tenant ## Referencias - [PostgreSQL RLS Documentation](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) - [Multi-tenancy Patterns](https://docs.microsoft.com/en-us/azure/architecture/patterns/multi-tenant) - Schema DDL: `apps/database/ddl/schemas/` --- **Fecha decision:** 2026-01-10 **Autores:** Claude Code (Arquitectura)