Saltar a contenido

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_id como 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_empresa en 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_id en cada conexión — típicamente al inicio de cada request, a partir del token. Ver el ADR 0012, que define que el SET ocurre 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).