From aaae3970575c6ef5696a56d9dda196996819ef0d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:52:30 +0000 Subject: [PATCH 1/6] Initial plan From ec53016496904f10a853df79328098f7b2759a5f Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Tue, 17 Mar 2026 16:55:59 +0000 Subject: [PATCH 2/6] Delete .github/workflows/use_central_workflow.yml --- .github/workflows/use_central_workflow.yml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .github/workflows/use_central_workflow.yml diff --git a/.github/workflows/use_central_workflow.yml b/.github/workflows/use_central_workflow.yml deleted file mode 100644 index fea6f93b..00000000 --- a/.github/workflows/use_central_workflow.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Move Task on PR Assignment -on: - pull_request: - types: [assigned] - -jobs: - move_task: - uses: TransactionProcessing/org-ci-workflows/.github/workflows/move_task.yml@main - secrets: inherit From 98702aaf415b0886ed7d9f3aca61a8a23f2a8fca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:58:33 +0000 Subject: [PATCH 3/6] Refactor read model event dispatch Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../ReadModelDomainEventHandlerTests.cs | 67 +++++ .../ReadModelDomainEventHandler.cs | 251 +++++++++++------- 2 files changed, 219 insertions(+), 99 deletions(-) create mode 100644 TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/ReadModelDomainEventHandlerTests.cs diff --git a/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/ReadModelDomainEventHandlerTests.cs b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/ReadModelDomainEventHandlerTests.cs new file mode 100644 index 00000000..361f7e90 --- /dev/null +++ b/TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/ReadModelDomainEventHandlerTests.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Shared.Logger; +using Shouldly; +using SimpleResults; +using TransactionProcessor.BusinessLogic.EventHandling; +using TransactionProcessor.DomainEvents; +using TransactionProcessor.Repository; +using TransactionProcessor.Testing; +using Xunit; + +namespace TransactionProcessor.BusinessLogic.Tests.DomainEventHandlers; + +public class ReadModelDomainEventHandlerTests +{ + private readonly Mock EstateReportingRepository; + private readonly ReadModelDomainEventHandler DomainEventHandler; + + public ReadModelDomainEventHandlerTests() + { + Logger.Initialise(NullLogger.Instance); + this.EstateReportingRepository = new Mock(); + this.DomainEventHandler = new ReadModelDomainEventHandler(this.EstateReportingRepository.Object); + } + + [Fact] + public async Task ReadModelDomainEventHandler_Handle_AdditionalRequestDataRecordedEvent_RecordSucceeds_SetsTransactionAmount() + { + TransactionDomainEvents.AdditionalRequestDataRecordedEvent domainEvent = new(TestData.TransactionId, + TestData.EstateId, + TestData.MerchantId, + TestData.OperatorId, + new Dictionary(), + TestData.TransactionDateTime); + + this.EstateReportingRepository + .Setup(r => r.RecordTransactionAdditionalRequestData(domainEvent, It.IsAny())) + .ReturnsAsync(Result.Success); + this.EstateReportingRepository + .Setup(r => r.SetTransactionAmount(domainEvent, It.IsAny())) + .ReturnsAsync(Result.Success); + + Result result = await this.DomainEventHandler.Handle(domainEvent, CancellationToken.None); + + result.IsSuccess.ShouldBeTrue(); + this.EstateReportingRepository.Verify(r => r.RecordTransactionAdditionalRequestData(domainEvent, It.IsAny()), Times.Once); + this.EstateReportingRepository.Verify(r => r.SetTransactionAmount(domainEvent, It.IsAny()), Times.Once); + } + + [Fact] + public async Task ReadModelDomainEventHandler_Handle_StatementGeneratedEvent_MarkFails_DoesNotUpdateMerchant() + { + MerchantStatementDomainEvents.StatementGeneratedEvent domainEvent = TestData.DomainEvents.StatementGeneratedEvent; + + this.EstateReportingRepository + .Setup(r => r.MarkStatementAsGenerated(domainEvent, It.IsAny())) + .ReturnsAsync(Result.Failure); + + Result result = await this.DomainEventHandler.Handle(domainEvent, CancellationToken.None); + + result.IsFailed.ShouldBeTrue(); + this.EstateReportingRepository.Verify(r => r.MarkStatementAsGenerated(domainEvent, It.IsAny()), Times.Once); + this.EstateReportingRepository.Verify(r => r.UpdateMerchant(domainEvent, It.IsAny()), Times.Never); + } +} diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index d821e53e..5585cbca 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -17,107 +17,160 @@ public ReadModelDomainEventHandler(ITransactionProcessorReadModelRepository esta this.EstateReportingRepository = estateReportingRepository; } - public async Task Handle(IDomainEvent domainEvent, - CancellationToken cancellationToken) { - Task task = domainEvent switch - { - EstateDomainEvents.EstateCreatedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), - EstateDomainEvents.SecurityUserAddedToEstateEvent de => this.EstateReportingRepository.AddEstateSecurityUser(de, cancellationToken), - EstateDomainEvents.EstateReferenceAllocatedEvent de => this.EstateReportingRepository.UpdateEstate(de, cancellationToken), - EstateDomainEvents.OperatorAddedToEstateEvent de => this.EstateReportingRepository.AddEstateOperator(de, cancellationToken), - EstateDomainEvents.OperatorRemovedFromEstateEvent de => this.EstateReportingRepository.RemoveOperatorFromEstate(de, cancellationToken), - - OperatorDomainEvents.OperatorCreatedEvent de => this.EstateReportingRepository.AddOperator(de, cancellationToken), - OperatorDomainEvents.OperatorNameUpdatedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), - OperatorDomainEvents.OperatorRequireCustomMerchantNumberChangedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), - OperatorDomainEvents.OperatorRequireCustomTerminalNumberChangedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), - - ContractDomainEvents.ContractCreatedEvent de => this.EstateReportingRepository.AddContract(de, cancellationToken), - ContractDomainEvents.FixedValueProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProduct(de, cancellationToken), - ContractDomainEvents.VariableValueProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProduct(de, cancellationToken), - ContractDomainEvents.TransactionFeeForProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProductTransactionFee(de, cancellationToken), - ContractDomainEvents.TransactionFeeForProductDisabledEvent de => this.EstateReportingRepository.DisableContractProductTransactionFee(de, cancellationToken), - - MerchantDomainEvents.MerchantCreatedEvent de => this.EstateReportingRepository.AddMerchant(de, cancellationToken), - MerchantDomainEvents.MerchantReferenceAllocatedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), - MerchantDomainEvents.AddressAddedEvent de => this.EstateReportingRepository.AddMerchantAddress(de, cancellationToken), - MerchantDomainEvents.ContactAddedEvent de => this.EstateReportingRepository.AddMerchantContact(de, cancellationToken), - MerchantDomainEvents.SecurityUserAddedToMerchantEvent de => this.EstateReportingRepository.AddMerchantSecurityUser(de, cancellationToken), - MerchantDomainEvents.DeviceAddedToMerchantEvent de => this.EstateReportingRepository.AddMerchantDevice(de, cancellationToken), - MerchantDomainEvents.DeviceSwappedForMerchantEvent de => this.EstateReportingRepository.SwapMerchantDevice(de, cancellationToken), - MerchantDomainEvents.OperatorAssignedToMerchantEvent de => this.EstateReportingRepository.AddMerchantOperator(de, cancellationToken), - MerchantDomainEvents.OperatorRemovedFromMerchantEvent de => this.EstateReportingRepository.RemoveOperatorFromMerchant(de, cancellationToken), - MerchantDomainEvents.SettlementScheduleChangedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), - MerchantDomainEvents.ContractAddedToMerchantEvent de => this.EstateReportingRepository.AddContractToMerchant(de, cancellationToken), - MerchantDomainEvents.ContractRemovedFromMerchantEvent de => this.EstateReportingRepository.RemoveContractFromMerchant(de, cancellationToken), - MerchantDomainEvents.MerchantNameUpdatedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), - MerchantDomainEvents.MerchantAddressLine1UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantAddressLine2UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantAddressLine3UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantAddressLine4UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantCountyUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantRegionUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantTownUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantPostalCodeUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), - MerchantDomainEvents.MerchantContactNameUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), - MerchantDomainEvents.MerchantContactEmailAddressUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), - MerchantDomainEvents.MerchantContactPhoneNumberUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), - MerchantDomainEvents.MerchantOpeningHoursUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantOpeningHours(de, cancellationToken), - MerchantScheduleDomainEvents.MerchantScheduleCreatedEvent de => this.EstateReportingRepository.AddMerchantSchedule(de, cancellationToken), - MerchantScheduleDomainEvents.MerchantScheduleMonthUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantSchedule(de, cancellationToken), - - TransactionDomainEvents.TransactionHasStartedEvent de => this.EstateReportingRepository.StartTransaction(de, cancellationToken), - TransactionDomainEvents.AdditionalRequestDataRecordedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), - TransactionDomainEvents.AdditionalResponseDataRecordedEvent de => this.EstateReportingRepository.RecordTransactionAdditionalResponseData(de, cancellationToken), - TransactionDomainEvents.TransactionHasBeenLocallyAuthorisedEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), - TransactionDomainEvents.TransactionHasBeenLocallyDeclinedEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), - TransactionDomainEvents.TransactionAuthorisedByOperatorEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), - TransactionDomainEvents.TransactionDeclinedByOperatorEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), - TransactionDomainEvents.TransactionSourceAddedToTransactionEvent de => this.EstateReportingRepository.AddSourceDetailsToTransaction(de, cancellationToken), - TransactionDomainEvents.ProductDetailsAddedToTransactionEvent de => this.EstateReportingRepository.AddProductDetailsToTransaction(de, cancellationToken), - TransactionDomainEvents.TransactionHasBeenCompletedEvent de => this.EstateReportingRepository.CompleteTransaction(de, cancellationToken), - TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent de => this.EstateReportingRepository.RecordTransactionTimings(de, cancellationToken), - ReconciliationDomainEvents.ReconciliationHasStartedEvent de => this.EstateReportingRepository.StartReconciliation(de, cancellationToken), - ReconciliationDomainEvents.OverallTotalsRecordedEvent de => this.EstateReportingRepository.UpdateReconciliationOverallTotals(de, cancellationToken), - ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent de => this.EstateReportingRepository.UpdateReconciliationStatus(de, cancellationToken), - ReconciliationDomainEvents.ReconciliationHasBeenLocallyDeclinedEvent de => this.EstateReportingRepository.UpdateReconciliationStatus(de, cancellationToken), - ReconciliationDomainEvents.ReconciliationHasCompletedEvent de => this.EstateReportingRepository.CompleteReconciliation(de, cancellationToken), - //VoucherDomainEvents.VoucherGeneratedEvent de => this.EstateReportingRepository.AddGeneratedVoucher(de, cancellationToken), - //VoucherDomainEvents.VoucherIssuedEvent de => this.EstateReportingRepository.UpdateVoucherIssueDetails(de, cancellationToken), - //VoucherDomainEvents.VoucherFullyRedeemedEvent de => this.EstateReportingRepository.UpdateVoucherRedemptionDetails(de, cancellationToken), - - FileProcessor.FileImportLog.DomainEvents.ImportLogCreatedEvent de => this.EstateReportingRepository.AddFileImportLog(de, cancellationToken), - FileProcessor.FileImportLog.DomainEvents.FileAddedToImportLogEvent de => this.EstateReportingRepository.AddFileToImportLog(de, cancellationToken), - - FileProcessor.File.DomainEvents.FileCreatedEvent de => this.EstateReportingRepository.AddFile(de, cancellationToken), - FileProcessor.File.DomainEvents.FileLineAddedEvent de => this.EstateReportingRepository.AddFileLineToFile(de, cancellationToken), - FileProcessor.File.DomainEvents.FileLineProcessingSuccessfulEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), - FileProcessor.File.DomainEvents.FileLineProcessingFailedEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), - FileProcessor.File.DomainEvents.FileLineProcessingIgnoredEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), - FileProcessor.File.DomainEvents.FileProcessingCompletedEvent de => this.EstateReportingRepository.UpdateFileAsComplete(de, cancellationToken), - - MerchantStatementDomainEvents.StatementCreatedEvent de => this.EstateReportingRepository.CreateStatement(de, cancellationToken), - MerchantStatementForDateDomainEvents.TransactionAddedToStatementForDateEvent de => this.EstateReportingRepository.AddTransactionToStatement(de, cancellationToken), - MerchantStatementForDateDomainEvents.SettledFeeAddedToStatementForDateEvent de => this.EstateReportingRepository.AddSettledFeeToStatement(de, cancellationToken), - MerchantStatementDomainEvents.StatementGeneratedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), - - FloatCreatedForContractProductEvent de => this.EstateReportingRepository.CreateFloat(de, cancellationToken), - FloatCreditPurchasedEvent de => this.EstateReportingRepository.CreateFloatActivity(de, cancellationToken), - FloatDecreasedByTransactionEvent de => this.EstateReportingRepository.CreateFloatActivity(de, cancellationToken), - - SettlementDomainEvents.SettlementCreatedForDateEvent de => this.EstateReportingRepository.CreateSettlement(de, cancellationToken), - SettlementDomainEvents.SettlementProcessingStartedEvent de => this.EstateReportingRepository.MarkSettlementAsProcessingStarted(de, cancellationToken), - SettlementDomainEvents.MerchantFeeAddedPendingSettlementEvent de => this.EstateReportingRepository.AddPendingMerchantFeeToSettlement(de, cancellationToken), - TransactionDomainEvents.SettledMerchantFeeAddedToTransactionEvent de => this.EstateReportingRepository.AddSettledMerchantFeeToSettlement(de, cancellationToken), - SettlementDomainEvents.MerchantFeeSettledEvent de => this.EstateReportingRepository.MarkMerchantFeeAsSettled(de, cancellationToken), - SettlementDomainEvents.SettlementCompletedEvent de => this.EstateReportingRepository.MarkSettlementAsCompleted(de, cancellationToken), - - _ => Task.FromResult(Result.Success()) - }; - - return await task; + public Task Handle(IDomainEvent domainEvent, + CancellationToken cancellationToken) { + Task task = this.HandleEstateDomainEvent(domainEvent, cancellationToken) ?? + this.HandleOperatorDomainEvent(domainEvent, cancellationToken) ?? + this.HandleContractDomainEvent(domainEvent, cancellationToken) ?? + this.HandleMerchantDomainEvent(domainEvent, cancellationToken) ?? + this.HandleTransactionDomainEvent(domainEvent, cancellationToken) ?? + this.HandleReconciliationDomainEvent(domainEvent, cancellationToken) ?? + this.HandleFileDomainEvent(domainEvent, cancellationToken) ?? + this.HandleStatementDomainEvent(domainEvent, cancellationToken) ?? + this.HandleFloatDomainEvent(domainEvent, cancellationToken) ?? + this.HandleSettlementDomainEvent(domainEvent, cancellationToken); + + return task ?? Task.FromResult(Result.Success()); } + private Task HandleEstateDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + EstateDomainEvents.EstateCreatedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), + EstateDomainEvents.SecurityUserAddedToEstateEvent de => this.EstateReportingRepository.AddEstateSecurityUser(de, cancellationToken), + EstateDomainEvents.EstateReferenceAllocatedEvent de => this.EstateReportingRepository.UpdateEstate(de, cancellationToken), + EstateDomainEvents.OperatorAddedToEstateEvent de => this.EstateReportingRepository.AddEstateOperator(de, cancellationToken), + EstateDomainEvents.OperatorRemovedFromEstateEvent de => this.EstateReportingRepository.RemoveOperatorFromEstate(de, cancellationToken), + _ => null + }; + + private Task HandleOperatorDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + OperatorDomainEvents.OperatorCreatedEvent de => this.EstateReportingRepository.AddOperator(de, cancellationToken), + OperatorDomainEvents.OperatorNameUpdatedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), + OperatorDomainEvents.OperatorRequireCustomMerchantNumberChangedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), + OperatorDomainEvents.OperatorRequireCustomTerminalNumberChangedEvent de => this.EstateReportingRepository.UpdateOperator(de, cancellationToken), + _ => null + }; + + private Task HandleContractDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + ContractDomainEvents.ContractCreatedEvent de => this.EstateReportingRepository.AddContract(de, cancellationToken), + ContractDomainEvents.FixedValueProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProduct(de, cancellationToken), + ContractDomainEvents.VariableValueProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProduct(de, cancellationToken), + ContractDomainEvents.TransactionFeeForProductAddedToContractEvent de => this.EstateReportingRepository.AddContractProductTransactionFee(de, cancellationToken), + ContractDomainEvents.TransactionFeeForProductDisabledEvent de => this.EstateReportingRepository.DisableContractProductTransactionFee(de, cancellationToken), + _ => null + }; + + private Task HandleMerchantDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + MerchantDomainEvents.MerchantCreatedEvent de => this.EstateReportingRepository.AddMerchant(de, cancellationToken), + MerchantDomainEvents.MerchantReferenceAllocatedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), + MerchantDomainEvents.AddressAddedEvent de => this.EstateReportingRepository.AddMerchantAddress(de, cancellationToken), + MerchantDomainEvents.ContactAddedEvent de => this.EstateReportingRepository.AddMerchantContact(de, cancellationToken), + MerchantDomainEvents.SecurityUserAddedToMerchantEvent de => this.EstateReportingRepository.AddMerchantSecurityUser(de, cancellationToken), + MerchantDomainEvents.DeviceAddedToMerchantEvent de => this.EstateReportingRepository.AddMerchantDevice(de, cancellationToken), + MerchantDomainEvents.DeviceSwappedForMerchantEvent de => this.EstateReportingRepository.SwapMerchantDevice(de, cancellationToken), + MerchantDomainEvents.OperatorAssignedToMerchantEvent de => this.EstateReportingRepository.AddMerchantOperator(de, cancellationToken), + MerchantDomainEvents.OperatorRemovedFromMerchantEvent de => this.EstateReportingRepository.RemoveOperatorFromMerchant(de, cancellationToken), + MerchantDomainEvents.SettlementScheduleChangedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), + MerchantDomainEvents.ContractAddedToMerchantEvent de => this.EstateReportingRepository.AddContractToMerchant(de, cancellationToken), + MerchantDomainEvents.ContractRemovedFromMerchantEvent de => this.EstateReportingRepository.RemoveContractFromMerchant(de, cancellationToken), + MerchantDomainEvents.MerchantNameUpdatedEvent de => this.EstateReportingRepository.UpdateMerchant(de, cancellationToken), + MerchantDomainEvents.MerchantAddressLine1UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantAddressLine2UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantAddressLine3UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantAddressLine4UpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantCountyUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantRegionUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantTownUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantPostalCodeUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantAddress(de, cancellationToken), + MerchantDomainEvents.MerchantContactNameUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), + MerchantDomainEvents.MerchantContactEmailAddressUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), + MerchantDomainEvents.MerchantContactPhoneNumberUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantContact(de, cancellationToken), + MerchantDomainEvents.MerchantOpeningHoursUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantOpeningHours(de, cancellationToken), + MerchantScheduleDomainEvents.MerchantScheduleCreatedEvent de => this.EstateReportingRepository.AddMerchantSchedule(de, cancellationToken), + MerchantScheduleDomainEvents.MerchantScheduleMonthUpdatedEvent de => this.EstateReportingRepository.UpdateMerchantSchedule(de, cancellationToken), + _ => null + }; + + private Task HandleTransactionDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + TransactionDomainEvents.TransactionHasStartedEvent de => this.EstateReportingRepository.StartTransaction(de, cancellationToken), + TransactionDomainEvents.AdditionalRequestDataRecordedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), + TransactionDomainEvents.AdditionalResponseDataRecordedEvent de => this.EstateReportingRepository.RecordTransactionAdditionalResponseData(de, cancellationToken), + TransactionDomainEvents.TransactionHasBeenLocallyAuthorisedEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), + TransactionDomainEvents.TransactionHasBeenLocallyDeclinedEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), + TransactionDomainEvents.TransactionAuthorisedByOperatorEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), + TransactionDomainEvents.TransactionDeclinedByOperatorEvent de => this.EstateReportingRepository.UpdateTransactionAuthorisation(de, cancellationToken), + TransactionDomainEvents.TransactionSourceAddedToTransactionEvent de => this.EstateReportingRepository.AddSourceDetailsToTransaction(de, cancellationToken), + TransactionDomainEvents.ProductDetailsAddedToTransactionEvent de => this.EstateReportingRepository.AddProductDetailsToTransaction(de, cancellationToken), + TransactionDomainEvents.TransactionHasBeenCompletedEvent de => this.EstateReportingRepository.CompleteTransaction(de, cancellationToken), + TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent de => this.EstateReportingRepository.RecordTransactionTimings(de, cancellationToken), + TransactionDomainEvents.SettledMerchantFeeAddedToTransactionEvent de => this.EstateReportingRepository.AddSettledMerchantFeeToSettlement(de, cancellationToken), + _ => null + }; + + private Task HandleReconciliationDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + ReconciliationDomainEvents.ReconciliationHasStartedEvent de => this.EstateReportingRepository.StartReconciliation(de, cancellationToken), + ReconciliationDomainEvents.OverallTotalsRecordedEvent de => this.EstateReportingRepository.UpdateReconciliationOverallTotals(de, cancellationToken), + ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent de => this.EstateReportingRepository.UpdateReconciliationStatus(de, cancellationToken), + ReconciliationDomainEvents.ReconciliationHasBeenLocallyDeclinedEvent de => this.EstateReportingRepository.UpdateReconciliationStatus(de, cancellationToken), + ReconciliationDomainEvents.ReconciliationHasCompletedEvent de => this.EstateReportingRepository.CompleteReconciliation(de, cancellationToken), + _ => null + }; + + private Task HandleFileDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + FileProcessor.FileImportLog.DomainEvents.ImportLogCreatedEvent de => this.EstateReportingRepository.AddFileImportLog(de, cancellationToken), + FileProcessor.FileImportLog.DomainEvents.FileAddedToImportLogEvent de => this.EstateReportingRepository.AddFileToImportLog(de, cancellationToken), + FileProcessor.File.DomainEvents.FileCreatedEvent de => this.EstateReportingRepository.AddFile(de, cancellationToken), + FileProcessor.File.DomainEvents.FileLineAddedEvent de => this.EstateReportingRepository.AddFileLineToFile(de, cancellationToken), + FileProcessor.File.DomainEvents.FileLineProcessingSuccessfulEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), + FileProcessor.File.DomainEvents.FileLineProcessingFailedEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), + FileProcessor.File.DomainEvents.FileLineProcessingIgnoredEvent de => this.EstateReportingRepository.UpdateFileLine(de, cancellationToken), + FileProcessor.File.DomainEvents.FileProcessingCompletedEvent de => this.EstateReportingRepository.UpdateFileAsComplete(de, cancellationToken), + _ => null + }; + + private Task HandleStatementDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + MerchantStatementDomainEvents.StatementCreatedEvent de => this.EstateReportingRepository.CreateStatement(de, cancellationToken), + MerchantStatementForDateDomainEvents.TransactionAddedToStatementForDateEvent de => this.EstateReportingRepository.AddTransactionToStatement(de, cancellationToken), + MerchantStatementForDateDomainEvents.SettledFeeAddedToStatementForDateEvent de => this.EstateReportingRepository.AddSettledFeeToStatement(de, cancellationToken), + MerchantStatementDomainEvents.StatementGeneratedEvent de => this.HandleSpecificDomainEvent(de, cancellationToken), + _ => null + }; + + private Task HandleFloatDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + FloatCreatedForContractProductEvent de => this.EstateReportingRepository.CreateFloat(de, cancellationToken), + FloatCreditPurchasedEvent de => this.EstateReportingRepository.CreateFloatActivity(de, cancellationToken), + FloatDecreasedByTransactionEvent de => this.EstateReportingRepository.CreateFloatActivity(de, cancellationToken), + _ => null + }; + + private Task HandleSettlementDomainEvent(IDomainEvent domainEvent, + CancellationToken cancellationToken) => domainEvent switch + { + SettlementDomainEvents.SettlementCreatedForDateEvent de => this.EstateReportingRepository.CreateSettlement(de, cancellationToken), + SettlementDomainEvents.SettlementProcessingStartedEvent de => this.EstateReportingRepository.MarkSettlementAsProcessingStarted(de, cancellationToken), + SettlementDomainEvents.MerchantFeeAddedPendingSettlementEvent de => this.EstateReportingRepository.AddPendingMerchantFeeToSettlement(de, cancellationToken), + SettlementDomainEvents.MerchantFeeSettledEvent de => this.EstateReportingRepository.MarkMerchantFeeAsSettled(de, cancellationToken), + SettlementDomainEvents.SettlementCompletedEvent de => this.EstateReportingRepository.MarkSettlementAsCompleted(de, cancellationToken), + _ => null + }; + private async Task HandleSpecificDomainEvent(TransactionDomainEvents.AdditionalRequestDataRecordedEvent domainEvent, CancellationToken cancellationToken) { From befd21b68256a5c0635b38db2b178a0cf1faa71e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:28:36 +0000 Subject: [PATCH 4/6] Reduce read model handler cyclomatic complexity Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../ReadModelDomainEventHandler.cs | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index 5585cbca..1bd93833 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -11,26 +11,42 @@ namespace TransactionProcessor.BusinessLogic.EventHandling { public class ReadModelDomainEventHandler : IDomainEventHandler { + private delegate Task DomainEventDispatchHandler(IDomainEvent domainEvent, + CancellationToken cancellationToken); + private readonly ITransactionProcessorReadModelRepository EstateReportingRepository; + private readonly DomainEventDispatchHandler[] DomainEventHandlers; public ReadModelDomainEventHandler(ITransactionProcessorReadModelRepository estateReportingRepository) { this.EstateReportingRepository = estateReportingRepository; + this.DomainEventHandlers = new[] + { + this.HandleEstateDomainEvent, + this.HandleOperatorDomainEvent, + this.HandleContractDomainEvent, + this.HandleMerchantDomainEvent, + this.HandleTransactionDomainEvent, + this.HandleReconciliationDomainEvent, + this.HandleFileDomainEvent, + this.HandleStatementDomainEvent, + this.HandleFloatDomainEvent, + this.HandleSettlementDomainEvent + }; } public Task Handle(IDomainEvent domainEvent, - CancellationToken cancellationToken) { - Task task = this.HandleEstateDomainEvent(domainEvent, cancellationToken) ?? - this.HandleOperatorDomainEvent(domainEvent, cancellationToken) ?? - this.HandleContractDomainEvent(domainEvent, cancellationToken) ?? - this.HandleMerchantDomainEvent(domainEvent, cancellationToken) ?? - this.HandleTransactionDomainEvent(domainEvent, cancellationToken) ?? - this.HandleReconciliationDomainEvent(domainEvent, cancellationToken) ?? - this.HandleFileDomainEvent(domainEvent, cancellationToken) ?? - this.HandleStatementDomainEvent(domainEvent, cancellationToken) ?? - this.HandleFloatDomainEvent(domainEvent, cancellationToken) ?? - this.HandleSettlementDomainEvent(domainEvent, cancellationToken); - - return task ?? Task.FromResult(Result.Success()); + CancellationToken cancellationToken) + { + foreach (DomainEventDispatchHandler domainEventHandler in this.DomainEventHandlers) + { + Task task = domainEventHandler(domainEvent, cancellationToken); + if (task != null) + { + return task; + } + } + + return Task.FromResult(Result.Success()); } private Task HandleEstateDomainEvent(IDomainEvent domainEvent, From b7a406e1286feba6abfb4045ab748a261301236a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:46:26 +0000 Subject: [PATCH 5/6] Fix read model dispatcher build regression Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../EventHandling/ReadModelDomainEventHandler.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index 1bd93833..2daf2ab5 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -11,11 +11,8 @@ namespace TransactionProcessor.BusinessLogic.EventHandling { public class ReadModelDomainEventHandler : IDomainEventHandler { - private delegate Task DomainEventDispatchHandler(IDomainEvent domainEvent, - CancellationToken cancellationToken); - private readonly ITransactionProcessorReadModelRepository EstateReportingRepository; - private readonly DomainEventDispatchHandler[] DomainEventHandlers; + private readonly Func>[] DomainEventHandlers; public ReadModelDomainEventHandler(ITransactionProcessorReadModelRepository estateReportingRepository) { this.EstateReportingRepository = estateReportingRepository; @@ -37,7 +34,7 @@ public ReadModelDomainEventHandler(ITransactionProcessorReadModelRepository esta public Task Handle(IDomainEvent domainEvent, CancellationToken cancellationToken) { - foreach (DomainEventDispatchHandler domainEventHandler in this.DomainEventHandlers) + foreach (Func> domainEventHandler in this.DomainEventHandlers) { Task task = domainEventHandler(domainEvent, cancellationToken); if (task != null) From bed4ae2a33173d7fcd61f1571019ea9149cd5c78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 20:06:12 +0000 Subject: [PATCH 6/6] Add missing System import for dispatcher Func Co-authored-by: StuartFerguson <16325469+StuartFerguson@users.noreply.github.com> --- .../EventHandling/ReadModelDomainEventHandler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index 2daf2ab5..56b894f4 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using Shared.DomainDrivenDesign.EventSourcing; using Shared.EventStore.EventHandling;