From 06fbce70f554cc307cf71fd64c1a65fa72415988 Mon Sep 17 00:00:00 2001 From: StuartFerguson Date: Fri, 12 Sep 2025 09:36:09 +0100 Subject: [PATCH] recon aggregate changed to results --- .../ReconciliationAggregateTests.cs | 136 +++++++--------- .../ReconciliationAggregate.cs | 151 +++++++++--------- .../Services/TransactionDomainService.cs | 43 ++--- 3 files changed, 153 insertions(+), 177 deletions(-) diff --git a/TransactionProcessor.Aggregates.Tests/ReconciliationAggregateTests.cs b/TransactionProcessor.Aggregates.Tests/ReconciliationAggregateTests.cs index 3077ca44..1a6ae48f 100644 --- a/TransactionProcessor.Aggregates.Tests/ReconciliationAggregateTests.cs +++ b/TransactionProcessor.Aggregates.Tests/ReconciliationAggregateTests.cs @@ -1,4 +1,5 @@ using Shouldly; +using SimpleResults; using TransactionProcessor.Testing; namespace TransactionProcessor.Aggregates.Tests @@ -18,9 +19,10 @@ public void ReconciliationAggregate_StartReconciliation_ReconciliationIsStarted( { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); - reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); + Result result = reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); + result.IsSuccess.ShouldBeTrue(); - reconciliationAggregate.IsStarted.ShouldBeTrue(); + reconciliationAggregate.HasBeenStarted.ShouldBeTrue(); reconciliationAggregate.EstateId.ShouldBe(TestData.EstateId); reconciliationAggregate.MerchantId.ShouldBe(TestData.MerchantId); } @@ -33,26 +35,26 @@ public void ReconciliationAggregate_StartReconciliation_InvalidData_ErrorThrown( Boolean validEstateId, Boolean validMerchantId) { - Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); + ReconciliationAggregate reconciliationAggregate = ReconciliationAggregate.Create(TestData.TransactionId); DateTime transactionDateTime = validDateTime ? TestData.TransactionDateTime : DateTime.MinValue; Guid estateId = validEstateId ? TestData.EstateId : Guid.Empty; Guid merchantId = validMerchantId ? TestData.MerchantId : Guid.Empty; - Should.Throw(() => { reconciliationAggregate.StartReconciliation(transactionDateTime, estateId, merchantId); }); + Result result = reconciliationAggregate.StartReconciliation(transactionDateTime, estateId, merchantId); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] - public void ReconciliationAggregate_StartReconciliation_ReconciliationAlreadyStarted_ErrorThrown() + public void ReconciliationAggregate_StartReconciliation_ReconciliationAlreadyStarted_NoErrorThrown() { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); - Should.Throw(() => - { - reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); - }); + Result result = reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); + result.IsSuccess.ShouldBeTrue(); } [Fact] @@ -65,21 +67,19 @@ public void ReconciliationAggregate_StartReconciliation_ReconciliationAlreadyCom reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); reconciliationAggregate.CompleteReconciliation(); - Should.Throw(() => - { - reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); - }); + Result result = reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); + result.IsSuccess.ShouldBeTrue(); } - [Fact] public void ReconciliationAggregate_RecordOverallTotals_OverallTotalsAreRecorded() { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); - reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - + Result result = reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); + result.IsSuccess.ShouldBeTrue(); + reconciliationAggregate.TransactionCount.ShouldBe(TestData.ReconciliationTransactionCount); reconciliationAggregate.TransactionValue.ShouldBe(TestData.ReconciliationTransactionValue); } @@ -87,30 +87,28 @@ public void ReconciliationAggregate_RecordOverallTotals_OverallTotalsAreRecorded [Fact] public void ReconciliationAggregate_RecordOverallTotals_ReconciliationNotStarted_ErrorThrown() { - Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); + ReconciliationAggregate reconciliationAggregate = ReconciliationAggregate.Create(TestData.TransactionId); - Should.Throw(() => - { - reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, + Result result = reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - }); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] public void ReconciliationAggregate_RecordOverallTotals_ReconciliationAlreadyCompleted_ErrorThrown() { - Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); + ReconciliationAggregate reconciliationAggregate = ReconciliationAggregate.Create(TestData.TransactionId); reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); reconciliationAggregate.CompleteReconciliation(); - Should.Throw(() => - { - reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, + Result result = reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - }); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] @@ -120,7 +118,8 @@ public void ReconciliationAggregate_Authorise_ReconciliationIsAuthorised() reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + Result result = reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); reconciliationAggregate.ResponseCode.ShouldBe(TestData.ResponseCode); reconciliationAggregate.ResponseMessage.ShouldBe(TestData.ResponseMessage); @@ -132,14 +131,13 @@ public void ReconciliationAggregate_Authorise_ReconciliationNotStarted_ErrorThro { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); - Should.Throw(() => - { - reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] - public void ReconciliationAggregate_Authorise_ReconciliationAlreadyAuthorised_ErrorThrown() + public void ReconciliationAggregate_Authorise_ReconciliationAlreadyAuthorised_NoErrorThrown() { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); @@ -147,10 +145,8 @@ public void ReconciliationAggregate_Authorise_ReconciliationAlreadyAuthorised_Er reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - Should.Throw(() => - { - reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Fact] @@ -162,10 +158,8 @@ public void ReconciliationAggregate_Authorise_ReconciliationAlreadyDeclined_Erro reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - Should.Throw(() => - { - reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Fact] @@ -178,10 +172,8 @@ public void ReconciliationAggregate_Authorise_ReconciliationAlreadyCompleted_Err reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); reconciliationAggregate.CompleteReconciliation(); - Should.Throw(() => - { - reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Fact] @@ -191,7 +183,8 @@ public void ReconciliationAggregate_Decline_ReconciliationIsDeclined() reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + Result result = reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); reconciliationAggregate.ResponseCode.ShouldBe(TestData.ResponseCode); reconciliationAggregate.ResponseMessage.ShouldBe(TestData.ResponseMessage); @@ -203,10 +196,9 @@ public void ReconciliationAggregate_Decline_ReconciliationNotStarted_ErrorThrown { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); - Should.Throw(() => - { - reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] @@ -218,14 +210,12 @@ public void ReconciliationAggregate_Decline_ReconciliationAlreadyAuthorised_Erro reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); reconciliationAggregate.Authorise(TestData.ResponseCode, TestData.ResponseMessage); - Should.Throw(() => - { - reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Fact] - public void ReconciliationAggregate_Decline_ReconciliationAlreadyDecline_ErrorThrown() + public void ReconciliationAggregate_Decline_ReconciliationAlreadyDecline_NoErrorThrown() { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); @@ -233,14 +223,12 @@ public void ReconciliationAggregate_Decline_ReconciliationAlreadyDecline_ErrorTh reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - Should.Throw(() => - { - reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Fact] - public void ReconciliationAggregate_Decline_ReconciliationAlreadyCompleted_ErrorThrown() + public void ReconciliationAggregate_Decline_ReconciliationAlreadyCompleted_NoErrorThrown() { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); @@ -249,10 +237,8 @@ public void ReconciliationAggregate_Decline_ReconciliationAlreadyCompleted_Error reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); reconciliationAggregate.CompleteReconciliation(); - Should.Throw(() => - { - reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); - }); + Result result = reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); + result.IsSuccess.ShouldBeTrue(); } [Theory] @@ -273,11 +259,11 @@ public void ReconciliationAggregate_CompleteReconciliation_ReconciliationIsCompl reconciliationAggregate.Decline(TestData.ResponseCode, TestData.ResponseMessage); } - reconciliationAggregate.CompleteReconciliation(); + Result result = reconciliationAggregate.CompleteReconciliation(); + result.IsSuccess.ShouldBeTrue(); reconciliationAggregate.IsAuthorised.ShouldBe(isAuthorised); reconciliationAggregate.IsCompleted.ShouldBeTrue(); - reconciliationAggregate.IsStarted.ShouldBeFalse(); } [Fact] @@ -285,10 +271,9 @@ public void ReconciliationAggregate_CompleteReconciliation_ReconciliationNotStar { Aggregates.ReconciliationAggregate reconciliationAggregate = Aggregates.ReconciliationAggregate.Create(TestData.TransactionId); - Should.Throw(() => - { - reconciliationAggregate.CompleteReconciliation(); - }); + Result result = reconciliationAggregate.CompleteReconciliation(); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Fact] @@ -299,10 +284,9 @@ public void ReconciliationAggregate_CompleteReconciliation_NotAuthorisedOrDeclin reconciliationAggregate.StartReconciliation(TestData.TransactionDateTime, TestData.EstateId, TestData.MerchantId); reconciliationAggregate.RecordOverallTotals(TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); - Should.Throw(() => - { - reconciliationAggregate.CompleteReconciliation(); - }); + Result result = reconciliationAggregate.CompleteReconciliation(); + result.IsFailed.ShouldBeTrue(); + result.Status.ShouldBe(ResultStatus.Invalid); } [Theory] @@ -325,10 +309,8 @@ public void ReconciliationAggregate_CompleteReconciliation_ReconciliationAlready reconciliationAggregate.CompleteReconciliation(); - Should.Throw(() => - { - reconciliationAggregate.CompleteReconciliation(); - }); + Result result = reconciliationAggregate.CompleteReconciliation(); + result.IsSuccess.ShouldBeTrue(); } } } diff --git a/TransactionProcessor.Aggregates/ReconciliationAggregate.cs b/TransactionProcessor.Aggregates/ReconciliationAggregate.cs index 0d0e6e48..d6f1652a 100644 --- a/TransactionProcessor.Aggregates/ReconciliationAggregate.cs +++ b/TransactionProcessor.Aggregates/ReconciliationAggregate.cs @@ -1,4 +1,5 @@ -using TransactionProcessor.DomainEvents; +using SimpleResults; +using TransactionProcessor.DomainEvents; namespace TransactionProcessor.Aggregates { @@ -12,7 +13,7 @@ public static void PlayEvent(this ReconciliationAggregate aggregate, Reconciliat { aggregate.EstateId = domainEvent.EstateId; aggregate.MerchantId = domainEvent.MerchantId; - aggregate.IsStarted = true; + aggregate.HasBeenStarted = true; aggregate.TransactionDateTime = domainEvent.TransactionDateTime; } @@ -38,123 +39,125 @@ public static void PlayEvent(this ReconciliationAggregate aggregate, Reconciliat public static void PlayEvent(this ReconciliationAggregate aggregate, ReconciliationDomainEvents.ReconciliationHasCompletedEvent domainEvent) { - aggregate.IsStarted = false; aggregate.IsCompleted = true; } - private static void CheckReconciliationNotAlreadyStarted(this ReconciliationAggregate aggregate) - { - if (aggregate.IsStarted) - { - throw new InvalidOperationException($"Reconciliation Id [{aggregate.AggregateId}] has already been started"); - } - } - - private static void CheckReconciliationNotAlreadyCompleted(this ReconciliationAggregate aggregate) - { - if (aggregate.IsCompleted) - { - throw new InvalidOperationException($"Reconciliation Id [{aggregate.AggregateId}] has already been completed"); + private static Result CheckReconciliationNotAlreadyCompleted(this ReconciliationAggregate aggregate) { + if (aggregate.IsCompleted) { + return Result.Invalid($"Reconciliation Id [{aggregate.AggregateId}] has already been completed"); } + return Result.Success(); } - public static void StartReconciliation(this ReconciliationAggregate aggregate, DateTime transactionDateTime, - Guid estateId, - Guid merchantId) - { - Guard.ThrowIfInvalidGuid(estateId, typeof(ArgumentException), $"Estate Id must not be [{Guid.Empty}]"); - Guard.ThrowIfInvalidGuid(merchantId, typeof(ArgumentException), $"Merchant Id must not be [{Guid.Empty}]"); - Guard.ThrowIfInvalidDate(transactionDateTime, typeof(ArgumentException), $"Transaction Date Time must not be [{DateTime.MinValue}]"); + public static Result StartReconciliation(this ReconciliationAggregate aggregate, + DateTime transactionDateTime, + Guid estateId, + Guid merchantId) { + if (estateId == Guid.Empty) + return Result.Invalid($"Estate Id must not be [{Guid.Empty}]"); + if (merchantId == Guid.Empty) + return Result.Invalid($"Merchant Id must not be [{Guid.Empty}]"); + if (transactionDateTime == DateTime.MinValue) + return Result.Invalid($"Transaction Date Time must not be [{DateTime.MinValue}]"); // TODO: Some rules here - aggregate.CheckReconciliationNotAlreadyStarted(); - aggregate.CheckReconciliationNotAlreadyCompleted(); + if (aggregate.HasBeenStarted || aggregate.IsCompleted) { + return Result.Success(); + } - ReconciliationDomainEvents.ReconciliationHasStartedEvent reconciliationHasStartedEvent = - new ReconciliationDomainEvents.ReconciliationHasStartedEvent(aggregate.AggregateId, estateId, merchantId, transactionDateTime); + ReconciliationDomainEvents.ReconciliationHasStartedEvent reconciliationHasStartedEvent = new(aggregate.AggregateId, estateId, merchantId, transactionDateTime); aggregate.ApplyAndAppend(reconciliationHasStartedEvent); + + return Result.Success(); } - private static void CheckReconciliationHasBeenStarted(this ReconciliationAggregate aggregate) - { - if (aggregate.IsStarted == false) - { - throw new InvalidOperationException($"Reconciliation [{aggregate.AggregateId}] has not been started"); + private static Result CheckReconciliationHasBeenStarted(this ReconciliationAggregate aggregate) { + if (aggregate.HasBeenStarted == false) { + return Result.Invalid($"Reconciliation [{aggregate.AggregateId}] has not been started"); } + return Result.Success(); } - public static void RecordOverallTotals(this ReconciliationAggregate aggregate, Int32 totalCount, Decimal totalValue) + public static Result RecordOverallTotals(this ReconciliationAggregate aggregate, Int32 totalCount, Decimal totalValue) { - // TODO: Rules - aggregate.CheckReconciliationHasBeenStarted(); - aggregate.CheckReconciliationNotAlreadyCompleted(); + Result result = aggregate.CheckReconciliationHasBeenStarted(); + if (result.IsFailed) + return result; + result = aggregate.CheckReconciliationNotAlreadyCompleted(); + if (result.IsFailed) + return result; - ReconciliationDomainEvents.OverallTotalsRecordedEvent overallTotalsRecordedEvent = new ReconciliationDomainEvents.OverallTotalsRecordedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, totalCount, totalValue, aggregate.TransactionDateTime); + ReconciliationDomainEvents.OverallTotalsRecordedEvent overallTotalsRecordedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, totalCount, totalValue, aggregate.TransactionDateTime); aggregate.ApplyAndAppend(overallTotalsRecordedEvent); + + return Result.Success(); } - public static void Authorise(this ReconciliationAggregate aggregate, String responseCode, String responseMessage) + public static Result Authorise(this ReconciliationAggregate aggregate, String responseCode, String responseMessage) { - aggregate.CheckReconciliationHasBeenStarted(); - aggregate.CheckReconciliationNotAlreadyCompleted(); - aggregate.CheckReconciliationNotAlreadyAuthorised(); - aggregate.CheckReconciliationNotAlreadyDeclined(); + if (aggregate.IsAuthorised || aggregate.IsDeclined) { + return Result.Success(); + } + + Result result = aggregate.CheckReconciliationHasBeenStarted(); + if (result.IsFailed) + return result; + result = aggregate.CheckReconciliationNotAlreadyCompleted(); + if (result.IsFailed) + return result; - ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent reconciliationHasBeenLocallyAuthorisedEvent = new ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, + ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent reconciliationHasBeenLocallyAuthorisedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, responseCode, responseMessage, aggregate.TransactionDateTime); aggregate.ApplyAndAppend(reconciliationHasBeenLocallyAuthorisedEvent); + return Result.Success(); } - - private static void CheckReconciliationNotAlreadyAuthorised(this ReconciliationAggregate aggregate) + + private static Result CheckReconciliationHasBeenAuthorisedOrDeclined(this ReconciliationAggregate aggregate) { - if (aggregate.IsAuthorised) + if (aggregate.IsAuthorised == false && aggregate.IsDeclined == false) { - throw new InvalidOperationException($"Reconciliation [{aggregate.AggregateId}] has already been authorised"); + return Result.Invalid($"Reconciliation [{aggregate.AggregateId}] has not been authorised or declined"); } + return Result.Success(); } - private static void CheckReconciliationNotAlreadyDeclined(this ReconciliationAggregate aggregate) + public static Result Decline(this ReconciliationAggregate aggregate,String responseCode, String responseMessage) { - if (aggregate.IsDeclined) - { - throw new InvalidOperationException($"Reconciliation [{aggregate.AggregateId}] has already been declined"); + if (aggregate.IsAuthorised || aggregate.IsDeclined) { + return Result.Success(); } - } - private static void CheckReconciliationHasBeenAuthorisedOrDeclined(this ReconciliationAggregate aggregate) - { - if (aggregate.IsAuthorised == false && aggregate.IsDeclined == false) - { - throw new InvalidOperationException($"Reconciliation [{aggregate.AggregateId}] has not been authorised or declined"); - } - } - - public static void Decline(this ReconciliationAggregate aggregate,String responseCode, String responseMessage) - { - aggregate.CheckReconciliationHasBeenStarted(); - aggregate.CheckReconciliationNotAlreadyCompleted(); - aggregate.CheckReconciliationNotAlreadyAuthorised(); - aggregate.CheckReconciliationNotAlreadyDeclined(); + Result result = aggregate.CheckReconciliationHasBeenStarted(); + if (result.IsFailed) + return result; + result = aggregate.CheckReconciliationNotAlreadyCompleted(); + if (result.IsFailed) + return result; - ReconciliationDomainEvents.ReconciliationHasBeenLocallyDeclinedEvent reconciliationHasBeenLocallyDeclinedEvent = new ReconciliationDomainEvents.ReconciliationHasBeenLocallyDeclinedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, + ReconciliationDomainEvents.ReconciliationHasBeenLocallyDeclinedEvent reconciliationHasBeenLocallyDeclinedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, responseCode, responseMessage, aggregate.TransactionDateTime); aggregate.ApplyAndAppend(reconciliationHasBeenLocallyDeclinedEvent); + + return Result.Success(); } - public static void CompleteReconciliation(this ReconciliationAggregate aggregate) + public static Result CompleteReconciliation(this ReconciliationAggregate aggregate) { - aggregate.CheckReconciliationHasBeenStarted(); - aggregate.CheckReconciliationNotAlreadyCompleted(); - aggregate.CheckReconciliationHasBeenAuthorisedOrDeclined(); + if (aggregate.IsCompleted) + return Result.Success(); + + Result result = aggregate.CheckReconciliationHasBeenAuthorisedOrDeclined(); + if (result.IsFailed) + return result; - ReconciliationDomainEvents.ReconciliationHasCompletedEvent reconciliationHasCompletedEvent = - new ReconciliationDomainEvents.ReconciliationHasCompletedEvent(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, aggregate.TransactionDateTime); + ReconciliationDomainEvents.ReconciliationHasCompletedEvent reconciliationHasCompletedEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, aggregate.TransactionDateTime); aggregate.ApplyAndAppend(reconciliationHasCompletedEvent); + return Result.Success(); } } @@ -189,7 +192,7 @@ private ReconciliationAggregate(Guid aggregateId) public Guid EstateId { get; internal set; } - public Boolean IsStarted { get; internal set; } + public Boolean HasBeenStarted { get; internal set; } public Guid MerchantId { get; internal set; } diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index a80c27ee..d32f0377 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -27,8 +27,6 @@ namespace TransactionProcessor.BusinessLogic.Services{ public interface ITransactionDomainService { - #region Methods - Task> ProcessLogonTransaction(TransactionCommands.ProcessLogonTransactionCommand command, CancellationToken cancellationToken); @@ -43,9 +41,7 @@ Task ResendTransactionReceipt(TransactionCommands.ResendTransactionRecei Task CalculateFeesForTransaction(TransactionCommands.CalculateFeesForTransactionCommand command, CancellationToken cancellationToken); - - #endregion - + Task AddSettledMerchantFee(TransactionCommands.AddSettledMerchantFeeCommand command, CancellationToken cancellationToken); @@ -92,8 +88,6 @@ public TransactionDomainService(Func aggregateService, #endregion - #region Methods - public async Task> ProcessLogonTransaction(TransactionCommands.ProcessLogonTransactionCommand command, CancellationToken cancellationToken) { @@ -158,20 +152,30 @@ public async Task> ProcessRecon ReconciliationAggregate reconciliationAggregate = reconciliationResult.Data; Result validationResult = await this.TransactionValidationService.ValidateReconciliationTransaction(command.EstateId, command.MerchantId, command.DeviceIdentifier, cancellationToken); - reconciliationAggregate.StartReconciliation(command.TransactionDateTime, command.EstateId, command.MerchantId); + Result stateResult = reconciliationAggregate.StartReconciliation(command.TransactionDateTime, command.EstateId, command.MerchantId); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); - reconciliationAggregate.RecordOverallTotals(command.TransactionCount, command.TransactionValue); + stateResult = reconciliationAggregate.RecordOverallTotals(command.TransactionCount, command.TransactionValue); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); if (validationResult.IsSuccess && validationResult.Data.ResponseCode == TransactionResponseCode.Success) { // Record the successful validation - reconciliationAggregate.Authorise(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); + stateResult = reconciliationAggregate.Authorise(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); } else { // Record the failure - reconciliationAggregate.Decline(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); + stateResult = reconciliationAggregate.Decline(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); } - reconciliationAggregate.CompleteReconciliation(); + stateResult = reconciliationAggregate.CompleteReconciliation(); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); Result saveResult = await this.AggregateService.Save(reconciliationAggregate, cancellationToken); if (saveResult.IsFailed) @@ -538,13 +542,6 @@ private async Task> GetTransactionFeesForCalcula return feesForCalculation; } - /// - /// Adds the device to merchant. - /// - /// The estate identifier. - /// The merchant identifier. - /// The device identifier. - /// The cancellation token. private async Task AddDeviceToMerchant(Guid merchantId, String deviceIdentifier, CancellationToken cancellationToken) { @@ -555,10 +552,6 @@ private async Task AddDeviceToMerchant(Guid merchantId, await this.AggregateService.Save(merchantAggregate.Data, cancellationToken); } - /// - /// Generates the transaction reference. - /// - /// [ExcludeFromCodeCoverage] private String GenerateTransactionReference() { Int64 i = 1; @@ -609,9 +602,7 @@ private async Task> ProcessMessageWithOperator(Models.M return CreateFailedResult(new OperatorResponse { IsSuccessful = false, ResponseCode = "9999", ResponseMessage = e.GetCombinedExceptionMessages() }); } } - - #endregion - + internal static Result CreateFailedResult(T resultData) { return new Result { IsSuccess = false, Data = resultData }; }