workspace-v1/shared/knowledge-base/architecture/PATRON-MULTI-TENANT.md
rckrdmrd 66161b1566 feat: Workspace-v1 complete migration with NEXUS v3.4
Sistema NEXUS v3.4 migrado con:

Estructura principal:
- core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles)
- core/catalog: Catalogo de funcionalidades reutilizables
- shared/knowledge-base: Base de conocimiento compartida
- devtools/scripts: Herramientas de desarrollo
- control-plane/registries: Control de servicios y CI/CD
- orchestration/: Configuracion de orquestacion de agentes

Proyectos incluidos (11):
- gamilit (submodule -> GitHub)
- trading-platform (OrbiquanTIA)
- erp-suite con 5 verticales:
  - erp-core, construccion, vidrio-templado
  - mecanicas-diesel, retail, clinicas
- betting-analytics
- inmobiliaria-analytics
- platform_marketing_content
- pos-micro, erp-basico

Configuracion:
- .gitignore completo para Node.js/Python/Docker
- gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git)
- Sistema de puertos estandarizado (3005-3199)

Generated with NEXUS v3.4 Migration System
EPIC-010: Configuracion Git y Repositorios
2026-01-04 03:37:42 -06:00

197 lines
5.5 KiB
Markdown

# Patron Multi-Tenant con RLS
**Categoria:** Architecture
**Version:** 1.0.0
**Origen:** projects/gamilit, projects/erp-core
**Fecha:** 2025-12-27
---
## Descripcion
Este documento describe el patron de arquitectura multi-tenant utilizado en todos los proyectos del workspace, basado en Row-Level Security (RLS) de PostgreSQL.
## Principios
1. **Aislamiento completo**: Cada tenant solo ve sus propios datos
2. **Transparencia**: El codigo de aplicacion no necesita filtrar por tenant
3. **Performance**: RLS optimizado con indices apropiados
4. **Seguridad**: Imposible acceder a datos de otros tenants
## Arquitectura
```
┌─────────────────────────────────┐
│ Application │
│ (NestJS / Express) │
└───────────────┬─────────────────┘
┌───────────────▼─────────────────┐
│ Tenant Middleware │
│ SET app.current_tenant_id │
└───────────────┬─────────────────┘
┌───────────────▼─────────────────┐
│ PostgreSQL │
│ Row-Level Security │
│ (USING tenant_id = ...) │
└─────────────────────────────────┘
```
## Implementacion
### 1. Variable de Sesion
```sql
-- Configurar en cada request
SET app.current_tenant_id = 'uuid-del-tenant';
-- Obtener en queries
current_setting('app.current_tenant_id', true)::UUID
```
### 2. Middleware NestJS
```typescript
// src/middleware/tenant.middleware.ts
@Injectable()
export class TenantMiddleware implements NestMiddleware {
constructor(private dataSource: DataSource) {}
async use(req: Request, res: Response, next: NextFunction) {
const tenantId = this.extractTenantId(req);
if (tenantId) {
await this.dataSource.query(
`SET app.current_tenant_id = $1`,
[tenantId]
);
}
next();
}
private extractTenantId(req: Request): string | null {
// 1. Del JWT token
if (req.user?.tenantId) return req.user.tenantId;
// 2. Del header
if (req.headers['x-tenant-id']) return req.headers['x-tenant-id'];
// 3. Del subdominio
const host = req.headers.host;
// tenant.example.com -> tenant
return null;
}
}
```
### 3. RLS Policies
```sql
-- Habilitar RLS en tabla
ALTER TABLE my_table ENABLE ROW LEVEL SECURITY;
-- Politica de aislamiento
CREATE POLICY tenant_isolation ON my_table
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- Para INSERT, asegurar que se use el tenant correcto
CREATE POLICY tenant_insert ON my_table
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
```
### 4. Estructura de Tabla
```sql
CREATE TABLE my_table (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
-- ... otros campos ...
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indice para performance
CREATE INDEX idx_my_table_tenant ON my_table(tenant_id);
```
## Best Practices
### DO (Hacer)
- Siempre incluir `tenant_id` en tablas de datos
- Crear indice en `tenant_id`
- Habilitar RLS en TODAS las tablas con datos de tenant
- Usar el middleware en TODAS las rutas protegidas
- Testear aislamiento en cada nueva tabla
### DON'T (No hacer)
- No filtrar manualmente por tenant_id (dejar a RLS)
- No desactivar RLS para "queries rapidas"
- No hardcodear tenant_id en codigo
- No compartir conexiones entre requests sin reset
## Tablas Exentas de RLS
Algunas tablas son globales y no tienen tenant_id:
- `auth.tenants` - Lista de todos los tenants
- `config.feature_flags` - Flags globales
- `billing.plans` - Planes disponibles
## Testing
```typescript
describe('Tenant Isolation', () => {
it('should not see data from other tenant', async () => {
// Crear datos en tenant A
await setTenant(tenantA);
await createRecord({ name: 'Record A' });
// Cambiar a tenant B
await setTenant(tenantB);
const records = await getRecords();
// No debe ver record de tenant A
expect(records).not.toContainEqual(
expect.objectContaining({ name: 'Record A' })
);
});
});
```
## Troubleshooting
### Error: "Permission denied for table"
- Verificar que RLS esta habilitado
- Verificar que la politica existe
- Verificar que `app.current_tenant_id` esta configurado
### Query retorna todos los registros
- Verificar que el middleware esta activo
- Verificar que `current_setting` retorna el UUID correcto
- Verificar que la politica no tiene errores
### Performance lenta
- Crear indice en `tenant_id`
- Verificar que el indice se usa (EXPLAIN ANALYZE)
- Considerar particionamiento por tenant_id
## Referencias
- `core/catalog/multi-tenancy/` - Modulo del catalogo
- `projects/erp-core/database/ddl/` - Ejemplos de DDL
- PostgreSQL RLS Documentation
---
*Knowledge Base - Workspace v1*