Saltar a contenido

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-tenants sin 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_dump global (MVP) o por schema de empresa (futuro).

Negativas / trade-offs

  • No es posible hacer un pg_dump nativo 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_admin puede 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_admin puede exportar datos de cualquiera de sus sucursales.
  • Esta feature se registra en docs/BACKLOG.md para 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_id ya 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_admin solo exporta su propia sucursal.

Documentos actualizados

  • docs/adr/ADR_INDEX.md
  • docs/ARCHITECTURE.md — sección sobre límite de schema y data portability
  • docs/BACKLOG.md — feature de exportación de datos por sucursal
  • docs/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.