Lapachos Lending : Documentación del Motor de Reglas de Negocio

1. Visión General

El Motor de Reglas de Negocio es un sistema centralizado y dirigido por datos, diseñado para gestionar y ejecutar lógica de negocio de forma dinámica sin necesidad de cambios en el código ni despliegues. Desacopla la lógica central de la aplicación de las reglas que gobiernan sus procesos, permitiendo una mayor flexibilidad, escalabilidad y un mantenimiento más sencillo.

Objetivos Clave:

  • Agilidad: Modificar la lógica de negocio (ej. criterios de aprobación de crédito, cálculo de comisiones, reglas de validación) simplemente cambiando datos en la base de datos.

  • Centralización: Consolidar las reglas de negocio en un único repositorio manejable.

  • Desacoplamiento: Separar la lógica de negocio del código de la aplicación para una arquitectura más limpia.

  • Adaptabilidad: Permitir que las reglas tengan un alcance específico para diferentes mercados, lenders, países u otros contextos.

El motor está diseñado para ser implementado progresivamente junto a la lógica existente ("legacy"), controlado por una Feature Flag para una transición segura y controlada.

2. Conceptos Fundamentales

El motor se construye sobre conceptos clave que definen cómo se estructura y aplica una regla.

Concepto

Descripción

Modelo de Negocio

Un esquema de alto nivel que define el marco operativo para un lender en un mercado específico (ej. B2B Capital de Trabajo, B2C Consumo). Cada Asociación opera bajo un Modelo de Negocio específico.

Regla de Negocio

La entidad central que contiene la lógica. Vincula un módulo, condiciones, acciones y parámetros. Cada regla tiene un código único, versión y prioridad para gestionar su orden de ejecución.

Módulo

El área o proceso específico del sistema donde se aplica una regla (ej. Gestion Asociaciones con código MODULE_GESTION_ASOCIACIONES).

Procedimiento

Un subproceso dentro de un Módulo. Permite una aplicación más granular de las reglas. Por ejemplo, dentro del módulo Gestion Asociaciones, existen los procedimientos Alta Asociación (PROCEDURE_ALTA_ASOCIACION) y Edición Asociación (PROCEDURE_EDICION_ASOCIACION).

Condición

La prueba lógica que el motor evalúa. Consiste en un campo, un operador y un valor (ej. name, ===, Nulo). Una regla puede tener múltiples condiciones que deben cumplirse.

Acción

El resultado si las condiciones de una regla se cumplen. Ejemplos: Rechaza, Bloquea. La acción incluye un mensaje para ser devuelto al usuario o sistema.

Parámetro

Valores configurables y reutilizables usados en las condiciones o acciones (ej. max_characters: 200). Esto evita hardcodear valores en la lógica de la regla.

Alcance (Scope)

Define el contexto donde una regla aplica. Una regla puede ser global o específica a un país, mercado, lender o asociación.


3. Esquema de la Base de Datos

Toda la lógica del motor de reglas se almacena en un conjunto de tablas relacionadas en la base de datos.

business_rule

La tabla central para cada regla. Conecta todos los demás componentes.

model business_rule {
  id          Int     @id @default(autoincrement())
  code        String  @unique
  name        String
  description String? @db.VarChar(255)

  business_model_id Int
  module_id         Int
  type_id           Int
  procedure_id      Int

  status       Boolean @default(true)
  configurable Boolean @default(true)

  level    Int?
  version  Int @default(1)
  priority Int @default(1)
  
  // ... relations
}
  • priority: Define el orden de ejecución para las reglas dentro del mismo módulo/procedimiento.

  • configurable: Determina si la regla puede ser actualizada.

  • version: Permite un seguimiento histórico de los cambios en las reglas.

Tablas de Soporte

  • business_rule_module: Define los módulos del sistema donde aplican las reglas (ej. code: 'MOD_GES_COM', name: 'Gestion Compradores').

  • business_rule_procedure: Define procedimientos específicos dentro de un módulo.

  • business_rule_type: Categoriza el tipo de regla (ej. 'Regla', 'Validación').

  • business_rule_condition: Almacena las condiciones para una regla.

  • business_rule_parameter: Contiene los parámetros configurables para una regla.

  • business_rule_action: Define la acción a tomar si las condiciones de una regla se cumplen.

  • business_rule_scope: Especifica el alcance de aplicación de una regla (ej. market_id, country_id).


4. Funcionamiento - Detalles de Implementación

4.1. Invocación y Feature Flag

El motor de reglas se inicia a través de la función checkRuleEngine. Antes de la ejecución, se verifica una Feature Flag de GrowthBook para determinar si el motor debe ser utilizado.

  • Feature Flag: USE_RULE_ENGINE

Si la bandera está desactivada, el motor omite la evaluación y el sistema utiliza la lógica legacy.

Ejemplo de Invocación:

import { checkRuleEngine } from "./engine-Rules";

// Objeto de contexto con todos los datos necesarios para la evaluación
const context = {
  name: "", // Nombre de la asociación
  lender_id: 0,
  market_active: true,
  // ... otros campos del formulario
};

// Llamada al motor
const result = await checkRuleEngine({
  context: context,
  associationId: 1, // ID de asociación aplicable
  module: "Gestion Asociaciones",
  procedure: "Alta Asociación",
  level: 1
});

if (!result.valid) {
  throw new Error(result.message);
}

4.2. Obtención de Reglas

Al ser invocado, la clase RuleEngine consulta la base de datos usando getRulesFromDb. Obtiene todas las reglas activas (status: true) que coinciden con module, procedure, y level proporcionados.

4.3. Validación del Contexto

Antes de evaluar las reglas, el motor realiza un paso crítico de validación de contexto. Analiza todas las reglas a evaluar e identifica cada campo variable utilizado en las condiciones (ej. buyer.age). Luego, verifica que estos campos existan en el objeto context pasado durante la invocación.

Información

Esta validación es crucial. Si falta algún dato requerido en el contexto, el motor lanzará un error Missing parameters in context, previniendo comportamientos inesperados o evaluaciones incorrectas.

4.4. Lógica de Evaluación

El proceso de evaluación está diseñado para ser robusto y determinista.

  1. División por Acción: El motor primero filtra las reglas según su tipo de acción.

  2. Rechazo Primero: Evalúa todas las reglas con acción de tipo "reject" (rechaza). Si alguna de estas reglas cumple todas sus condiciones, el motor se detiene inmediatamente y devuelve un resultado { valid: false, message: "..." }.

  3. Accionable Segundo: Si ninguna regla de rechazo fue activada, el motor evalúa las reglas de tipo "actionable" (accionable). Si una de estas reglas se cumple, devuelve un resultado { valid: true, message: "..." }.

  4. Éxito por Defecto: Si ninguna regla se activa, el motor devuelve { valid: true }, lo que significa que el proceso puede continuar.


5. Integración con Frontend: Validación Dinámica con Yup

Una característica clave de esta arquitectura es la capacidad de generar esquemas de validación para el frontend directamente desde las reglas de negocio. Esto asegura que las validaciones en la UI (ej. en un formulario) estén siempre sincronizadas con la lógica del backend.

Esto es gestionado por la función utilidad buildRuleConditions, que construye un esquema de validación Yup.

Cómo funciona:

  1. Las reglas se obtienen de la base de datos, tal como en la evaluación del backend.

  2. La función buildRuleConditions itera a través de las reglas para un campo dado (ej. name).

  3. Traduce la condición de cada regla a su validador Yup correspondiente.

Ejemplos de Mapeo:

Condición de la Regla

Equivalente en Yup

operator: "===", value: "nulo"

.required("Mensaje")

operator: "!==", value: "email_format"

.email("Mensaje")

operator: "<=", parameter: { value: "100" }

.min(100, "Mensaje")

operator: ">=", parameter: { value: "5000" }

.max(5000, "Mensaje")

operator: "!==", value: "regex"

.test("regex", "Mensaje", val => /.../.test(val))

operator: ">=", value: "var_other_field"

Un .test() personalizado que compara el valor del campo con el valor de other_field en el formulario.

6. Anexo: Listados de Referencia

A continuación se encuentran los catálogos actuales de Módulos, Procedimientos y Tipos de reglas definidos en el sistema.

Módulos de Reglas de Negocio

ID

Código

Nombre

1

MOD_GES_COM

Gestion Compradores

2

MOD_GES_PRO

Gestion Proveedores

3

MODULE_GESTION_LENDER

Gestion Lender

4

MODULE_REGLAS_DE_OFERTA_CREDITICIA

Reglas de oferta crediticia

5

MODULE_GESTION_MERCADO

Gestion Mercado

6

MODULE_GESTION_ASOCIACIONES

Gestion Asociaciones

7

MODULE_GESTION_LENDERS

Gestión Lenders

8

MODULE_GESTION_MODELO_DE_NEGOCIO

Gestion Modelo de negocio

9

MODULE_GESTION_RELACIONES_Y_OFERTA_CREDITICIA

Gestion Relaciones y oferta crediticia

10

MODULE_TRANSACCIONAL_FINANCIERO

Transaccional financiero

Procedimientos de Reglas de Negocio

ID

Código

Nombre

28

PROCEDURE_ALTA_ASOCIACION

Alta Asociación

29

PROCEDURE_EDICIN_ASOCIACION

Edición Asociación

45

PROCEDURE_OPERATORIA_INACTIVACION_ASOCIACION

Operatoria Inactivacion Asociación

46

PROCEDURE_OPERATORIA_ACTIVACION_ASOCIACION

Operatoria Activacion Asociación

Tipos de Reglas de Negocio

ID

Código

Nombre

1

TYPE_RULE

Regla

2

TYPE_VALIDATION

Validación


7. Cómo Crear una Nueva Regla de Negocio (Ejemplo Real)

Usaremos un ejemplo real del módulo Gestion Asociaciones: la regla RULE-60001, que valida que el nombre de la asociación sea obligatorio.

  • Regla: "El campo Nombre de la asociación debe ser completado".

  • Módulo: Gestion Asociaciones (ID 6).

  • Procedimiento: Alta Asociación (ID 28).

  • Acción: Rechaza el guardado si la condición se cumple.

Así se registrarían los datos en la base de datos:

  1. Tabla business_rule

    • id: 60001

    • code: "RULE-60001"

    • name: "El campo Nombre de la asociación debe ser completado "

    • module_id: 6

    • procedure_id: 28

    • type_id: 2

    • business_model_id: 1

    • status: true

      (Fuente: gestion_asociaciones_rules.ts)

  2. Tabla business_rule_condition

    • rule_id: 60001

    • field: "name"

    • operator: "==="

    • value: "Nulo"

    • description: "name = Nulo"

      (Fuente: gestion_asociaciones_conditions.ts)

  3. Tabla business_rule_parameter

    • Para esta regla específica, no se necesitan parámetros, ya que el valor de la condición (Nulo) está directamente definido.

  4. Tabla business_rule_action

    • rule_id: 60001

    • action_type: "Rechaza"

    • message: "El nombre nombre de la asociación es requerido"

      (Fuente: gestion_asociaciones_actions.ts)

  5. Tabla business_rule_scope

    • rule_id: 60001

    • Aquí se vincularía la regla a un lender_id, market_id, country_id o association_id específico. Si no se crea un registro en esta tabla para la regla, se considera de alcance global.

Con estos registros, la próxima vez que se llame al motor para el procedimiento de Alta Asociación y el campo name en el context sea nulo o vacío, el motor rechazará automáticamente la operación con el mensaje especificado.

Enlace Postman.

https://developers-4440358.postman.co/workspace/Developers's-Workspace~e0252bea-7d80-43fd-8403-f1234009d032/folder/46003120-4f2ff101-70a9-4329-872d-239477c94538?action=share&source=copy-link&creator=46003120&ctx=documentation

https://developers-4440358.postman.co/workspace/Developers's-Workspace~e0252bea-7d80-43fd-8403-f1234009d032/folder/46003120-b156eaeb-6644-44e8-a674-1deb3d9a2654?action=share&source=copy-link&creator=46003120&ctx=documentation