ADR-001: Aislamiento multi-tenant por schema de empresa, no por sucursal
Fecha: 2026-06-25
Estado: Aprobado
Responsable: Orchestrator
Contexto
Venuo opera como SaaS multi-tenant con la librería django-tenants (Shared Database, Separate Schemas). Antes de iniciar el Sprint 0 técnico surgió la necesidad de definir cuál es la unidad de aislamiento en PostgreSQL: la Empresa (tenant) o la Sucursal (branch dentro de un tenant).
La distinción importa porque determina la granularidad de los backups operacionales y el nivel de autonomía que puede tener cada sucursal sobre sus propios datos.
Decisión
El schema de PostgreSQL se crea por Empresa (tenant), no por Sucursal.
Cada sucursal es un registro en la tabla Sucursal dentro del schema de su empresa, identificada por sucursal_id. Los datos de todas las sucursales de una empresa conviven en el mismo schema, filtrados por sucursal_id en cada query.
La autonomía de datos que requieren las sucursales se resuelve como feature de la aplicación (exportación de datos por sucursal), no como aislamiento de base de datos.
Alternativas consideradas
| Alternativa | Ventajas | Desventajas |
|---|---|---|
| Schema por Empresa (elegida) | Simple, estándar en django-tenants, migraciones centralizadas por tenant, sin explosión de schemas | Los backups de pg_dump no son granulares por sucursal — se compensan con exportaciones a nivel app |
| Schema por Sucursal | pg_dump real e independiente por sucursal, aislamiento máximo | Explosión de schemas (100 empresas × 5 sucursales = 500 schemas), routing de django-tenants más complejo, migraciones multiplicadas, poco común en SaaS multi-tenant |
Consecuencias
Positivas
- La arquitectura sigue el patrón estándar de
django-tenantssin modificaciones al routing. - Las migraciones se ejecutan una vez por empresa, no una vez por sucursal.
- El número de schemas crece con el número de empresas, no con el número de sucursales — escala linealmente.
- Backups operacionales simples:
pg_dumpglobal (MVP) o por schema de empresa (futuro).
Negativas / trade-offs
- No es posible hacer un
pg_dumpnativo e independiente de una sola sucursal. - La granularidad por sucursal se resuelve en capa de aplicación — requiere construir la feature de exportación.
Compensación — Data Portability por sucursal
Para cubrir la necesidad de que cada sucursal gestione sus propios datos de forma autónoma, se construirá como feature de la aplicación:
- El rol
sucursal_adminpuede exportar sus datos desde el panel de administración. - Exportación disponible en formato CSV/JSON para: ventas por período, historial de caja, stock actual e historial de movimientos.
- El
empresa_adminpuede exportar datos de cualquiera de sus sucursales. - Esta feature se registra en
docs/BACKLOG.mdpara planificación.
Estrategia de backups operacionales
Entornos
El sistema de backups opera igual en desarrollo y producción. El comportamiento se controla por variables de entorno — no hay diferencia en el código.
| Entorno | Bind mount local | Offsite | Proveedor VPS |
|---|---|---|---|
| Desarrollo (Windows) | C:\ops\venuo\backups\ |
No aplica | Docker Desktop local |
| Producción (VPS Linux) | /opt/venuo/backups/ |
Sí — via rclone |
A definir al momento del deploy |
Automatización
El backup corre en un contenedor cron dedicado definido en docker-compose.yml. No depende del cron del sistema operativo del host — la automatización viaja con el proyecto y funciona igual en desarrollo y producción con docker compose up.
El contenedor usa la misma imagen de PostgreSQL que el servicio principal para garantizar compatibilidad exacta de pg_dump.
Alternativas descartadas: - Manual: no viable en producción — depende de intervención humana. - Cron del host: rompe portabilidad — requiere reconfiguración manual al migrar de servidor.
Flujo completo de producción
02:00 AM — contenedor cron ejecuta backup.sh
↓
pg_dump comprimido en /backups/{daily|weekly|monthly}/
↓
Script GFS elimina archivos viejos según retención configurada
↓
Si BACKUP_OFFSITE_ENABLED=true: rclone copia a BACKUP_RCLONE_REMOTE
Frecuencia y granularidad
| Fase | Estrategia | Frecuencia | Implementación |
|---|---|---|---|
| MVP | Backup global de toda la DB | Diaria — 02:00 AM | pg_dump venuo_db comprimido |
| Producción con clientes | Backup por schema de empresa | Cada 6 horas — solo cambiar BACKUP_CRON_SCHEDULE |
pg_dump --schema=tenant_X por cada tenant activo |
RPO: máximo 24 horas en MVP. Reducible a 6 horas sin reescribir código.
Retención — esquema GFS simplificado
| Tipo | Cantidad | Criterio |
|---|---|---|
| Diarios | 7 | Último backup de cada día |
| Semanales | 4 | Backup del domingo |
| Mensuales | 3 | Backup del día 1 de cada mes |
Total: 14 archivos máximo en disco local + espejo en offsite. Cubre 3 meses de historia.
Extensible a 12 mensuales ajustando BACKUP_MONTHLY_RETENTION en .env.
Variables de entorno requeridas
| Variable | Descripción | Ejemplo |
|---|---|---|
BACKUP_LOCAL_PATH |
Ruta local dentro del contenedor | /backups |
BACKUP_CRON_SCHEDULE |
Schedule del cron | 0 2 * * * (02:00 AM diario) |
BACKUP_DAILY_RETENTION |
Días a conservar | 7 |
BACKUP_WEEKLY_RETENTION |
Semanas a conservar | 4 |
BACKUP_MONTHLY_RETENTION |
Meses a conservar | 3 |
BACKUP_OFFSITE_ENABLED |
Activa copia offsite | false (dev) / true (prod) |
BACKUP_RCLONE_REMOTE |
Destino rclone configurado | r2:venuo-backups / b2:venuo-backups / s3:venuo-backups |
El proveedor offsite (Cloudflare R2, Backblaze B2, Amazon S3 u otro compatible con rclone) se define al momento del deploy de producción sin modificar código ni estructura.
Snapshots de VPS
El proveedor de VPS debe tener snapshots automáticos del disco habilitados. Se configura desde el panel del proveedor al momento del deploy — es independiente del sistema de backups de la aplicación y actúa como última línea de defensa.
Los backups operacionales son responsabilidad del DevOps Agent e invisibles para los roles de la aplicación.
Impacto en el sistema
- Backend: Sin impacto — arquitectura de django-tenants sin cambios. El filtrado por
sucursal_idya existe en el modelo. - Frontend: La feature de exportación por sucursal requiere una pantalla en el panel del
sucursal_admin. - DevOps: Script de backup global en Sprint 0. Migración a backup por schema en sprint futuro documentado en BACKLOG.
- Seguridad: La exportación de datos debe respetar el scope RBAC —
sucursal_adminsolo exporta su propia sucursal.
Documentos actualizados
docs/adr/ADR_INDEX.mddocs/ARCHITECTURE.md— sección sobre límite de schema y data portabilitydocs/BACKLOG.md— feature de exportación de datos por sucursaldocs/STACK.md— estrategia de backups definida
Revisión futura
Revisar cuando el número de tenants activos supere 50 o cuando un cliente requiera contractualmente aislamiento de base de datos a nivel sucursal. En ese caso evaluar schema por sucursal con ADR de reemplazo.