0002. Multiempresa: una base por producto, aislada por empresa vía RLS¶
Estado: Aceptado Fecha: 2026-08-28 Autor: Duval Alcivar
Contexto¶
Gerencia decidió que el sistema sea multiempresa: varias empresas usan la misma plataforma, cada una con sus propios datos. Hay que decidir cómo se aísla la información de una empresa frente a otra, y cómo viaja la empresa activa a través de cada petición.
Con la arquitectura de microservicios definida (ver ADR 0003), una base
por empresa multiplicaría el problema: N servicios × M empresas = bases
que migrar en cada cambio. Además, hoy el aislamiento ya se resuelve a
nivel de fila en svc-operaciones usando empresa_id, así que existe
precedente interno.
El código actual referencia 15.200 sentencias WHERE co_empresa repartidas
por el sistema — cada una es una oportunidad de fuga entre empresas si un
desarrollador la olvida.
Decisión¶
- Una base de datos por producto (
core_sigfa,erp_sigfa,crm_sigfapro), no una base por empresa. Se descarta explícitamente el modelo de "una base por cliente". - Aislamiento por Row-Level Security (RLS) en cada tabla, usando
empresa_idcomo columna de partición lógica:
-- El aislamiento entre empresas lo garantiza la base de datos, no el programador:
CREATE POLICY aislamiento_empresa ON erp.operaciones.trx_existencia
USING (empresa_id = NULLIF(current_setting('app.current_empresa_id', true), '')::BIGINT);
- La empresa viaja en el token de autenticación, nunca como parámetro que el cliente pueda mandar directamente en la petición.
- Tenant = empresa. Planta y sucursal son alcance de permisos dentro de una empresa, no una dimensión de aislamiento adicional.
- Cada microservicio, tabla y test de CI debe validar el aislamiento por
empresa_id+ RLS — esto es una regla no negociable, exigida antes de la primera línea de código de cada servicio, no agregada después.
Alternativas descartadas¶
- Una base de datos por empresa — descartada: con 14 microservicios planeados, multiplicaría a N servicios × M empresas bases que provisionar, migrar y respaldar en cada cambio de esquema.
- Aislamiento solo a nivel de aplicación (filtrar
WHERE co_empresaen cada consulta manualmente) — descartado: es el patrón actual, y con 15.200 puntos donde se debería aplicar el filtro, un solo olvido en código nuevo o en una consulta ad-hoc es una fuga de datos entre empresas. RLS mueve la garantía de "cada desarrollador se acuerda" a "la base de datos lo impone". - Tenant = planta o sucursal — descartado: planta y sucursal son alcance de permisos dentro de una misma empresa, no fronteras de aislamiento de datos.
Consecuencias¶
- El aislamiento entre empresas queda garantizado por PostgreSQL (RLS), no por disciplina de cada desarrollador al escribir cada query.
- Cada tabla nueva y cada microservicio nuevo deben incluir test de aislamiento en CI desde el primer commit.
- Queda pendiente (fuera de este ADR, a definir por servicio) cómo se
setea
app.current_empresa_iden cada conexión — típicamente al inicio de cada request, a partir del token. Ver el ADR 0012, que define que elSETocurre en el middleware, una vez por request, y que los Repositories no lo repiten. - Este ADR es uno de los "cuatro ADR de fundación" recomendados antes de escribir código de plataforma (junto con el ADR 0003 de microservicios, uno de modelo de despliegue y uno de mensajería/contratos entre servicios).