0022. Patrón party: mae_entidades, personas natural y jurídica¶
Estado: Propuesto
Fecha: 2026-09-14
Autor: Duval Alcivar
Módulo(s) afectado(s): svc-identidad (mae_usuarios, mae_personas), todos los servicios que referencian personas
Contexto¶
mae_personas centraliza los datos de personas naturales (cédula) y la usan
varios módulos (nómina, promotores internos, usuarios). mae_usuarios tenía un
personal_id NOT NULL hacia mae_personas.
El problema: un usuario también puede representar a una persona jurídica
externa (empresa cliente o consultor que factura con RUC), y ese registro no
debe ir en mae_personas (exclusiva de personas naturales con cédula), porque
necesita su propio conjunto de campos (identificación fiscal, razón social,
ubicación). "Externo" es el
rol/tipo del usuario, no el tipo de entidad: un externo puede ser persona
natural (cédula) o jurídica (RUC).
Condicionar con if/else cada consulta de usuario según el tipo de entidad es
costoso y frágil. El ADR 0005 (usuarios) y el 0007 (tipos de usuario) no cubrían
este caso.
Decisión¶
Se aplica el patrón party con herencia por tabla (joined table inheritance):
mae_entidades— raíz polimórfica.id UUID+tipo_entidad(PERSONA_NATURAL|PERSONA_JURIDICA). Elidnace aquí, una sola vez.mae_personas— persona natural. Hereda demae_entidades: su PK es FK amae_entidades.id(mismo UUID, sinDEFAULT gen_random_uuid()).mae_personas_juridicas— persona jurídica externa (nombre,nombre_comercial,identificacione identificación geográfica/fiscal vía catálogos). Misma regla de herencia. No es una empresa del grupo: esas siguen enmae_empresas.mae_usuarios.entidad_id— reemplaza apersonal_idy apunta amae_entidades. Un solo FK, sin condicionales.cat_tipos_usuario— catálogo de tipos de cuenta (usuario,root,externo).mae_usuarios.tipo_usuario(VARCHAR + CHECK) pasa atipo_usuario_id(FK). Ver el ADR 0007, que se actualiza con este.
Cada subtipo expone nombre_mostrar (nombres + apellidos o nombre),
de modo que el código consume usuario.nombre_mostrar sin saber el tipo.
CREATE TABLE core.identidad.mae_entidades (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tipo_entidad VARCHAR(20) NOT NULL
CHECK (tipo_entidad IN ('PERSONA_NATURAL', 'PERSONA_JURIDICA'))
);
CREATE TABLE core.identidad.mae_personas (
id UUID PRIMARY KEY REFERENCES core.identidad.mae_entidades(id),
identificacion VARCHAR(20) NOT NULL,
nombres VARCHAR(100) NOT NULL,
apellidos VARCHAR(100) NOT NULL,
-- ...resto de columnas y auditoría
);
CREATE TABLE core.identidad.mae_personas_juridicas (
id UUID PRIMARY KEY REFERENCES core.identidad.mae_entidades(id),
nombre VARCHAR(200) NOT NULL,
nombre_comercial VARCHAR(200),
identificacion VARCHAR(20) NOT NULL,
pais_id UUID REFERENCES core.identidad.cat_paises(id),
ciudad_id UUID REFERENCES core.identidad.cat_ciudades(id),
tipo_identificacion_id UUID REFERENCES core.identidad.cat_tipos_identificacion(id)
);
CREATE TABLE core.identidad.mae_usuarios (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
entidad_id UUID NOT NULL REFERENCES core.identidad.mae_entidades(id),
tipo_usuario_id UUID NOT NULL REFERENCES core.identidad.cat_tipos_usuario(id),
-- ...
);
Regla clave: el id no se genera en las hijas; nace en mae_entidades y se
copia a la hija en la misma transacción. Solo mae_entidades y mae_usuarios
tienen DEFAULT gen_random_uuid().
Alternativas descartadas¶
- Dos columnas en
mae_usuarios(persona_idypersona_juridica_id, mutuamente excluyentes) — obliga aif/elseen cada consulta y no escala a nuevos subtipos. - Unificar todo en
mae_personascon campos opcionales — mezcla personas naturales con empresas y ensuciamae_personas, que es exclusiva de cédulas. - Guardar la persona jurídica externa en
mae_empresas—mae_empresases la raíz de tenencia del grupo (BIGINT, RLS); una empresa cliente no lo es. - Mantener el
CHECKdetipo_usuario— agregarexternoexigiríaALTER TABLErevalidando la tabla; un catálogo + FK solo inserta una fila.
Consecuencias¶
mae_usuariosreferencia una entidad y no condiciona por tipo; los módulos consumennombre_mostrarsin ramas.mae_personasymae_personas_juridicasmantienen su auditoría propia; elidde ambas es el mismo demae_entidades.- Los catálogos y la identidad no llevan
empresa_idni RLS (son globales, comomae_personas); se declara explícitamente para no romper el ADR 0004. - La migración
0005_entidadesmigra los datos existentes con el mismo UUID y es reversible (downgrade()). - Agregar un subtipo nuevo (p. ej. persona jurídica interna) es una tabla hija
más y su
polymorphic_identity, sin tocarmae_usuarios.