DSL 6 - Funciones de mapeo
|
Comenzando
Este paso del tutorial utiliza la solución Si en cualquier momento quieres ver la solución de este paso, la encontrarás en la solución |
¿Qué es una función de mapeo?
Una función de mapeo es, sencillamente, una función que realiza una traducción de muchos a muchos de elementos de datos de negocio. Dentro del DSL hay tres lugares donde es posible utilizar funciones de mapeo. Se describen a continuación:
Enriqueciendo el agregado: «Funciones de agregado»
Un agregado es el almacén de datos en vivo para un pago concreto; es como el resumen, en un solo lugar, de todos los eventos recibidos hasta la fecha, de modo que pueda utilizarse rápidamente y pasarse allí donde sea necesario. Sin embargo, ¿qué ocurre si queremos realizar una acción sobre ese agregado, por ejemplo transformar datos, hacer seguimiento de actualizaciones o realizar un cálculo? A este tipo de mapeo nos referimos como «Función de agregado»: es la capacidad de ejecutar una función contra los datos del agregado cuando llega un nuevo evento.
Una «función de agregado» es, por tanto, relativamente sencilla de definir:
-
Una función de agregado ocurre al recibir un evento determinado.
-
Una función de agregado tiene acceso a los datos del evento y a cualquier dato previamente presente en el agregado.
-
Una función de agregado define qué datos devuelve; esto puede ser una actualización de datos previos o la generación de datos nuevos.
| Los datos generados por una función de agregado no se persisten en el journal maestro, pero se volverán a ejecutar durante la recuperación de un nodo. Por lo tanto, hay que tener cuidado al calcular valores dinámicos como fechas. |
Enriqueciendo el evento: «Enriquecimiento de entrada»
El segundo uso de las funciones de mapeo es cuando queremos enriquecer un evento con los datos que recibimos de la entrada junto con los datos actualmente almacenados en el agregado. A este tipo de mapeo lo llamamos «enriquecimiento de entrada».
Un «enriquecimiento de entrada» se puede definir de forma similar como:
-
Un enriquecimiento de entrada se produce antes de guardar un evento.
-
Una función de enriquecimiento de entrada tiene acceso a los datos del evento y a cualquier dato previamente presente en el agregado.
-
Una función de enriquecimiento de entrada define qué datos devuelve; esto puede ser una actualización de datos previos o la generación de datos nuevos.
-
Los puntos de datos actualizados se añaden al evento.
| Los datos generados por un enriquecimiento de entrada se persisten en el evento y, por tanto, son de larga duración y se enviarán aguas abajo mediante funcionalidades de datos de procesamiento. |
Suministrar datos para una llamada de acción
El uso final de una función de mapeo es en una llamada de acción. Si al llamar a una acción se necesita un punto de datos que no se proporciona en otra parte del flujo, es posible crear ese dato usando el mapeo. Nos referiremos a esto como un «Mapeo de acción».
Un «mapeo de acción» se puede definir, por tanto, simplemente como:
-
Un mapeo de acción se ejecuta cuando el dominio invoca una acción.
-
Una función de acción tiene acceso a todos los datos del agregado.
-
Una función de acción define qué datos devuelve.
-
Los datos generados por la función de acción no se retienen, por lo que solo existen en un punto en el tiempo.
Tabla de tipos de funciones de mapeo
| Tipo | Se llama cuando… | Datos disponibles | Efecto lateral | Persistido | Caso de uso |
|---|---|---|---|---|---|
Función de agregado |
Evento recibido |
event, aggregate |
Datos nuevos o actualizar existentes |
No |
realizar cálculos sobre datos transitorios de conteo |
Enriquecimiento de entrada |
Antes de guardar el evento |
event, aggregate |
Datos nuevos o actualizar existentes |
Sí |
realizar cálculos que necesitan persistirse en el journal |
Mapeo de acción |
Acción llamada |
aggregate |
ninguno |
No |
generación de datos cuando esos datos no son relevantes en otras partes del flujo |
Uso de una función de mapeo
Vamos a usar una función de agregado como método para ser más precisos en lo que enviamos a nuestro sistema contable cuando hicimos nuestra solicitud Validate Account. En ese caso enviamos el pacs008 completo en la llamada al sistema contable; ahora queremos enviar solo la parte del pacs008 que contiene la información de la cuenta. Así que usemos una función de agregado como forma de dividir el pacs.008 y dejar disponibles únicamente los datos relevantes.
Configuración del DSL
Configurar un nuevo punto de datos de negocio
Hasta ahora no hemos considerado realmente los datos de negocio, usando únicamente la transferencia de crédito del cliente proporcionada. Ahora que queremos utilizar un tipo diferente de datos, necesitaremos definirlo para que esté disponible para nuestro flujo. Aquí es donde entran en juego los datos de negocio.
Empecemos creando una nueva biblioteca de datos de negocio haciendo clic derecho en nuestro ipftutorialmodel y seleccionando (New > v2Flo > Business Data Library).
Se mostrará la pantalla de la biblioteca de datos de negocio, donde configuraremos:
-
Una descripción: "A sample data library"
-
Un elemento de datos de negocio (haciendo clic en "Add Business Data") que contenga:
-
Un nombre: "Creditor Account"
-
Una descripción: "the creditor details"
-
Para el tipo de dato seleccionaremos "CashAccount38" (nota: hay tipos para cada tipo de mensaje ISO; necesitarás la versión pacs008)
-
Una vez hecho, deberíamos ver:
|
Consejo
Cualquier objeto Java puede utilizarse como un tipo de datos de negocio. Estos tipos Java pueden importarse en el proyecto (lo cubriremos en Uso de datos de negocio personalizados). Si el tipo está en el alcance pero no aparece en las sugerencias, prueba a pulsar Ctrl+R, ya que esto buscará también en objetos que aún no se han importado y que el modelo puede ver. |
Actualizar la petición
Ahora que tenemos nuestro tipo de dato, el primer trabajo es simplemente sustituir los "Business Data" en la llamada a "Account Validation Request" para que, en lugar de tomar "Customer Credit Transfer" como datos de negocio de entrada, ahora tomemos "Creditor Account". Una vez hecho, la nueva definición de la petición debería ser así:
Con esto finaliza toda la configuración de soporte; ahora es el momento de crear realmente nuestra función de agregado. Si recordamos, la función debía extraer los detalles del acreedor de la transferencia de crédito del cliente. En nuestro caso, los detalles de la transferencia de crédito del cliente llegan en el evento "Flow Initiation", así que usemos esto para configurar nuestra función de agregado.
Crear una función de mapeo para actualizar nuestros datos de agregado
Empezamos pulsando el botón "Add Function" en nuestro flujo y luego introduciendo los siguientes parámetros:
-
Un nombre: "Extract Creditor Account"
-
Una descripción: "Extracts the creditor account from the pacs008"
-
Los datos de entrada: "Customer Credit Transfer"
-
Los datos de salida: "Creditor Account"
Aquí simplemente proporcionamos una función sencilla que puede extraer los detalles del acreedor de la transferencia de crédito del cliente para que pueda usarse más adelante en el flujo. Nuestra nueva función debería verse así:
A continuación debemos definir dónde queremos que se ejecute nuestra función. En nuestro caso, queremos que se ejecute al inicio del flujo cuando recibimos la transferencia de crédito inicial del cliente. Para ello, simplemente actualizamos el comportamiento de iniciación con la función de agregado que hemos definido:
Con esto terminamos todo el trabajo del flujo; el flujo ahora extraerá los detalles de la cuenta del acreedor del pacs008 y los utilizará para enviarlos al sistema contable.
Ahora veamos la parte de implementación de este requisito.
Implementación en Java
Cambiemos ahora a IntelliJ y veamos cómo conectamos esto con nuestro código de implementación. Como de costumbre, empezaremos ejecutando una build desde una ventana de terminal:
mvn clean install
Una vez construido, podemos volver a mirar el código generado en /domain-root/domain/target, y ahora deberíamos encontrar el puerto para llamar a nuestra función así (/domain-root/domain/target/classes/com/iconsolutions/ipf/tutorial/ipftutorialmodel/mapping):
public interface IpftutorialflowMappingPort {
ExtractCreditorAccountForFlowIpftutorialflowMappingOutput performExtractCreditorAccount(ExtractCreditorAccountForFlowIpftutorialflowMappingParameters parameters);
}
Aquí podemos ver la definición de nuestro puerto de función; es bastante directa. Así que ahora necesitamos una implementación adaptadora para él. Lo hacemos como siempre, añadiéndola a la declaración del dominio en la configuración de IPF Tutorial. Intenta hacerlo ahora; a continuación tienes una implementación:
@Bean
public IpftutorialmodelDomain init(ActorSystem actorSystem) {
// 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 SampleFraudSystemActionAdapter())
.withDecisionLibraryAdapter(input ->
input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getIntrBkSttlmAmt().getValue().compareTo(BigDecimal.TEN)>0 ?
RunFraudCheckDecisionOutcomes.FRAUDREQUIRED : RunFraudCheckDecisionOutcomes.SKIPFRAUD)
.withIpftutorialflowMappingAdapter(input -> new ExtractCreditorAccountForFlowIpftutorialflowMappingOutput(input.getCustomerCreditTransfer().getCdtTrfTxInf().get(0).getCdtrAcct()))
.build();
}
Comprobación de nuestra solución
Aquí podemos ver que hemos añadido nuestra función de agregado a la configuración y todo está completo y listo para probar; así que, como siempre, comprobemos que nuestra solución funciona. Inicia la aplicación como antes (las instrucciones están disponibles en Revisión de la aplicación inicial si necesitas recordarlas).
Luego podemos enviar un pago:
curl -X POST localhost:8080/submit -H 'Content-Type: application/json' -d '{"value": "5"}' | jq
Nota: la elección del valor del pago es bastante arbitraria aquí, ya que solo nos interesa ver el cambio en los datos contables. Para comprobar que nuestro cambio funciona, necesitaremos mirar el propio agregado. Esta vez consultaremos el agregado completo:
Abramos el pago en la GUI para desarrolladores, y abramos la vista del agregado (buscar por unit of work id → clic en "view" → "click to view aggregate data"); hacia el final de los detalles del agregado deberíamos ver los nuevos detalles de la cuenta del acreedor:
Así que eso es todo: hemos extraído correctamente los detalles del acreedor de nuestro agregado y ahora podemos utilizarlos en otras partes del flujo.
Alternativas
Como se mencionó arriba, existen múltiples formas de usar funciones de mapeo. Podemos probarlas aquí para proporcionar distintas maneras de obtener los datos necesarios para la llamada contable.
Mapear en el momento de la llamada
El primer enfoque es establecer la función de mapeo en el punto en que se realiza la llamada. Para ello, hacemos clic derecho en la llamada de validación de cuenta:
y seleccionamos "Inspector" en el menú desplegable (o pulsamos Ctrl+Alt+I); esto muestra las opciones de la llamada. Aquí podemos aplicar nuestra función de mapeo:
Entonces no necesitaríamos nuestra función de mapeo en el comportamiento de iniciación, pero desde el punto de vista de la implementación no cambiaría nada.
La única diferencia importante aquí es que, si mirases el agregado (por ejemplo, cargando la transacción en la app de desarrollador y pulsando el botón de ver agregado), los datos no se mostrarían aquí.
Mapear como un enriquecimiento de iniciación
La siguiente opción es cambiarla de función de agregado a enriquecimiento de entrada. Podríamos hacer esto simplemente moviendo la función en el comportamiento de entrada a un enriquecimiento de entrada.
De nuevo, aquí no serían necesarios cambios en la implementación. Podríamos ver la diferencia mirando el evento de dominio 'Flow Initiated' en la app de desarrollador: ahora la cuenta del acreedor se persistiría como un elemento de datos allí.
Mapear como función de agregado en otros pasos
También podemos ejecutar funciones de agregado en pasos distintos al de enriquecimiento. Para hacerlo, añadimos aquí una función de agregado:
Cuando hacemos clic en el botón 'Add Aggregate Function', podemos indicar el evento y la función de mapeo que queremos aplicar tras recibir ese evento. Esto funcionará igual que en la iniciación, salvo por el evento después del cual se invoca. De nuevo, no se requieren cambios de implementación, pero recuerda que estos datos tampoco se persistirán y se recrearán al reactivar el flujo.
Mapear como enriquecimiento de entrada en otros pasos
El último enfoque es añadirlo como parte de la recepción de una entrada, igual que arriba con el enriquecimiento de entrada en el comportamiento de iniciación. Para ello, simplemente seleccionamos la función de mapeo que queremos aplicar como parte del bloque de input enrichers:
Conclusiones
En esta sección hemos:
-
aprendido a usar funciones de agregado para convertir tipos de datos.
Ahora que hemos configurado nuestra aplicación para usar una función de agregado, veamos otra capacidad en: DSL 7 - Gestión de timeouts