Introducción
Procedimiento de evaluación de las Reglas
Control de dependencias de Reglas (Forward Chaining Control)
Separar las Reglas de Negocio del Assembly
Referencias
Introducción
La tecnología DotNet Framework 3.0 tiene un componente para implementar Workflow’s[0]
llamado Windows Workflow Foundation [1]. Este no es un producto de Workflow sino un motor
de workflow más herramientas para los desarrolladores para que construyan sus propias
soluciones de Workflow.
Dentro de las cosas más interesantes que veo en WF se encuentra su motor de Reglas de
negocio (Rule Engine)[2].
Rules Engine permite hacer desde evaluación de condiciones lógicas hasta la definición y
evaluación de complejas reglas que conforman políticas. En este artículo voy a tratar de
explicar cómo se implementan esas políticas y cómo podemos usarlas en nuestros Worklows.
Lo primero que hay que entender es que las pol íticas son definidas por un conjunto de
reglas semánticas llamado RuleSet. Estas reglas son del estilo If/Then/Else. Para poder invocar
desde un Workflow la evaluación de un RuleSet se usa la actividad Policy[3].
En este artículo revisaremos como es el procedimiento de evaluación de las reglas
y sus dependencias. Después de eso, vernos como tomar el control de las secuencias
de evaluación dando así mayor control sobre las reglas a los desarrolladores.
Por último veremos un ejemplo de cómo desacoplar las reglas de negocio del Workflow,
sacándolas del Assembly de este.
Los ejemplos de este artículo se encuentran en las referencias [5]
y [6]
Procedimiento de evaluación de las Reglas
Las Reglas que componen un RuleSet tienen una propiedad llamada Priority,
que permite asignarle una prioridad a cada regla. El procedimiento de Evaluación
del RuleSet es el siguiente:
1. La regla de mayor prioridad se evalúa y ejecuta la acción del Then o Else.
2. Si la acción (Then o Else) cambia el valor de un campo o propiedad que es
usado por una regla anterior en la lista de reglas, esa regla afectada por el
cambio se re evalúa.
3. La siguiente regla de la lista se evalúa y ejecuta la acción del Then o Else.
Veamos el siguiente ejemplo. Tenemos un Workflow sencillo de 3 Actividades,
el que se muestra en la imagen 1. La primera actividad muestra los valores
de las variables A, B, C, D, E en la consola. La segunda actividad es una
Política que tiene 4 reglas que están priorizadas y la última muestra los valores
después de aplicar el RuleSet de reglas.

Imagen 1: Workflow de ejemplo y RuleSet
El RuleSet se ve en la imagen 1. Las reglas se ejecutan desde la
mayor prioridad (4) hasta la menor (1). El proceso en que se ejecutan
las reglas es el siguiente:
1. Regla_4: Falso
2. Regla_3: Verdadero entonces B = 10.
3. Regla_2: Verdadero entonces A=15.
a. Como cambio A la Regla_4 se re evalua. Regla_4: Verdadero entonces
B=5.
4. Regla_1: Verdadero entonces E=7.
Quedando cuando se ejecutan los valores mostrados en la imagen 2
en color Rojo.

Imagen 2: Resultados de la evaluación.
Las dependencias entre las reglas son identificadas de tres maneras
por el Rules Engine:
1. Dependencia implícita:
Este tipo de dependencia es la que vimos en el ejemplo 1, donde
las acciones cambian valores que se usan en otras reglas.
2. Dependencia por atributos:
La dependencia por atributos es usado en reglas que llaman a métodos
en el Workflow. Como llama a un método no tiene como deducir
implícitamente que reglas debe "Re Evaluar". Para
usar este tipo de dependencia se debe usar uno de estos tres atributos
en el método:
. RuleRead: Este quiere decir que el método lee un
valor usado en el RuleSet
. RuleWrite: Este atributo indica que en ese método
el valor indicado es modificado.
. RuleInvoke: Este atributo le indica al motor que el método
llama a otro método.
Veamos un ejemplo para que sea más simple de entender. Tenemos
un Workflow igual al anterior pero con el RuleSet mostrado en la
imagen 3. Las reglas son las mismas que antes, pero en vez de cambiar
los valores de las propiedades A, B, C, D y E directamente en la
acción de la regla se invoca a un método.
El código de cada uno de los métodos invocados desde
el RuleSet se muestra en el código 1.
Pueden ver en la imagen 4 que el resultado de la evaluación
es el mismo que en ejemplo de reglas implícitas porque se
hacen los mismos cambios de valores pero en métodos decorados
con los atributos que le dan al motor de regla la información
necesaria para actuar.

Imagen 3: RulSet con dependencia por atributos.
3. Dependencia Explicita
La dependencia explicita se basa e declarar uno mismo los cambios
que ocurren al ejecutar una acción. Para ello se usa la instrucción
Update de esta forma Update("this/A") por ejemplo para
indicarle al motor que el valor de la propiedad A cambio.
La única ventaja que he encontrado en la dependencia explicita
es que se puede hacer por ejemplo esto Update("this/OrdenCompra/*")
que le indica al motor que todos los atributos de la orden de compra
cambian con esa acción. Esto es notable!!.
El próximo ejemplo se muestra en la imagen 4. Las reglas
contienen ahora en la acción la directiva Update. El código
de los métodos ya no tiene la directiva RuleWrite, por lo
que el motor se entera del cambio de manera explícita. El
código de los métodos de cambio, sin el atributo,
se muestra en el código 2.
Por último el resultado sigue siendo el mismo y se muestra
en la imagen 5.

Imagen 4: RuleSet con dependencia explicita.
[RuleWrite("B")]
private void CambiarB(int valor)
{
this.B = valor;
}
[RuleWrite("A")]
private void CambiarA(int valor)
{
this.A = valor;
}
[RuleWrite("E")]
private void CambiarE(int valor)
{
this.E = valor;
} |
C ódigo 2: Métodos sin atributos de dependencia.

Imagen 5: Resultado de la evaluación usando dependencia explicita.
Control de dependencias de Reglas (Forward Chaining Control)
Ahora revisaremos cómo se puede controlar la ejecución
de reglas atómicas del RuleSet sin dependencia entre ellas.
La idea es que en algunos escenarios complejos, quien escribe las
reglas necesita tener mayor control sobre el comportamiento de las
re evaluaciones, específicamente limitar la cantidad de reevaluaciones.
Con esto se busca evitar loops infinitos, resultados erróneos
por iteraciones de las reglas y algo no menor, mejorar el desempeño.
En nivel de control que permite Windows Workflow Foundation se puede
configurar en dos puntos:
1. Conjunto de Reglas (RuleSet): con la propiedad Chaining Behavior.
2. En cada Regla (Rule): con la propiedad Reevaluation Behaivor.
3. Chaining Behavior
Esta propiedad actúa a nivel de RuleSet, y tiene las siguientes
tres posibilidades:
1. Full Chaining: este es el comportamiento por defecto que está
descrito en el primero post de esta serie. Este es el comportamiento
por defecto, que se mostró en la primera sección de
este artículo.
2. Explicit Update Only: está opción deshabilita
las dependencias implícitas y por atributos, dejando la posibilidad
solo a las explicitas declaradas usando Update.
Un ejemplo sería usando el RuleSet del código 3 y
los métodos del código 4 utilizando Chaining Behavior
con el valor por defecto Full Chaining el valor de salida de las
variables A, B, C, D y E son 15, 5, 5, 2 y 7 respectivamente.
Ahora si se cambia la propiedad Chaining Behavior a Explicit Update
Only, el resultado cambia a 15, 0, 5, 2, 0. Esto ocurre porque en
este modo de se ignoran las dependencias declaradas como atributos.
3. Sequiential: esta es la opción final y lo que hace es
forzar a que el motor realice las evaluaciones en un orden lineal
estricto, es decir no re evalúa las dependencias.
Si al ejemplo anterior, le cambiamos el valor a Sequential, entonces
obtenemos el resultado 15, 0, 5, 2 y 0 porque no hay re evaluación
de la Regla 4.
Reevaluation Behavior Property
Está propiedad se aplica a nivel de Reglas y permite la re
evaluación o no de la regla. Los valore posibles son:
1. Always: es el comportamiento descrito hasta ahora y el que viene
por defecto.
2. Never: al marcar una regla con Never está ejecutará
las acciones THEN o ELSE sólo una vez según corresponda.
En el ejemplo anterior, si la Regla 4 es marcada con Never entonces
los valores de salida con 15, 0, 5, 2 y 0 porque la Regla 4 no se
re evaluó.
-
Regla_1: IF this.B == 5 THEN this.CambiarE(7)
-
Regla_2: IF this.D == 2 THEN this.CambiarA(15)
-
Regla_3: IF this.C == 15 THEN this.CambiarB(10)
-
Regla_4: IF this.A == 15 THEN this.CambiarB(5)
|
Código 3: RuleSet ejemplo Explicit Update Only
private int A = 0;
private int B = 0;
private int C = 5;
private int D = 2;
private int E = 0;
///....código completo en el proyecto de ejemplo
[RuleWrite("B")]
private void CambiarB(int valor)
{
this.B = valor;
}
[RuleWrite("A")]
private void CambiarA(int valor)
{
this.A = valor;
}
[RuleWrite("E")]
private void CambiarE(int valor)
{
this.E = valor;
} |
Código 4: Métodos usados ejemplo Explicit Update Only
Para poder obtener el mismo resultado que en la primera prueba tenemos que cambiar las reglas del RuleSet como se muestra en el código 5.
- Regla_1: IF this.B == 5 THEN this.CambiarE(7)
- Regla_2: IF this.D == 2 THEN this.CambiarA(15) Update("this/A")
- Regla_3: IF this.C == 15 THEN this.CambiarB(10)
- Regla_4: IF this.A == 15 THEN this.CambiarB(5)
|
Código 5: RulSet con Update explicito.
Al poner la dependencia de forma explícita entonces el motor de reglas cuando evalúa la regla 2, re evalúa a regla 4. El resultado obtenido es 15, 5, 5, 2 y 7 respectivamente.
3. Sequiential: esta es la opción final y lo que hace es forzar a que el motor realice las evaluaciones en un orden lineal estricto, es decir no re evalúa las dependencias.
Si al ejemplo anterior, le cambiamos el valor a Sequential, entonces obtenemos el resultado 15, 0, 5, 2 y 0 porque no hay re evaluación de la Regla 4.
Reevaluation Behavior Property
Está propiedad se aplica a nivel de Reglas y permite la re evaluación o no de la regla. Los valore posibles son:
1. Always: es el comportamiento descrito hasta ahora y el que viene por defecto.
2. Never: al marcar una regla con Never está ejecutará las acciones THEN o ELSE sólo una vez según corresponda.
En el ejemplo anterior, si la Regla 4 es marcada con Never entonces los valores de salida con 15, 0, 5, 2 y 0 porque la Regla 4 no se re evaluó.
Función Halt
Está función es muy interesante porque permite controlar
la ejecución de dependencias entre reglas relacionadas. Al
incluir Halt en la acción THEN o ELSE, entonces el RuleSet
retorna de manera inmediata el control al código que lo invocó.
Separar las Reglas de Negocio del Assembly
Hasta ahora, hemos visto que las reglas de negocio se encuentran
compiladas dentro del Assembly del Workflow. La consecuencia de
esto es que es necesario para cambiar las reglas volver a compilar.
Esto es un contra sentido, porque la idea de desacoplar las reglas
de código de programación es poder tener una separación
clara del código del Wrokflow y las reglas de Negocio.
Ahora se explicará cómo usar una pieza de software
de terceros que permite poner el RuleSet de negocio en una base
de datos y así cambiar las reglas sin volver a compilar.
El software a utilizar en esta sección es "External
RuleSet Toolkit" [4]. Este código es de ejemplo y muestra
como almacenar en SQL el RuleSet. Además trae una interfaz
Windows Forms (RuleSet Designer) para modificar las reglas en la
DB y así los Workflow no son alterados por cambios en las
reglas de negocio.
Lo primero que hay que entender es la arquitectura de esta solución.
En la ilustración 1 muestra un WorkflowRuntime que tiene
3 instancias de Workflow corriendo. Cada una de esas instancias
tiene una Custom Activity llamada "Custom Policy Activity",
que se encarga de obtener el RuleSet desde la base de datos a través
del servicio "RuleSet Services". Este Servicio debe ser
agregado al WorkflowRuntime para que esta actividad de Políticas
cutomizada pueda comunicarse con el RuleSet Repository que está
en la base de datos.
 Ilustración 1: modelo de External RuleSet Toolkit
Además de los componentes para el Workflow este ejemplo de código trae el RuleSet Designer que es una interfaz gráfica para administrar los elementos del RuleSet Repository. Esta interfaz se muestra en la imagen 6.
 Imagen 6: RuleSet Designer
¿Cómo se Instala?
Para poder usar el componente se deben cumplir con el proceso de instalación. Estos no los voy a explicar porque están perfectamente detallados en el manual de instalación de este ejemplo de código.
Además de los componentes para el Workflow este ejemplo
de código trae el RuleSet Designer que es una interfaz gráfica
para administrar los elementos del RuleSet Repository. Esta interfaz
se muestra en la imagen 6.
Ejemplo de Uso 1
Para usar este componente vamos a importar el RuleSet que usamos
en el primer ejemplo artículo. Como sabemos las reglas de
guardan en un archivo de extensión .rule. Usando la aplicación
RuleSet Designer vamos a importar el archivo wfReglasEjemplo_1.rule
que está en el proyecto de ejemplo de este artículo.
Una vez importado grabamos el RuleSet para que así quede
persistente en la base de datos. Las reglas son las siguientes:
-
Regla_4: IF this.A == 15 THEN this.B = 5
-
Regla_3: IF this.C == 5 THEN this.B = 10
-
Regla_2: IF this.D == 2 THEN this.A = 15
-
Regla_1: IF this.B == 5 THEN this.E = 7
|
Código 6: Reglas del RuleSet
Ahora, creamos un Workflow secuencial y le agregamos la actividad
customizada PolicyFromService que es la que enlaza con las reglas
de la base de datos a través del Servicio. El workflow muestra
en la imagen 7.
 Imagen 7: Workflow con Custom Policy Activity
Las propiedades de la actividad PolicyFromService son:
1.- RuleSet Name: nombre del juego de reglas a usar.
2.- Major Version: numero de versión.
3.- Menor Version: número de sub versión.
EL RuleSet de nuestro ejemplo se llama Rule Set1. La versión
es la por defecto 1.0.
Ahora para que esto funcione debemos incluir el servicio de comunicaciones
con el repositorio de la reglas en la base de datos. Para eso al
runtime del Workflow agregamos el Servicio RuleSet Services. Para
esto agregamos código 7 donde se agrega al Runtime el servicio
RuleSetService.
workflowRuntime.AddService(new RuleSetService()); |
Código 7
Con esto tenemos listo nuestro ejemplo. Si lo ejecutamos obtenemos el resultado A=15, B=5, C=5, D=2 y E=7. Las reglas para hacer los cambios de los valores los leyó desde la base de datos.
Ejemplo de Uso 2
Ahora, vamos a cambiar los valores para comprobar que descaplamos
las reglas del Assembly. Para ello usamos la herramienta RuleSet
Designer. Lo que haremos es mantener dos versiones de las reglas,
así dependiendo de un valor usaremos una u otra regla.
Primero creamos una copia de las reglas, para eso usamos la función
copy. Al hacer eso, nos aparece una nueva versión que por
defecto es la versión 2.0. Usando la opción de Edit
Rule, nos aparece el mismo formulario usamos antes para cambiar
las reglas de Rule Set.
Si cambiamos las reglas y las grabamos en la base de datos el workflow
inmediatamente comenzará a evaluar con el nuevo set de Reglas.
Los valores de las reglas del segundo ejemplo se muestran en el
código 8.
-
Regla_4: IF this.A == 15 THEN this.B = 5
-
Regla_3: IF this.C == 5 THEN this.B = 10
-
Regla_2: IF this.D == 2 THEN this.A = 15
-
Regla_1: IF this.B == 5 THEN this.E = 7000
|
Código 8: RuleSet ejemplo 2.
Las Reglas versión 2.0 se muestran en la imagen 8.
 Imagen 8: RuleSet Versión 2.0
Los resultados obtenidos al usar las reglas 2.0 son A=15, B=5,
C=5, D=2 y E=7000, y para esto no tuvimos que compilar.
-----------------------------------------------------------------------------------------------------------------------
Juan Pablo García
Juan Pablo García es MVP desde 2006; estudió ingeniería civil electrónica, física moderna, gestión informática y tiene una maestría en tecnologías de la información. Lleva 10 años trabajando en la industria de desarrollo de software, actuando como desarrollador, líder técnico, arquitecto de software y gerente de desarrollo.En el último tiempo su foco de trabajo han diso los temas de integración y arquitectura orientada a servicios. Blog: http://liarjo.spaces.live.com/
Referencias
|