--- id: "ADR-002" title: "Estrategia de Multi-Tenancy" type: "ADR" status: "Accepted" date: "2025-12-08" deciders: ["Architecture Team"] tags: ["database", "rls", "multitenancy", "security"] project: "platform_marketing_content" created_date: "2025-12-08" updated_date: "2026-01-04" --- # ADR-002: Estrategia de Multi-Tenancy **Fecha:** 2025-12-08 **Estado:** Aceptado --- ## Contexto La plataforma debe soportar múltiples organizaciones (tenants) con: 1. Aislamiento completo de datos entre tenants 2. Configuración personalizable por tenant 3. Posibilidad de escalar a SaaS público 4. Costos operativos manejables Opciones de multi-tenancy: - **Database per tenant:** Una BD por cada tenant - **Schema per tenant:** Un schema por tenant en misma BD - **Shared schema:** Todos en mismas tablas con tenant_id --- ## Decisión ### Estrategia: Shared Schema con Row-Level Security (RLS) Usaremos una sola base de datos con esquemas funcionales (auth, crm, projects, etc.) donde todas las tablas tienen columna `tenant_id` y políticas RLS que filtran automáticamente. ### Implementación ```sql -- Columna tenant_id en todas las tablas principales ALTER TABLE crm.clients ADD COLUMN tenant_id UUID NOT NULL; -- Política RLS CREATE POLICY tenant_isolation ON crm.clients USING (tenant_id = current_setting('app.current_tenant')::uuid); -- Habilitar RLS ALTER TABLE crm.clients ENABLE ROW LEVEL SECURITY; ``` ### Middleware de Tenant ```typescript @Injectable() export class TenantMiddleware implements NestMiddleware { async use(req: Request, res: Response, next: NextFunction) { const tenantId = this.extractTenantFromJwt(req); // Set para la conexión actual await this.dataSource.query( `SET app.current_tenant = '${tenantId}'` ); next(); } } ``` ### Storage Isolation ``` bucket/ ├── {tenant_slug}/ │ ├── assets/ │ ├── models/ │ └── temp/ ``` --- ## Consecuencias ### Positivas - **Un solo deployment:** Una instancia sirve a todos los tenants - **Costos reducidos:** No hay overhead de múltiples BDs - **Mantenimiento simple:** Migraciones se aplican una vez - **RLS nativo:** PostgreSQL garantiza aislamiento a nivel de motor - **Queries simples:** No hay JOINs entre BDs/schemas - **Escalabilidad:** Read replicas y sharding posibles ### Negativas - **Complejidad de RLS:** Requiere configurar políticas en cada tabla - **Risk de bug:** Error en middleware podría filtrar datos incorrectos - **Backups:** No hay backups por tenant (todo junto) - **Noisy neighbors:** Un tenant pesado afecta a todos ### Mitigaciones 1. **Tests exhaustivos** de RLS en cada tabla 2. **Logs de auditoría** de todas las queries con tenant_id 3. **Rate limiting** por tenant 4. **Monitoring** de queries lentas por tenant --- ## Alternativas Consideradas ### 1. Database per Tenant **Pros:** - Aislamiento completo - Backups individuales - Fácil de escalar por tenant **Contras:** - Costos altos (una BD por cliente) - Complejidad de deployment - Migraciones en múltiples BDs - Connection pooling complejo **Rechazo:** Costos prohibitivos para MVP ### 2. Schema per Tenant **Pros:** - Buen aislamiento - Una sola BD - Backups parciales posibles **Contras:** - Migraciones en múltiples schemas - Límite de schemas en PostgreSQL - Complejidad de routing **Rechazo:** Complejidad sin beneficio claro sobre RLS --- ## Referencias - [PostgreSQL RLS Documentation](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) - [Multi-tenant SaaS patterns](https://docs.microsoft.com/en-us/azure/architecture/guide/multitenant/) --- **Documento generado por:** Requirements-Analyst **Fecha:** 2025-12-08