---
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
