Implementación de la Función de Verificación de Duplicados de Muestra
Verificación de duplicados a través de la transacción cache se modela como una implementación de Función de Dominio que llama a la configuración Transacción Cache Servicio.
| Esto utiliza PersistentTransactionCacheService respaldado por MongoDB, por lo que los datos necesarios para completar el cache y realice verificaciones de duplicados sobrevive a un servicio restart. |
La definición de la Función de Dominio se realiza (actualmente) dentro de la MPS proyecto, ya que los puntos de datos pueden ser específicos de la solución. Consulte las capturas de pantalla de un ejemplo de definición de Función de Dominio y su uso dentro de un flujo (llamando a la función y manejando la respuesta).events) de una solución de referencia:
La implementación correspondiente es la siguiente:
package com.iconsolutions.instantpayments.credittransfer.sample.config;
import com.iconsolutions.ipf.core.platform.txcache.service.PersistentTransactionCacheService;
import com.iconsolutions.ipf.core.platform.txcache.repository.TransactionCacheRepository;
import com.iconsolutions.ipf.core.platform.txcache.service.TransactionCacheService;
import com.iconsolutions.ipf.core.shared.retry.RepositoryRetryProvider;
import com.iconsolutions.ipf.payments.domain.clearing_and_settlement.pacs008.FIToFICustomerCreditTransfer;
import io.vavr.control.Try;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Optional;
@Configuration
public class TransactionCacheServiceConfig {
/**
*
* This definition builds a TransactionCacheService instances that handles entries for pacs.008 objects.
* The following fields are being used to determine if the transaction is a duplicate and forms the key:
* EndToEndId, DbtrAcct.Id, LclInstrm.Prtry/Cd, IntrBkSttlmAmt.Value, IntrBkSttlmAmt.Ccy
*
* @param transactionCacheRepository - by default this bean is provide by spring-data repository through the
* com.icon.ipf.core.platform:ipf-transaction-cache dependency
* @param repositoryRetryProvider - by default this bean is provided by the SharedRepositoryConfiguration config
* from the com.iconsolutions.ipf.core.shared:shared-application-common dependency.
* Added below for completeness
*
* <pre>
* @Bean
* @ConditionalOnMissingBean
* public RepositoryRetryProvider repositoryRetryProvider() {
* return new RepositoryRetryProvider(
* 0,
* t -> false,
* null);
* }</pre>
*/
@Bean
public TransactionCacheService<FIToFICustomerCreditTransfer> debtorCTTransactionCacheService(
TransactionCacheRepository transactionCacheRepository, RepositoryRetryProvider repositoryRetryProvider) {
return new PersistentTransactionCacheService<FIToFICustomerCreditTransfer>(
fi2fi -> {
var cdtTrfTxInf = fi2fi.getCdtTrfTxInf().get(0);
return List.of(
cdtTrfTxInf.getPmtId().getEndToEndId(),
Try.of(() -> cdtTrfTxInf.getDbtrAcct().getId().getOthr().getId()).getOrElseTry(() -> cdtTrfTxInf.getDbtrAcct().getId().getIBAN()),
Try.of(() -> Optional.ofNullable(cdtTrfTxInf.getPmtTpInf().getLclInstrm().getPrtry()).orElseThrow()).getOrElseTry(() -> cdtTrfTxInf.getPmtTpInf().getLclInstrm().getCd()),
cdtTrfTxInf.getIntrBkSttlmAmt().getValue().toString(),
cdtTrfTxInf.getIntrBkSttlmAmt().getCcy());
},
transactionCacheRepository,
repositoryRetryProvider
);
}
}
package com.iconsolutions.instantpayments.credittransfer.sample.adapter.action;
import com.iconsolutions.instantpayments.domain.credittransfer.actions.CheckFunctionalDuplicateAction;
import com.iconsolutions.instantpayments.domain.credittransfer.adapters.DuplicateCheckingActionPort;
import com.iconsolutions.instantpayments.domain.credittransfer.domain.CredittransferDomain;
import com.iconsolutions.instantpayments.domain.credittransfer.inputs.CheckFunctionalDuplicateResponseInput;
import com.iconsolutions.instantpayments.domain.credittransfer.inputs.responsecodes.AcceptOrRejectCodes;
import com.iconsolutions.instantpayments.domain.credittransfer.reasoncodes.ISOReasonCodes;
import com.iconsolutions.ipf.core.platform.txcache.service.TransactionCacheService;
import com.iconsolutions.ipf.payments.domain.clearing_and_settlement.pacs008.FIToFICustomerCreditTransfer;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CompletionStage;
/**
* This class is the implementation of an external domain action adapter that calls the transaction cache in order to perform
* a duplicate check.
* <p>
* It depends on an instance of TransactionCacheService that has been defined in TransactionCacheServiceConfig
*/
@Slf4j
@AllArgsConstructor
public class SampleDuplicateCheckingActionAdapter implements DuplicateCheckingActionPort {
private final TransactionCacheService<FIToFICustomerCreditTransfer> transactionCacheService;
@Override
public CompletionStage<Void> execute(CheckFunctionalDuplicateAction action) {
return saveAndVerify(transactionCacheService, action)
.thenCompose(CredittransferDomain.duplicateChecking()::handle)
.thenAccept(result -> log.debug("DuplicateCheckingActionAdapter completed with {}", result.getResult()));
}
/**
* In this example we eagerly save the payment and then verify if it has any duplicates
* - save it to the cache
* - re-read it from the cache using the derived key
* - if more than one entry is found then at least one previously existed and therefore it IS a duplicate
* - return the appropriate response to the process response Input
* <p>
* Note:
* This "eager" save is a preferable alternative to the process of:
* - read from cache with derived key
* - if there is a result then flag a duplicate else save to the cache
* <p>
* As it reduces the window for concurrent duplicates slipping through, at the cost of an extra record being stored
*
* @param cacheService
* @param action
*/
public CompletionStage<CheckFunctionalDuplicateResponseInput> saveAndVerify(TransactionCacheService<FIToFICustomerCreditTransfer> cacheService,
CheckFunctionalDuplicateAction action) {
return cacheService.saveToCache(action::getFlowType, action.getCustomerCreditTransfer(), action.getId())
.thenCompose(entry -> cacheService.findInCache(action::getFlowType, action.getCustomerCreditTransfer(), action.getId()))
.thenApply(entries -> entries.size() == 1 ? accepted(action.getId()) : rejected(action.getId()));
}
private CheckFunctionalDuplicateResponseInput accepted(String aggregateId) {
return new CheckFunctionalDuplicateResponseInput.Builder(aggregateId, AcceptOrRejectCodes.Accepted)
.build();
}
private CheckFunctionalDuplicateResponseInput rejected(String aggregateId) {
return new CheckFunctionalDuplicateResponseInput.Builder(aggregateId, AcceptOrRejectCodes.Rejected)
.withReasonCode(ISOReasonCodes.AM05)
.build();
}
}