Reglas en tiempo de ejecución (runtime) en aplicaciones GeneXus.

colors-25
En Concepto desarrollamos aplicaciones con GeneXus, para varios clientes. En las mismas aparecen muchas veces situaciones donde la aplicación debe tomar decisiones basadas en los datos de la misma.
Algunos ejemplos de estos problemas:
  • Documentos que hay que exigir para una determinada operación.
  • A que oficina debe pasar el tramite dependiendo su contenido (monto, producto, etc.).
  • Controles a aplicar a una determinada operativa
  • Descuentos a realizar a una venta (depende del cliente, de la cantidad vendida en el año, la cantidad del pedido actual, etc.)
Esto se puede resolver de varias formas.
Opción 1)
Una fácil, es hacer un programa de la forma (para el último ejemplo)
do case
case TipoCliente='IMPORTANTE'
   &Descuento=20
case FacturaImporte>1000 and TipoCliente='CHICO'
   &Descuento=10
case TipoCliente='CHICO'
   &Descuento=5
otherwise
   &Descuento=0
enddo


Que problemas tiene este enfoque? Cada vez que el departamento comercial de la empresa define una nueva política comercial hay que modificar el código, generando esto una aplicación que no es fácil de adaptar a las necesidades comerciales de la empresa.


Opción 2)
La segunda opción es tener la lógica de los descuentos en tablas
Orden
TipoCliente
Importe
Descuento
1 GRANDE 20
2 CHICO 1000 10
3 CHICO 5
4 0


Este enfoque, mejora la situación mientras los criterios que se manejen en la aplicación para la aplicación de descuentos no cambien. El mantenimiento es realizada por los clientes, posibilitando esto que adapten el sistema a su forma de trabajar.


En este ejemplo solo tenemos 2 atributos a tomar en cuenta, pero si hay muchos atributos los que influyen en el descuento  la complejidad de la evaluación también se complica.En mi experiencia, estos programas pueden tener problemas de performance complicados si hay muchos atributos a evaluar y también muchas reglas.


Un día, llega el gerente comercial, diciendo que lanzó un campaña en la prensa, donde  a todos los clientes que cumplen años en Octubre, reciben un descuento de un 25%. Al agregarse un nuevo criterio (FechaCumpleaños) a la forma de calcular el descuento, se necesitan cambiar la estructura de la tabla y además los programas que lo utilizan. Si bien se mejoró con la opción 1) todavía es mejorable. **


Opción 3)
Utilización de reglas para determinar el descuento. La idea es utilizar reglas, editadas por los usuarios de la aplicación para darle mas flexibilidad a la misma.


La idea es que ellos puedan ingresar reglas del tipo:


Descuento=0
REGLA1:
SI TipoClient='GRANDE'  ENTONCES
   Descuento=20
FINSI
REGLA2
SI TipoCliente='CHICO' Y IMPORTE>1000 ENTONCES
   Descuento=10
FINSI
REGLA3:
SI TipoCliente='CHICO' ENTONCES
   Descuento=5
FINSI


Durante un tiempo estuvimos analizando diferentes BRMS (business rule manager system) como Drools, Drools.NET, InRule, NxBRE, OpenRules, ILog. Todos tiene sus pros y contras. El mas profesional y barato de todos es Drools que tiene mucho respaldo y esta bien documentado. En el Encuentro GeneXus del año pasado Luciano dio una charla sobre el tema, de como usar reglas dinámicas en aplicaciones Genexus. El problema es que usaba Drools que está en Java y nosotros necesitábamos una solución para .NET pues es la plataforma que domina el cliente donde iba a ser instalado. No queríamos complicar el deployment. Alejandro Rinaldi hizo un muy buen trabajo evaluando diferentes manejadores de reglas, logrando prototipos interesantes con InRule, NxBre y varios otros. Había uno que era muy caro, otros exigían editar las reglas con Microsoft Visio.


Alejandro Rivoir tuvo una buena idea e hizo una implementación de prueba que funcionó bien. La idea era codificar las reglas en XSLT. Este es un lenguaje completo que se escribe en un archivo XML y permite aplicar transformaciones a archivos XML.


Para el ejemplo, la factura de entrada quedaría así:


<Parametros>
<Factura>100</Factura>
<Importe>1567</Importe>
<ClienteId>CONCEPTO</ClienteId>
<ClienteTipo>GRANDE</ClienteTipo>
<Fecha>2010-01-01</Fecha>
</Parametros>


Como quedarían estas reglas codificadas en XSLT?


<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/>
 <xsl:template match="/">
  <Resultados>
   <xsl:apply-templates/>
  </Resultados>
 </xsl:template>
 <xsl:template match="PARAMETROS[TipoCliente = 'GRANDE'">
            <Descuento>20</Descuento>
 </xsl:template>
 <xsl:template match="PARAMETROS[TipoCliente = 'CHICO']">
  <xsl:if test="Importe > 1000">
   <Descuento>10</Descuento>
  </xsl:if>
 </xsl:template>
 <xsl:template match="PARAMETROS[TipoCliente='CHICO']">
  <xsl:if test="Importe > 100">
   <Descuento>5</Descuento>
  </xsl:if>
 </xsl:template>
 <xsl:template match="PARAMETROS">
     <Descuento>0</Descuento>
 </xsl:template>
 <xsl:template match="text()|@*"/>
</xsl:stylesheet>


En GeneXus se aplicaría la función XSLTApply() para aplicar al xml de entrada el xslt que lo transformaría en un xml de salida, que quedaría así:


<Resultados>
<Descuento>20</Descuento>
</Resultados>


Que ventajas tiene esta solución frente a las anteriores?
  • Todas las plataformas que usamos (.net,java, ruby) tienen manejo de xml y xslt, por lo que la solución es multiplataforma. También es una tecnología  muy probada y standard, por lo que no deberíamos tener sorpresas grandes.
  • Da muy poco trabajo para programar.
  • Permite con un costo bastante bajo la ejecución de reglas en el momento en runtime.
Que nos falta para mejorar esta implementacion?


Es mas lenta. Estaría bueno que se pudieran compilar los archivos XSLT y hacerlos mas rápidos.


Hay que hacer un editor de reglas amigable para el usuario que genere el xslt. Hacer un editor genérico de reglas de usuario es un lindo proyecto. La generación del XSLT es la parte fácil.


Hay un problema en el generador .NET que hace que cuando se abre por segunda vez un xml con OpenFromString() mantiene el contenido viejo, duplicando todo. Esto deberia poder solucionarse, lo cual posibilitaría hacer mas rápida esta implementación.


Tenemos un problema con el XSLTApply() para XML con namespaces, que hay que resolver. No analice aun si el problema es que la funcion no toma en cuenta el namespace o si falta derfinir algo en el xslt. 


Conclusión


A mi me tiene muy entusiasmado la solución encontrada. Posibilita que a las transacciones tengan un conjunto de reglas dinámicas. Si mezclamos los Business Component de GeneXus (pasándolos a xml con la función ToXml() ) y un editor de reglas que tomo como entrada los atributos de la transacción que permita generar XSLT con reglas, se pueden hacer cosas realmente interesantes. Hay que seguir investigando.


Se les ocurre algo para mejorarlo o hacerlo mas facil?







** se pueden almacenar los datos como metadatos, facilitando el utilizar nuevos campos, lo cual perjudica la performance, pero da mas flexibilidad.


UPDATE: Links que pueden servir para los que están interesados en el tema
EditArea UserControl
UfProject

Comentarios

  1. Hola.
    Quizá te sirva el ufProject como base, o ejemplo, para convertir esas declaraciones a XSLT.

    Aunque la idea original de ufProject es desarrollar con GX un intérprete de un lenguaje sencillo, para reglas o lo que sea:
    if &TipoClient='GRANDE'
    &Descuento=20
    elseif &TipoCliente='CHICO' and &IMPORTE>1000
    &Descuento=10
    elseif &TipoCliente='CHICO'
    &Descuento=5
    else
    &Descuento=0
    endif

    Otro enfoque en ufProject va por el lado de usar algún motor Javascript (server-side):
    if (TipoClient=="GRANDE")
    {Descuento=20;}
    else if (TipoCliente=="CHICO" && IMPORTE>1000)
    {Descuento=10;}
    else if (TipoCliente=="CHICO")
    {Descuento=5;}
    else
    {Descuento=0;}

    También te puede interesar el UC EditArea. Es un editor con coloreo de sintaxis al que puedes incorporar tus propias definiciones.

    salu2!!!

    ResponderEliminar
  2. Miguel:
    Conozco tus proyectos. Han aportado mucho para tener formulas dinamicas en aplicaciones Genexus.

    El UC EditArea, no lo conocia y me puede llegar a servir, cuando migremos a la X. Esto aun esta en la 9.0.

    Gracias por el comentario y voy a agregar links a tus sugerencias en el post, para que quede como referencia.

    ResponderEliminar
  3. Muy pero muy bueno el post Enrique, me parece mas que interesante la solución encontrada.

    Quería comentar otra diferencia entre las dos primeras opciones y la tercera. Cuando se tiene un software que se vende a muchos clientes, cada cliente tiene su customización. Si esta customización se hace utilizando alguna de las dos primera opciones terminas instalando en los clientes un monton de lógica que posiblemente nunca se ejecute y que hace el sistema más complejo y menos mantenible. En cambio en la opción 3 podés facilmente dejar solo las reglas que le interesa al cliente.

    Capaz que agarré para cualquier lado, pero esto me hizo acordar al tema de Software Product Lines y justament me parecio una muy buena alternativa para customizar el software de acuerdo al cliente.

    Me parece un excelente aporte para la comunidad!

    Arriba!

    ResponderEliminar
  4. Matias:
    Es verdad lo que decis. Algunas veces, con la personalizacion de la solucion, solo se ejecuta una parte del codigo, haciendo la aplicacion mas grande de lo estrictamente necesario. Si se pudiera personalizar mas del lado del usuario, este problema se minimizaria (agregandose otros nuevos!!). De cualquier forma, el objetivo primario de esto, no es tener aplicaciones mas chicas sino que sean mas adaptables.

    Gracias por el comentario.

    ResponderEliminar
  5. Desde hace años el tema está dando vueltas en nuestras cabezas. Creo que se plantean dos problemas. Por un lado, la posibilidad de que los usuarios modifiquen sus reglas "a piacere" y por el otro, la performance de la implementación.

    Sobre la posibilidad de que los usuarios modifiquen sus reglas, lo imagino como "sencillo". Basta dar un lenguaje acorde a los usuarios y un mecanismo que lo interprete. Hasta podría ser "redactado" en lenguaje natural.

    Sobre la performance veo, a su vez, dos temas.

    Por un lado, calcular sólo el valor de las variables involucradas en la decisión. Ver que para aplicar un XSL necesito todas las variables posibles (en todos mis clientes) y si al gerente se le ocurre otra voy a tener que agregarla por código (también a todos mis clientes). Si hay una variable que el XSL de algún cliente no se utiliza, el cálculo (y su peso en la performance) fue al santo botón.

    Por otro lado, el cómo se hace la evaluación (XSL, Interprete, etc.). Me da la impresión que esto es "menor" y, si no se hacen barbaridades, prácticamente cualquier solución podría dar buenos resultados.

    Por dónde pienso una solución (sin una clara definición)? Por el lado de expresar las reglas de alguna forma, analizarlas, ver qué variables necesitan y generar el código que las obtenga de la DB o de rutinas específicas ya que algunos pueden tener su lógica particular.

    Lo que siempre se ha criticado de esta solución es que "ninguna empresa" permite que se genere código en su ambiente de producción. Claro que, en cierta forma, todas las soluciones generan o interpretan código de forma menos visible. El "pecado" de ésta es que lo dice :-).

    ResponderEliminar
  6. Anonimo:
    Por el comentario, parece que estas dentro del grupo de desarrollo de GeneXus.

    Entiendo los problemas planteados y creo que se podrian ir solucionando a medida que tengamos algo implementado que funcione. Ahi vamos a poder medir mejor estos problemas y solucionarlos.

    Contesto por partes. En cuanto a que los usuarios puedan modificar las reglas, creo que hay que limitarlos a determinados casos y ademas podrian limitarse el disparo de reglas, a determinados momentos (por ejemplo, after validate o after trn). Esto es discutible..

    En cuanto a que tipos de reglas tener, creo que para empezar reglas del tipo:

    If (condicion) entonces comando

    y tener comandos con asignacion de variables o call que manipulen el xml alcanzan para empezar.

    Performance.
    Por el lado de las variables, para empezar empezaria con TODAS las que ya tengo en la transaccion que tome como base.
    Si al cliente se le ocurre utilizar otra, tengo que modificar la transaccion, pero es algo con lo cual puedo convivir para empezar.

    En cuanto a lo de que utilizar para interpretar y ejecutar las reglas, estoy de acuerdo que son solo un tema menor y podriamos tener cosas mejores.

    Otra cosa que me gustaria agregar (no lo puse en el blog para no hacer tan largo), seria la evaluacion del orden de las reglas, pues podrian tener precedencia unas sobre otras, pudiendose necesitar disparar una antes que las otras. Los BRMS manejan algoritmos sofisticados para esto.

    En cuanto a la generacion de codigo en los ambientes de produccion, trae nuevos problemas, como puede ser seguridad y demas. Creo que esto puede solucionarse de diversas formas. La que mas me gusta, es hacer una primera aproximacion de un lenguaje de reglas limitado que permita hacer pocas cosas y no le de muchas opciones al usuario a meter la pata.

    Lo que estamos buscando ahora, es una solucion "good enough" que pueda evolucionar en el futuro.

    En los sistemas grandes que conozco, habria al menos unos 10 ejemplos donde este tipo de reglas nos ahorraria trabajo. Creo que eso ya justifica el trabajar en el tema.

    Se que somos varios trabajando en lo mismo. La idea del post es tratar de crear masa critica para resolver el problema entre todos.

    ResponderEliminar
  7. Efectivamente, el del último anónimo, es del equipo de desarrollo de GeneXus. Soy Proto.

    Si te parece, andá tirando alguno(s) de los 10 ejemplos. Suelen ser la mejor manera de entender los problemas y su solución.
    Saludos.

    ResponderEliminar
  8. Gustavo:
    Algunos de los problemas que pueden solucionarse con estas reglas.

    1) El sistema aduanero, recibe mensajes en formato XML para declaraciones aduaneras (importacion,extporacion, etc). Estos mensajes estan compuestos por bloques (Cabezal, Lineas, Facturas, Contenedores, Medio de transporte, Documentos, etc). Estos bloques, son obilgatorios para unas operativas, o para algunos productos y no para otros. Esto tambien incluye que campos son obligatorios o no dependiendo si es un mensaje de alta o un mensaje de modificacion.

    2) Calculo de Regimen Precedente. Hay declaraciones que solamente son permitidas si hay una operacion anterior que las hagan viable. Por ejemplo, se puede hacer una Exportacion , tiene que existir una Admision Temporaria previa. Esto cambia mucho de pais a pais y tambien con el tiempo.

    3) Calculo de Comisiones por Ventas. Dada una Venta, calcular cual es la comision que se le va a pagar al vendedor. Entran las ventas pasadas, las condiciones de la venta actual.

    4) Calculo de Descuentos a Clientes. Al realizar un pedido, calcularle que descuento va a tener esa venta, respetando los acuerdos comerciales que tenga ese cliente con la empresa.

    5) Documentos obligatorios. Dada una operacion aduanera de importacion, dar una lista de todos la documentacion que se necesita exigir, en los diferentes momentos del tramite. Si voy a importar armas, necesito un certificado de las FFAA. Si voy a importar combustibles, tengo que presentar documentos de Ancap. Si son alimentos, son documentos bromatologicos. Algunos son al momento de hacer la declaracion, otros al retirar la mercaderia.

    Con eso, creo que hay para divertirse.

    ResponderEliminar
  9. creo que la idea basica es.. tengo un control, pero no se cuando se quiere ejecutar. El que sabe, es el usuario en la instalacion final. Y de hecho, ni quiero enterarme.

    ResponderEliminar

Publicar un comentario

1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.

Entradas más populares de este blog

El Sordo

StackOverflow Documentation

Codigo simple