Skip to main content

Arquitectura del motor

Ubicación: proteus-backend/src/modules/rules-engine/ — el módulo más grande del backend. Sigue el patrón de módulo de feature.

src/modules/rules-engine/
├── core/ # los contratos — leer primero
├── entities/ # 11 entidades
├── services/
│ ├── validators/ # 42 validadores + ruleTypeCatalog.ts
│ ├── context/ # los cortes del EvaluationContext
│ └── practice-sources/ # de dónde salen las prácticas
├── controllers/ # 6
├── dtos/
└── docs/ # glosario por archivo

El contrato: IRuleValidator

core/ define los contratos y es lo primero que hay que leer:

// core/validator.ts
export interface IRuleValidator {
readonly code: string; // ruleTypeCode del catálogo RULE_TYPE
readonly requires: ContextSlice[]; // qué cortes del contexto usa (declarativo)
validate(rule: RuleDefinition, ctx: EvaluationContext): Finding[];
}
validate es una función pura

(rule, ctx) → Finding[]: sin DB, sin efectos, sin red. De ahí que se pueda testear con contextos armados a mano, y de ahí que sumar un tipo de regla sea una clase + registrarla, con cero cambios al núcleo.

Si un validador necesita un dato que no está en el contexto, la solución no es meter una query adentro: es agregar el corte al contexto (services/context/) o crear una medición materializada.

Los enums transversales

core/enums.ts define los ejes (ver Ejes transversales):

EnumValores
SeverityDEBITO, POSIBLE_DEBITO, INFORMATIVO
RevisionScopeEN_EL_DIA, EN_EL_EPISODIO, POR_PERIODO, POR_AUTORIZACION
PatientModalityPROPIO, EXTERNO, TODOS
LimitScopeDIA, MES, ANIO, EPISODIO
DayTypePISO, UTI, UCE, GUARDIA, LIBRE
AuthLevelEPISODIO, PRACTICA, AMBOS
BillingModalityEXTERNO, CAPITADO
EnablementNodeTypepráctica / módulo
Son string enums a propósito

Coinciden 1:1 con los VARCHAR persistidos. No los cambies a numéricos: se rompe la lectura de todo lo ya guardado.

El contexto de evaluación

core/context.ts + services/context/ construyen el EvaluationContext: los cortes de datos que los validadores consumen.

CorteQué trae
PrácticasLas prácticas del episodio / día / período
AutorizacionesLas autorizaciones imputables y su estado
Días de estadíaLa ocupación de camas del episodio
DiagnósticosLos diagnósticos del episodio
PacienteDatos del afiliado: vigencia, sexo, edad, modalidad
ConfiguraciónReglas y configuración de prácticas vigentes para el financiador y la fecha
HabilitacionesLos pares origen → destino
MedicionesLos acumuladores materializados

Cada validador declara qué cortes usa (requires), así que el contexto se puede armar cargando solo lo necesario.

Las piezas de servicio

ServicioRol
RuleValidatorRegistryRegistry in-memory: código → validador
RuleEngineOrquesta: configuración + contexto + validadores → hallazgos
MeasurementService + MeasurementMaterializerMediciones materializadas cross-episodio: lo que permite las reglas por período
BatchEvaluationServiceEvaluación por período (bandeja y débitos históricos)
RuleEvaluationQueueServiceEncola en la cola RULES_EVALUATION
RuleFindingLoggerHistorial de hallazgos (entidad RuleFindingLog)

Entidades

FinancierRule, RuleType, RuleFinding, RuleFindingLog, RuleMeasurement, PracticeFinancierConfig, Enablement, Nomenclador, NomencladorCode, BedDayCoverage, BillingAuditedEntity.

Evaluación asíncrona

La evaluación arrancó corriendo dentro del request y se movió a RabbitMQ + worker.

Si el worker no está levantado, no hay error visible

Los hallazgos simplemente no aparecen. Nada falla ruidosamente. Es el primer sospechoso cuando "el motor no detecta nada". Ver Workers y colas.

El frontend

PiezaQué es
pages/posadas/rules-config/Las 5 pantallas del grupo "Reglas" del menú
components/rules-engine/params-form/Los params de cada regla se editan con formularios por tipo de regla, no como JSON crudo
components/rules-engine/rules-nomenclador-dialog/Drawer de ayuda con el nomenclador de reglas: qué hace cada tipo
rule-findings-panel.componentPanel de hallazgos embebible en el detalle del episodio
Al agregar un tipo de regla
  1. La clase validadora en services/validators/.
  2. Registrarla en RuleValidatorRegistry.
  3. Sumarla al ruleTypeCatalog.ts.
  4. Agregar su formulario de params en components/rules-engine/params-form/.
  5. Documentarla en el nomenclador de reglas del drawer de ayuda.

Gotchas

  • El registry es in-memory: un validador que no se registre no corre, y no falla ruidosamente.
  • Los validadores son puros a propósito. Una query adentro de un validador rompe la testeabilidad y el diseño. El dato va en el contexto o en una medición.
  • Los enums son strings para coincidir con lo persistido. No los toques.
  • El módulo tiene DDL propio que hay que correr antes de levantar el código. Ver DDL y ops.
  • Los permisos del módulo se siembran al boot: si se agregan permisos al catálogo, hay que reiniciar para que existan en la tabla. Ver Permisos y roles.