DSL 9 - Uso de subflujos
|
Comenzando
Este paso del tutorial utiliza como punto de partida la solución "add_version" del proyecto. Si en cualquier momento quieres ver la solución de este paso, la encontrarás en la solución "add_subflow". |
¿Qué es un subflujo?
Un "Subflow" es una sección reutilizable de un flujo. Efectivamente tiene todas las mismas características que un flujo, pero no está pensado para ser independiente, sino que se incluye dentro de otro flujo.
Por lo tanto, un subflujo puede:
-
Usarse dentro de muchos flujos diferentes.
-
Usarse varias veces dentro del mismo flujo.
Un ejemplo habitual de subflujo es una comprobación de sanciones. Puede que muchos flujos diferentes utilicen una comprobación de sanciones o que una comprobación de sanciones sea necesaria en distintas partes del flujo (por ejemplo, tras reanudar después de una espera).
En esta sección usaremos el ejemplo de sanciones para integrar un subflujo en nuestro flujo.
Nuestro subflujo de sanciones tendrá que realizar las siguientes acciones:
Cuando comience el subflujo, haremos una petición a un sistema de sanciones.
Luego, el sistema de sanciones puede devolver:
-
Passed: todo está bien, el flujo puede continuar.
-
Wait
Y en la condición de espera, deberíamos esperar hasta que el sistema de sanciones nos envíe uno de tres mensajes adicionales:
-
Passed
-
Blocked
-
Failed
Configuración del DSL
Añadir el dominio de sanciones
Antes de empezar a ver el subflujo en sí, primero necesitamos añadir un dominio externo que sea capaz de soportar nuestro flujo. Hagámoslo.
Primero crearemos un nuevo dominio externo para nuestro sistema de sanciones. Es igual que hicimos en (), salvo que la respuesta necesita usar una capacidad adicional que aún no hemos tratado en detalle. Empecemos añadiendo lo básico:
-
Un nombre: "Sanctions Domain"
-
Una descripción: "A sample sanctions system"
-
Añade una petición con detalles:
-
Un nombre: "Check Sanctions"
-
Una descripción: "Sample sanctions call"
-
Añade la transferencia de crédito del cliente a los datos de negocio
-
Ahora consideremos las respuestas. Hay varias formas en las que podríamos modelar las respuestas requeridas usando combinaciones de códigos de respuesta y de razón como hicimos en el sistema de fraude. Aquí, sin embargo, lo haremos modelando múltiples respuestas a nuestra única petición de sanciones.
La primera respuesta es el escenario "passed"; lo llamaremos escenario "no hit". Para esto añadimos una respuesta simple como antes:
-
Un nombre: "Sanctions No Hit"
-
Una descripción: "The sanctions check has passed"
-
El resto de campos los dejamos por defecto.
La segunda respuesta es la de cuando la comprobación inicial no pasa inmediatamente y se nos pide esperar. Lo llamaremos escenario "hit".
La diferencia clave entre los escenarios "hit" y "no hit" es que:
-
en el escenario no-hit se completa la petición, es decir, no esperamos más información del sistema de sanciones.
-
en el escenario hit no se completa la petición; seguimos esperando un resultado final de nuestra llamada de sanciones.
Así que, esta vez, para nuestro escenario hit necesitamos establecer nuestra marca "Completing" en false para indicar al sistema que esperamos mensajes posteriores del sistema de sanciones en respuesta a la petición inicial.
Configuremos esta respuesta:
-
Un nombre: "Sanctions Hit"
-
Una descripción: "The sanctions system is checking the outcome"
-
La marca de completing sin seleccionar
-
El resto de campos por defecto.
Finalmente, también necesitamos la respuesta de resultado cuando el sistema de sanciones nos envíe finalmente una respuesta. La llamaremos "Final Sanctions Response". Añadámosla:
-
Un nombre: "Sanctions Final Response"
-
Una descripción: "The final result from the sanctions system"
-
Para los códigos de respuesta necesitaremos crear una nueva biblioteca de códigos de respuesta para nuestros "Sanctions Final Response Codes". Esto es igual que en DSL 4 (Uso de un dominio externo). En nuestro caso tendremos tres códigos de respuesta: False Hit, Block, Reject.
-
El resto de campos por defecto.
Una vez juntado todo, nuestra definición del sistema de sanciones debería verse así:
Y, como referencia, nuestros nuevos "Sanctions Final Response Codes" se verán así:
Observa que podemos simplemente añadir nuestros códigos de respuesta a la biblioteca existente que usamos para nuestros códigos de validación de cuentas.
Con esto, nuestro sistema de sanciones está listo para usar.
Añadir el subflujo
Ahora que tenemos configurado nuestro dominio de sanciones, creemos nuestro subflujo. Lo hacemos haciendo clic derecho en nuestro modelo y seleccionando .
Esto debería crear una nueva página de subflujo:
Lo primero a destacar es lo parecido que es a la página del flujo. Esto se debe a que, en esencia, es un tipo especializado de flujo. Así que usar esta página debería resultar bastante familiar.
Configuremos nuestro nuevo subflujo; empezaremos dando a nuestro flujo de sanciones el nombre "Sanctions Subflow" y la descripción "An example subflow for sanctions".
Lo siguiente a considerar son los estados que vamos a necesitar. A partir de los requisitos, vemos que necesitaremos estos estados:
-
Un estado "Checking Sanctions" para cuando hacemos la petición inicial a sanciones y estamos esperando una respuesta.
-
Un estado "Awaiting Final Result" para cuando hemos recibido una notificación de espera de sanciones y estamos esperando la respuesta final.
-
Un estado "Complete" para cuando hemos completado con éxito una comprobación de sanciones.
-
Un estado "Rejected" para cuando la comprobación de sanciones ha fallado.
-
Un estado "Blocked" para cuando la comprobación de sanciones ha resultado en una notificación de bloqueo.
Adelante, configúralos ahora. Considera qué valores necesitarás para la marca de terminal y el estado global en cada uno de estos estados; cuando termines, la solución es la siguiente:
Es muy importante en un subflujo establecer correctamente los estados terminales. Esto se debe a que esos son los estados que el subflujo podrá reportar al flujo padre. Aquí tenemos "Complete", "Rejected" y "Blocked" como estados terminales. Esto es porque los estados "Checking Sanctions" y "Awaiting Final Result" son estados intermedios durante el procesamiento del subflujo. Podemos enfatizarlo además configurando el estado global "PENDING".
Sigamos con nuestro subflujo; lo siguiente a considerar son los eventos. De nuevo, en base a los requisitos, necesitaremos 4 eventos:
-
"Sanctions Passed" para una comprobación de sanciones exitosa
-
"Sanctions Rejected" para una comprobación de sanciones fallida
-
"Sanctions Blocked" para una comprobación de sanciones bloqueada
-
"Sanctions Hit" para una notificación de espera
Observa que hay muchos nombres de evento posibles. Hemos elegido usar el mismo evento "Sanctions Passed" tanto para el aprobado directo como para el aprobado indirecto (vía espera). Podríamos haber creado dos eventos para identificar de forma única cada caso.
Añadamos ahora estas definiciones de eventos; deberíamos ver:
Continuando, la siguiente sección a considerar es el Input Behaviour. Aquí debemos contemplar cada una de nuestras tres respuestas diferentes del sistema de sanciones y cómo queremos manejarlas. Intenta hacerlo tú mismo; la solución está a continuación:
A continuación, en la iniciación necesitamos llamar al sistema de sanciones. Necesitaremos un nuevo estado "Checking Sanctions" y un comportamiento de iniciación que nos lleve al estado "Checking Sanctions" y llame a nuestro sistema de sanciones. Inténtalo ahora; la solución está a continuación:
Por último, necesitamos manejar nuestro comportamiento de eventos. Intenta resolverlo y luego compara con la solución:
Un punto interesante aquí es cómo hemos manejado la línea 4 en el comportamiento de evento. Habría sido igual de correcto tener dos líneas aquí, una para el estado actual "Checking Sanctions" y otra para el estado actual "Awaiting Final Result". Pero aquí hemos elegido usar la capacidad de definir múltiples estados en el estado actual. Es simplemente una forma abreviada de evitar repetir la misma lógica varias veces si el resultado no es diferente.
Ya hemos completado todas las secciones del flujo, pero si miramos aún aparece un error en el "Perform Action" del comportamiento de iniciación. Investiguémoslo validando el flujo (ALT + ENTER y luego Validate Flow). Nos dice:
Nos está indicando que el subflujo no tiene acceso a una transferencia de crédito del cliente y, por tanto, no puede realizar la llamada al sistema de sanciones. En nuestro caso, nuestra transferencia de crédito del cliente pertenece al flujo padre. Por lo tanto, para proporcionarla al subflujo debemos añadirla a los datos de iniciación; hagámoslo y deberíamos ver ahora:
Y nuestro error se ha resuelto. Como siempre, antes de terminar, veamos nuestro gráfico (Tools > Open Flow Viewer) para encontrar:
Con esto, nuestro subflujo está listo para usarse; la siguiente pregunta es cómo lo aplicamos en nuestro flujo padre. Volvamos a él; actualizaremos solo en nuestra versión más reciente V2:
Vamos a añadir nuestro subflujo como un nuevo paso después de la validación de la cuenta. Actualmente tenemos:
Para insertar el paso extra, queremos que la validación de cuenta llame a nuestro subflujo (no ejecutar la decisión de fraude) y luego ejecutar la comprobación de fraude tras pasar sanciones con éxito.
Si recuerdas DSL 5 - Using a decision, presentamos el concepto de estado pseudo y, en ese caso, el tipo específico "Decision State". Aquí queremos usar un tipo diferente de estado: un "Subflow State". Empecemos añadiéndolo después de recibir el resultado "Account Validation Passed".
Así que, en lugar de movernos a la decisión "Run Fraud Check", creemos nuestro "Subflow State".
-
Para ello primero debemos eliminar el estado de decisión existente y luego seleccionar "Create Subflow State".
-
Introduce el nombre como "In Sanctions".
-
Luego, en el cuadro "Perform Action", borraremos la llamada existente a Fraud y seleccionaremos "Call Subflow" y luego elegiremos "Sanctions Subflow". Una vez hecho, deberíamos ver algo como:
Si inspeccionas el error ahora, veremos:
Aquí vemos que el flujo necesita que definamos cómo manejar el hecho de que el subflujo haya alcanzado los estados Complete, Rejected o Blocked. ¿Por qué solo esos 3? Porque son los que especificamos como terminales en el subflujo de sanciones.
Configuremos el manejo de la finalización del subflujo como "Event Behaviour" en el flujo principal V2. Para ello, empezamos añadiendo un nuevo Event Behaviour con estado actual "In Sanctions". Luego, en "For Event" seleccionamos "Subflow Completion" y comenzamos seleccionando "Complete".
En nuestro caso, al recibir "Complete" desde el flujo de sanciones, necesitaremos crear nuestra decisión "Run Fraud Check" y ejecutar la decisión de fraude como antes (nota: hasta que esto se haga, "Run Fraud Check" aparece como una 'Unresolved Reference'):
Ahora hagamos lo mismo para manejar los outcomes Rejected y Blocked; por ahora enviaremos ambos a rejected.
De nuevo, observa aquí cómo hemos utilizado la capacidad "Any Of" para minimizar la entrada.
Por último, veamos cómo ha impactado esto en nuestro diagrama de flujo (Tools > Open FlowViewer):
Aquí vemos que nuestra llamada al subflujo ha sido representada por un recuadro. El recuadro contiene toda la lógica del subflujo para minimizar la complejidad del gráfico. Sin embargo, si miras en la parte superior del gráfico, hay una nueva opción "Expand Subflow". Haz clic y aplica para ver:
Y en esta vista vemos el subflujo expandido para poder ver el funcionamiento interno del flujo.
Con esto terminamos toda la configuración del DSL. Pasemos ahora a la parte de implementación.
Implementación en Java
Definir el adaptador
Cambiemos a IntelliJ para trabajar con la parte Java.
Primero, reconstruyamos nuestro proyecto para generar nuestro nuevo flujo. Para ello, abre una nueva terminal y ejecuta:
mvn clean install
No hay nada especial sobre el subflujo desde el punto de vista de generación; los cambios a considerar son simplemente que hemos definido un nuevo Dominio Externo (Sanctions Domain) que ahora debemos implementar. De nuevo, aquí elegiremos usar la implementación de la sample app como hicimos en DSL 4 - Using an external domain; intenta añadirla ahora y, cuando estés listo, la solución es la siguiente:
@Bean
public IpftutorialmodelDomain init(ActorSystem actorSystem, SchedulerPort schedulerAdapter) {
// All adapters should be added to the domain model
return new IpftutorialmodelDomain.Builder(actorSystem)
.withTutorialDomainFunctionLibraryAdapter(input -> CompletableFuture.completedStage(new DuplicateCheckResponseInput.Builder(input.getId(), AcceptOrRejectCodes.Accepted).build()))
.withAccountingSystemActionAdapter(new SampleAccountingSystemActionAdapter())
.withFraudSystemActionAdapter(new FraudSystemActionAdapter())
.withDecisionLibraryAdapter(input ->
input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(BigDecimal.TEN)>0 ?
RunFraudCheckDecisionOutcomes.FRAUDREQUIRED : RunFraudCheckDecisionOutcomes.SKIPFRAUD)
.withIpftutorialflowV1AggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV1AggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withIpftutorialflowV2AggregateFunctionAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV2AggregateFunctionOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withSchedulerAdapter(schedulerAdapter)
.withCSMServiceActionAdapter(new SampleCSMServiceActionAdapter())
.withSanctionsDomainActionAdapter(new SampleSanctionsDomainActionAdapter())
.build();
}
Aquí hemos añadido a nuestra configuración la especificación del sistema de sanciones.
Comprobación de nuestra solución
Como siempre, comprobemos ahora que nuestra solución funciona. Inicia la aplicación como antes (si necesitas recordatorio, las instrucciones están en Reviewing the initial application).
Para los pagos, enviaremos uno estándar:
curl -X POST localhost:8080/submit | jq
Luego, como siempre, si ahora abrimos el pago en la GUI para desarrolladores y abrimos el gráfico del flujo (buscar por unit of work id, clic en view, clic en view graph), deberíamos ver:
Y podemos ver que nuestro subflujo se ha expandido para formar parte del flujo principal en ejecución, que ha finalizado correctamente. Si miramos los eventos en su lugar (haz clic en domain events):
Debemos observar que el nombre del evento es la combinación tanto del prefijo que proporcionamos en nuestro estado pseudo como del nombre real del evento en el subflujo. Es importante darse cuenta de esto, ya que es esta capacidad la que nos permite usar nuestro subflujo en múltiples lugares de nuestro flujo. Puedes probarlo tú mismo si quieres añadiendo una segunda llamada de subflujo de sanciones en el flujo.
Conclusiones
En esta sección hemos aprendido a crear un subflujo e invocarlo desde dentro de nuestro flujo.
|
Habiendo considerado subflujos y cómo usarlos, ahora centremos nuestra atención en otros flujos y cómo llamar a un flujo desde otro en: DSL 10 - Calling other flows |