DSL 10 - Llamando a Otros Flujos
Si en algún momento desea ver la solución a este paso, puede encontrarla en el |
Escenario
Hasta ahora, solo hemos considerado un flujo único, que recibe un pacs. 008 y realiza algunas acciones sobre él. Podríamos considerar esto como un flujo de "ejecución". Ahora consideremos cómo podemos hacer la "iniciación" y crear un segundo flujo que:
-
Reciba un pain. 001
-
Convierte el pain. 001 a un pacs. 008
-
Reenvíe el pacs. 008 al flujo de ejecución
-
Determine el resultado del flujo de ejecución y termine de manera apropiada.
@startuml control "Start Flow" as start process Convert process "Execution Flow" as execFlow circle Rejected circle "cont.." as continue start --> Convert: call Convert --> Rejected: failed Convert --> execFlow: success execFlow --> continue @enduml
Lo clave a considerar aquí es cómo comunicamos el flujo a flujo. En este paso, solo examinaremos los flujos que se implementan juntos.- es decir, cuando dos flujos están definidos en el mismo modelo.
Mientras esta sección discute cómo utilizar el DSL para establecer comunicación de flujo a flujo, es importante darse cuenta de que, en esencia, el flujo a flujo es efectivamente un dominio llamando a otro y, como tal, sería igualmente válido (y posible) modelar la interacción de flujo a flujo utilizando external domains. |
Configuración de DSL
Añadiendo el Flujo de Iniciación
Agreguemos un nuevo flujo haciendo clic derecho en el modelo y seleccionando . Luego, en el editor de flujo, estableceremos:
-
Un nombre de "Flujo de Iniciación"
-
Una descripción de "Inicia el procesamiento de pagos"
-
Un nuevo state para representar el rechazo de flujo llamado "Rechazado". Esto debe tener un código global RECHAZADO.
Cuando haya terminado, debería verse algo así:
Ahora pasemos a la Initiation Behaviour De acuerdo con nuestro requisito mencionado anteriormente, necesitamos recibir un pain001. Lo primero que haremos aquí es utilizar los tipos de iniciación preempaquetados importándolos en nuestra solución.
Para hacer esto, presione Ctrl+R, ingrese 'Tipos de Iniciación ISO' en la búsqueda y luego seleccione la entrada resultante.
Al importar modelos en su solución utilizando este método, puede importar inadvertidamente un modelo que no tenía la intención de importar, lo que puede resultar en que su aplicación no inicie con errores tales como:
Para eliminar esto y cualquier otro modelo no utilizado que pueda tener en su solución, haga clic en el |
Si vamos a nuestro Initiation Behaviour en el nuevo flujo, debemos tener la opción de seleccionar "Iniciación de Pago" - así que hagámoslo.
Integrando los Flujos
Ahora integremos los dos flujos. Para hacer esto, necesitamos utilizar otro pseudo-state igual que en DSL 5 - Usando un Decision. En este caso, es un "Flujo" State ". Así que, en el "Mover a State " cuadro, seleccione "Crear Flujo" State.
Llamemos a nuestro Flujo State "Ejecución de Llamadas".
Luego, en la acción de realizar state, seleccionaremos "Flujo de Llamadas" y seleccionaremos nuestro flujo ipftutorial. Cuando haya hecho esto, debería verse algo así:
Ahora podemos ver que la llamada al flujo está subrayada como un error. Si valida el flujo (ALT+ENTER luego Valide el Flujo) encontraremos:
Trabajemos a través de cada uno de estos. En primer lugar, vemos "El flujo llamado requiere datos faltantes: [Customer Credit Transfer]". Esto se debe a que nuestro flujo de ejecución necesita recibir un objeto de transferencia de crédito, pero en este momento el Flujo de Iniciación no tiene uno para proporcionar. Para solucionar esto, crearemos un nuevo mapping función para mapear desde el pain. 001 a la pacs. 008. Si necesita un recordatorio de mapping funciones, revise DSL 6 - Mapping Funciones. Intente añadir esto nuevo mapping funciónese ahora. Cuando esté listo, puede comparar con la solución proporcionada a continuación:
A continuación, necesitaremos añadir el mapping función a nuestro Initiation Behaviour. En este caso, vamos a utilizarlo como un enriquecedor de entradas:
Si ahora volvemos a validar nuestro flujo, no deberíamos ver el error de datos faltantes.
El state los problemas se deben a que no hemos referenciado el Complete y Rejected state definiciones en cualquier parte del Flujo de Inicio aún. Para solucionar esto, comenzaremos añadiendo un Event Comportamiento para cuando estamos en la "Ejecución de Llamadas" state definimos en nuestro Initiation Behaviour. Primero añadimos el Complete state en "Mover a State " sección de la Event Comportamiento y luego, en el "Para Event " sección, seleccionamos el especial "On Flow State " event tipo, que nos proporciona una manera de manejar los estados publicados por el Flujo de Ejecución (similar a cómo manejamos los estados de finalización del Subflujo en DSL 9 - Uso de Subflujos). Después de implementar esto event tipo, seleccionamos Ipftutorialflow (V2) para la parte de "Cuándo". Notará que al intentar seleccionar una opción para la parte de "alcances", ¡no hay opciones para elegir!
Esto se debe a que, a diferencia de subflows, necesitamos seleccionar explícitamente qué estados de flujo en el flujo llamado (el Flujo de Ejecución aquí) deseamos que sean publicados y estén disponibles para cualquier flujo que los llame (el Flujo de Iniciación aquí).
En este caso, solo queremos hacer que los estados terminales en el Flujo de Ejecución estén disponibles para el Flujo de Iniciación, por lo que marcamos la opción "¿Está Publicando?" para el Complete,Rejected y Timed Out estados en el Flujo de Ejecución:
Ahora podemos seleccionar el Complete state publicado por el Flujo de Ejecución en la parte de "alcances" para esto Event Comportamiento:
Luego seguimos el mismo proceso para añadir Event Comportamiento para el Rejected terminal state en el Flujo de Inicio, que se transfiere a Cualquiera de los Rejected y Timed Out estados que están siendo publicados por el Flujo de Ejecución:
Visualización de los Flujos
Eso es todo, nuestros cambios de DSL están completos. Antes de continuar, consideremos el gráfico de nuestro nuevo Flujo de Iniciación. Como es habitual, abra el Visor de Flujos (Tools > Open Flo Viewer):
Aquí podemos ver que nuestro (llamado) Flujo de Ejecución está representado por el cuadro verde en el diagrama. Tenga en cuenta que, a diferencia de un subflujo, no podemos expandir este cuadro, ya que es un flujo separado y no se considera como un embedded parte del flujo de iniciación (de llamada).
Java Implementación
Ahora volvamos a Intellij y veamos cómo integrar esto en nuestro código de implementación. Como de costumbre, comenzaremos ejecutando una compilación desde una ventana de terminal:
mvn clean install
Esto generará con éxito todos nuestros componentes relacionados con DSL.
Anteriormente, estábamos enviando nuestras solicitudes directamente al flujo del tutorial ipf, mientras que ahora queremos llamar al Flujo de Iniciación. Por lo tanto, necesitaremos cambiar InitiateIpftutorialflowInput to InitiateInitiationflowInput en el InitiationController(ipftutorialsolution/app/controller/InitiationController.java). El InitiateInitiationflowInput espera un objeto de iniciación de pago en lugar de un customer credit transfer, por lo que necesitamos actualizar nuestra definición de inicio en consecuencia. Por ahora, podemos utilizar el Pain001Generator desde dentro de la aplicación ipftutorial-app para proporcionarnos esto.
Intente realizar este cambio usted mismo. Cuando esté listo, puede comparar con la solución proporcionada a continuación:
var samplePain001 = Pain001Generator.generate();
if (request!= null && request.getValue()!= null) {
samplePain001.getPmtInf().get(0).getCdtTrfTxInf().get(0).getAmt().getInstdAmt().setValue(request.getValue());
}
return Mono.fromCompletionStage(IpftutorialmodelDomain.initiation().handle(new InitiateInitiationFlowInput. Builder(entityId)
.withProcessingContext(ProcessingContext.builder()
.unitOfWorkId(unitOfWorkId)
.clientRequestId(clientRequestId)
.build())
.withPaymentInitiation(samplePain001)
.build()).thenApply(done -> InitiationResponse.builder().requestId(clientRequestId).uowId(unitOfWorkId).aggregateId(done.getAggregateId()).build()));
Asegúrese de recordar limpiar las importaciones en InitiationController después de realizar este cambio (CTRL+SHIFT+O o CTRL+ALT+O en Windows)!
A continuación, debemos añadir el mapping función para el Flujo de Iniciación. Recordemos lo que estábamos tratando de lograr con esta función: una forma de mapping el recibido pain. 001 a un pacs. 008 que luego podemos enviar al Flujo de Ejecución.
Puede utilizar cualquier enfoque que desee para realizar el mapping, pero en este caso, vamos a utilizar una biblioteca predefinida que nos proporciona un método para mapear el pain. 001 a un pacs. 008. Así que añadamos eso ahora.
Primero, necesitamos agregar la dependencia a la pom.xml dentro del módulo ipf-tutorial-app (ipf-tutorial-app/pom.xml).:
<dependency>
<groupId>com.iconsolutions.ipf.core.mapper</groupId>
<artifactId>mapping-library-spring</artifactId>
</dependency>
Esto cargará en una implementación basada en Spring de varias ISO a ISO.mapping métodos. Consulte el IsoMappingService clase que ha sido incorporada, y puede ver que tiene un método para aplicar un mapping desde pain. 001 to pacs. 008:
public FIToFICustomerCreditTransferV08 mapPain001toPacs008(CustomerCreditTransferInitiationV09 initiation) {
return (FIToFICustomerCreditTransfer)this.transformationService.mapThenEnrichWithDefault(initiation, FIToFICustomerCreditTransfer.class);
}
Aquí podemos ver que se está llamando a un servicio de transformación para aplicar el mapping. Esto está utilizando el ícono "Mapping Framework" para realizar el mapping. El mapping framework puede ser utilizado para construir su propio custom mappings, pero esto está fuera del alcance de esta sección del tutorial. Tenga en cuenta que esta implementación asume una relación de 1:1 entre la entrada (pain. 001) y salida (pacs. 008).
Dado que hemos optado por utilizar una implementación basada en Spring, una instancia de la IsoMappingService se inyectará automáticamente en el contexto de Spring para nosotros. Por lo tanto, simplemente necesitamos agregarlo a la definición del modelo de dominio de nuestro tutorial de ipf y utilizarlo para proporcionar la implementación de nuestro Flujo de Iniciación.mapping función puerto.
Intente y haga esto usted mismo ahora, tal como lo hicimos para el flujo del tutorial de ipf.mapping función puerto anteriormente en DSL 6 - Mapping Funciones. Cuando esté listo, puede comparar con la solución proporcionada a continuación:
@Bean
public IpftutorialmodelDomain ipftutorialmodelDomain(ActorSystem actorSystem, IsoMappingService mappingService, SchedulerPort schedulerAdapter) { (1)
// 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)
.withIpftutorialflowV1MappingAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV1MappingOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withIpftutorialflowV2MappingAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowV2MappingOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.withInitiationFlowMappingAdapter(parameters -> new MapPain001ToPacs008ForFlowInitiationFlowMappingOutput(mappingService.mapPain001toPacs008(parameters.getPaymentInitiation()))) (2)
.withSchedulerAdapter(schedulerAdapter)
.withCSMServiceActionAdapter(new SampleCSMServiceActionAdapter())
.withSanctionsSystemActionAdapter(new SampleSanctionsSystemActionAdapter())
.build();
}
| 1 | Se ha añadido IsoMappingService para permitir el uso de mapPain001toPacs008. |
| 2 | Se ha añadido InitiationFlowMappingAdapter y utiliza IsoMappingService. |
Ahora, cuando nuestra Iniciación Flow calls the mapping función, tomará la entrada pain. 001 y devuelva un mapeado pacs. 008.
Verificando Nuestra Solución
Como siempre, comencemos nuestra aplicación (instructions están disponibles en Revisando la aplicación tutorial inicial si necesita un repaso) y verifique que nuestra solución funcione.
Utilizando nuestro confiable comando curl:
curl -X POST localhost:8080/submit | jq
Notamos que nuestro id agregado en la respuesta se ve un poco diferente que antes:
{
"requestId": "1a53c51c-c96e-4786-9f3f-d0d91f80b973",
"uowId": "ba0b5c6c-855b-41ef-98a7-9b0ee121e6da",
"aggregateId": "InitiationFlow|176a4e23-6299-49be-89bf-891ca12740de"
}
y significa que ahora se está activando el Flujo de Inicio a través de la solicitud.
Si ahora mencionamos el pago en el Developer GUI y seleccione la vista de flujo (search by unit of work id, haga clic en ver), deberíamos ver:
Este es un momento realmente importante para entender. Hemos recuperado todos los flujos asociados a nuestro unit of work id aquí, y al hacerlo, ahora estamos viendo dos flujos en lugar de uno.
Primero tenemos el Flujo de Inicio. Veamos su gráfico completo:
Podemos ver aquí que está llamando al Flujo de Ejecución Y recibiendo la respuesta de finalización de este. Esto indica que nuestro Flujo de Ejecución principal debe haber finalizado correctamente. Podemos validar eso haciendo clic en el gráfico del flujo ipftutorialv2 y viendo cómo se ve:
Como se esperaba, también se ha completado con éxito.
Al mirar en la pestaña de Estructuras de Datos de Mensaje, podemos verificar que el pain. 001 el archivo fue pasado al flujo:
También vale la pena verificar el " Domain Events "pestaña, donde puede observar el" Events y el Process Flow de los cuales esos events originado:
Conclusiones
En esta sección, hemos logrado:
-
Se creó un Flujo de Iniciación
-
Se añadió la integración entre el Flujo de Iniciación y el Flujo de Ejecución.
-
Se implementaron los puertos recién creados.
-
Desplegada y probada la aplicación
-
Se observó que los dos flujos están vinculados a través del unit of work id.