¿Hasta dónde llega el motor de Workflow del .net Framework 3.0?

Esta pregunta me la he hecho muchas veces durante los últimos años, ya que mi pasado está muy ligado a construir soluciones y herramientas de Workflow y en este artículo intento transmitirles alguna de las formas en que con el tiempo, la implementación de WF, me ha llegado a convencer de que sus limitaciones no son tales.

Abordemos el que parece ser el problema mayor de la implementación, tomando como base los patrones de Workflow concebidos por las universidades de Eindhoven  (Eindhoven University of Technology) y de Queensland (Queensland University of Technology), las cuales han creado un repositorio de 43 patrones de Workflow (ver: https://www.workflowpatterns.com/), los cuales sirven para evaluar las capacidades de los diversos sistemas de Workflow.  (para los que deseen introducirse en el concepto de workflow, en esta página he publicado algunos conceptos básicos de workflow: https://blogs.msdn.com/pcgarcia/pages/capitulo-1-conceptos-de-workflow.aspx)

De estos patrones, voy a tomar dos que a primera vista aparecen como insalvables, los cuales son:   "Ciclos arbitrarios" y "Discriminador estructurado". La implementación de Workflow Foundation tiene dos restricciones que hacen imposible implementar estos patrones. La primera de ellas es que una actividad siempre está contenida en otra, por lo tanto, solo puede conectarse con su actividad madre, con sus hermanas y/o con sus hijas. La segunda restricción tiene que ver con que no existe el concepto de thread de ejecución en Workflow Foundation y si bien el concepto de "Execution Context" se le aproxima mucho, está sujeto a una limitación impuesta en el runtime y es que al finalizar una actividad, el runtime fuerza la finalización de todas sus actividades hijas, con lo cual, si queremos despachar la actividad que sigue a la actual, se cancelarán todas las actividades hijas de la actual que estén en estado de ejecución (el despachador no le dará ni oportunidad de ejecutar porque una actividad en estado de cancelación tiene prioridad en la cola de despacho sobre las que están esperando para ejecutar).

Bien, formalicemos un poco estos patrones y analicemos si existen equivalentes funcionales que nos permitan obtener las mismas prestaciones del motor de workflow a pesar de las restricciones descriptas.

Structured Discriminator (ver: https://www.workflowpatterns.com/patterns/control/advanced_branching/wcp9.php )

Este patrón se utiliza cuando tenemos una separación en hilos de ejecución paralelos (Split y Join) donde queremos que al llegar uno de los hilos de ejecución al join se active la tarea que prosigue al join y que las actividades que se encontraban pendientes de ejecución o activas en los demás hilos de ejecución continúen siendo ejecutadas en forma normal.

Podemos graficar esta operación de la siguiente manera:

Structured Discriminator

 

 

 

 

 

 

 

 

 

 

 

Consideremos las Actividades en verde como actividades activadas (ejecutando) y las azules como ya terminadas. En esta secuencia se ilustra como ejecuta este patrón, donde luego de completarse la actividad A, se pasa por el join y se activa la actividad C, quedando activa la actividad B. Cuando la actividad B se termina, no se vuelve a activar la actividad C.

¿Cómo compatibilizamos este hecho con la aseveración anterior de que al terminar una actividad madre (por ejemplo, el Split-join) el motor de workflow foundation se asegura de que no quede ninguna actividad hija ejecutando?

Planteemos el caso inicial en un Workflow Secuencial de Workflow Foundation y veamos como lo podemos "Refactorizar" para obtener un funcionamiento equivalente al patrón presentado.

Structured discriminator 01

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Aplicando una refactorización obtenemos el siguiente workflow:

 

Structured Discriminator Refactorizado 

Técnicamente, podemos implementar esta solución con un par de actividades custom, una de envío de la señal de sincronización y otra de espera por la señal, la cual puede estar basada en el uso del mecanismo de colas internas que posee Workflow Foundation y en  el uso de una propiedad con la cual hacer metadata binding de forma de asociar el destino de la señal enviada por las actividades SendSync con la actividad WaitforSync. El mecanismo de comunicación usado puede limitarse simplemente a crear una cola con el nombre de la actividad (el cual es clave dentro del workflow) en el método Initialize de la actividad WaitForSync y en cada una de las actividades SendSync simplemente introducir una novedad en la cola (no importa el contenido).

 

 

 

 

 

 

 

 

 

 

Si queremos llevar este patrón a una resolución gráfica que presente el mismo nivel de transparencia en el orden de ejecución de las actividades que presentan los mini-diagramas presentados anteriormente, tendremos que construir nuestra propia actividad "StructuredDiscriminatorActivity", la cual debe ser funcionalmente igual al proceso que acabamos de presentar, pero presentando un diseño gráfico compuesto de una actividad de ejecución en paralelo, donde colocar las actividades que se ejecutarán en paralelo con la capacidad de continuar ejecutando cuando uno de los hilos de  ejecución alcance el join, continuada por una actividad de secuencia, donde colocar la parte correspondiente al resto del workflow que continúa a posteriori del join. De esta forma, la actividad contenedora será una sola y al estar las actividades hijas contenidas en el mismo nivel, podremos mantener estas activas cuanto tiempo sea necesario.

 

Analicemos ahora un segundo patrón de workflow (quizás el más complicado de resolver con WF).

Arbitrary Cycles (ver: https://www.workflowpatterns.com/patterns/control/structural/wcp10.php )

Este patrón nos dice que dentro del flujo del proceso deben de poderse implementar conexiones que formen ciclos o bucles sin un constructor específico. Un ejemplo sencillo se ve de la siguiente forma:

Arbitrary Cycles

 

En este ejemplo, se forman dos ciclos (B - C - D.Else) y (C - D - E - F.Else) los cuales están cruzados entre sí y la importancia de este patrón radica en lo simple que es construir un workflow sin tener que utilizar un constructor específico (si quieren una notación específica) para formar los ciclos.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Implementemos lo  mismo ahora con un workflow secuencial de Workflow Foundation.

 

Arbitrary Cyces con WF Lograr la representación equivalente con el diseñador de workflow foundation sin caer en la tentación de repetir las actividades B y C es todo un desafío y con una simple mirada al workflow (que ya pierde mucho sentido de si) podemos imaginar lo que pasaría si apareciera otro bucle mezclado con los dos bucles identificados o si el workflow fuera un poco más complejo, bueno, mejor no imaginarlo siquiera.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Analicemos ahora si existe una forma alternativa de construir este workflow sin caer en la trampa de agregar tantos bucles. Hagamos de cuenta que el visual studio tiene un Refactoring llamado "Arbitrary Loop" que permite inyectar en una secuencia cualquiera de actividades un bucle arbitrario, respetando todos los cruzamientos que  se den entre los bucles definidos en el workflow. Sigamos el proceso de diseño:

1) Diseñemos el workflow inicial ignorando los loops y simplemente ingresando las actividades del tipo If-Then-Else y dejando indicado que en el Else debemos hacer un loop con determinada actividad.

Paso 01 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2) Ahora, seleccionando el primer IF, apliquemos un "Refactoring" al que llamaremos "Arbitrary Loop", indicando que queremos hacer un loop con B. Si bien estamos haciendo algo que no es estrictamente un refactoring sino una transformación del workflow que conserva la lógica que hemos implementado en el workflow y que usando la salida por "Else" del condicional que tomamos como punto de partida del refactoring forma un loop con la acción seleccionada.
En este caso estamos aplicando un "Arbitrary Loop" de Eval.Else a B.

Paso 02

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3) Y ahora nuevamente aplicamos el mismo refactoring, uniendo El Else de Eval1 con la actividad C, para lo cual deberemos fraccionar la secuencia B-C, ya que cuando la condición F sea falsa, vamos a necesitar hacer un bucle en el proceso a la actividad C, por lo cual deberemos saltear en la secuencia a B. El proceso refactorizado nos quedaría formado de la siguiente manera:

Paso 03

 

En este proceso tenemos un loop central que oficia de bucle maestro y en cada hilo o bifurcación de ejecución dentro del bucle se termina con una actividad Setxxxx que establece, a partir de ese punto del proceso, por donde se debe continuar al volverse atrás en el bucle, usándose bifurcaciones condicionales en cada punto de entrada para soportar los saltos representados en el proceso.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Nuevamente voy a abusar de la imaginación de Uds, pero el ejemplo presentado es claramente funcionalmente equivalente al problema planteado y nuevamente la solución para implementar bucles arbitrarios en workflow foundation pasa por construir un tipo de actividad bucles arbitrarios, que ejecute la secuencia de actividades de acuerdo al esquema presentado pero que presente al usuario una interfase gráfica similar a una secuencia pero con un menú contextual que permita conectar una rama de un condicional con una actividad de dicha secuencia. Gráficamente, creo que la solución mas simple es presentarlo así:

 

arbitrary loops finished

 

En este último Workflow, contenido dentro de una actividad del tipo arbitraryloop, se utilizan actividades del tipo LoopTo para indicar la formación de un loop arbitrario, es decir, cuando se alcancen las actividades LoopToC o LoopToB se pasará a ejecutar directamente la actividad C o B según corresponda.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Puede que este análisis les parezca rebuscado, puede que Uds piensen que estos patrones se utilizan muy poco en la realidad o puede que simplemente piensen que hay que eliminarle algunas restricciones al motor de workflow de WF para seguir un camino más simple, pero como conclusión, quisiera transmitirles mi convicción de que el camino para extender workflow foundation y para hacer y construir Sistemas de Workflow sobre Workflow Foundation (sistemas que implementen patrones avanzados de workflow y que permitan que un analista de procesos  cree directamente los procesos de negocios, trabajando en el nivel de abstracción adecuado) es solo cuestión de usar a fondo el propio sistema de extensibilidad del runtime de workflow foundation, es decir, crear actividades y servicios, claro está, algunas actividades serán "estructurales" y otras de dominio específico.