DSL 10 - Llamando a Otros Flujos

Iniciando

El paso del tutorial utiliza el add_subflow solución del proyecto como su punto de partida.

Si en algún momento desea ver la solución a este paso, esta se puede encontrar en el add_flow solución

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:

  1. Reciba un pain.001

  2. Convierte el pain.001 a un pacs.008

  3. Reenvíe el pacs.008 al flujo de ejecución

  4. Determine el resultado del flujo de ejecución y termine de manera apropiada.

Diagram

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 dominios externos.

Configuración de DSL

Añadiendo el Flujo de Iniciación

Agreguemos un nuevo flujo haciendo clic derecho en el modelo y seleccionando Nuevo  v2Flo  Flujo. 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 estado para representar el rechazo de flujo llamado "Rechazado". Esto debe tener un código global RECHAZADO.

Cuando haya terminado, debería verse algo así:

calling1 create flow

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:
An action processor has not been provided for external domain INITIATION_DOMAIN in model INITIATION

Para eliminar esto y cualquier otro modelo no utilizado que pueda tener en su solución, haga clic en el ipftutorialmodel en la ventana del proyecto de la mano izquierda, presione Alt+Enter and then remove any light grey items (indicating they are unused) in the imported models list by selecting them and clicking on the 'minus' icon (or use the shortcut Alt+Elimine). Alternativamente, puede eliminar todas las importaciones no utilizadas de una vez haciendo clic en el botón "Eliminar importaciones de modelo no utilizadas".remove unused model imports icon.

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.

calling1 init behaviour 1

Integrando los Flujos

Ahora integremos los dos flujos. Para hacer esto, necesitamos utilizar otro pseudoestado, al 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 el estado de realizar acción, seleccionaremos "Flujo de Llamada" y seleccionaremos nuestro flujo ipftutorial. Cuando haya hecho esto, debería verse algo como:

calling1 init behaviour 2

Ahora podemos ver que la llamada al flujo está subrayada como un error. Si valida el flujo (ALT+ENTER luego Valide el Flujo) encontraremos:

calling1 validation errors

Trabajemos a través de cada uno de estos. En primer lugar, vemos "Called Flow 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 - Funciones de Mapeo. Intente añadir esto nuevo mapping funciónese ahora. Cuando esté listo, puede comparar con la solución proporcionada a continuación:

calling1 mapping function

Luego necesitaremos añadir el mapping función a nuestro Initiation Behaviour. En este caso, vamos a utilizarlo como un enriquecedor de entradas:

calling1 init behaviour 3

Si ahora volvemos a validar nuestro flujo, no deberíamos ver el error de datos faltantes.

Los problemas de estado se deben a que no hemos referenciado el Complete y Rejected definiciones de estado en cualquier parte del Flujo de Inicio aún. Para solucionar esto, comenzaremos añadiendo un Event Comportamiento para cuando estamos en el estado de "Ejecución de Llamadas" que definimos en nuestro Initiation Behaviour. Primero añadimos el Complete indique en el "Mover a" State " sección del Event Comportamiento y luego, en el "Para Event " sección, seleccionamos el especial "On Flow State "tipo de evento, 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 este tipo de evento, 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 disponibles para elegir!

calling1 event behaviour 1

Esto se debe a que, a diferencia de los subflujos, 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:

calling1 exec state definitions

Ahora podemos seleccionar el Complete estado publicado por el Flujo de Ejecución en la parte de "alcances" para esto Event Comportamiento:

calling1 event behaviour 2

Luego seguimos el mismo proceso para añadir Event Comportamiento para el Rejected estado terminal en el Flujo de Inicio, al que se transiciona a Cualquiera de los Rejected y Timed Out estados que están siendo publicados por el Flujo de Ejecución:

calling1 event behaviour 3

Visualización de los Flujos

Eso es todo, hemos completado nuestros cambios en DSL. Antes de continuar, consideremos el gráfico de nuestro nuevo Flujo de Iniciación. Como es habitual, abra el Visor de Flo (Tools > Open Flo Viewer):

calling1 flo viewer

Aquí podemos ver que nuestro Flujo de Ejecución (denominado) 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 una parte incrustada del Flujo de Inicio (llamante).

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 transferencia de crédito, 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 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 el marco 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 - Funciones de Mapeo. 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 nuestro Flujo de Inicio llama al mapping función, tomará la entrada pain.001 y devuelva un mapeado pacs.008.

Verificando Nuestra Solución

Como siempre, comencemos nuestra aplicación (las instrucciones 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, click view), deberíamos ver:

calling1 dev app summary

Este es un momento realmente importante para entender. Hemos recuperado todos los flujos asociados a nuestra unidad de trabajo 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:

calling1 dev app init flow diagram

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 observando cómo se ve:

calling1 dev app exec flow diagram

Como se esperaba, también se ha completado con éxito.

Al mirar en la pestaña Estructuras de Datos del Mensaje, podemos verificar que el pain.001 el archivo fue pasado al flujo:

calling1 dev app mds

También vale la pena verificar el " Domain Events "pestaña, donde puede observar el Events y el Process Flow de los cuales se originaron esos eventos:

calling1 dev app domain events

Conclusiones

En esta sección, hemos logrado:

  • Se creó un Flujo de Inicio

  • Se añadió la integración entre el Flujo de Inicio 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 id de la unidad de trabajo.