diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs index a06e5ad..f9f861e 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/ContractDomainServiceTests.cs @@ -23,7 +23,8 @@ public class ContractDomainServiceTests { public ContractDomainServiceTests() { this.AggregateService = new Mock(); this.EventStoreContext = new Mock(); - this.DomainService = new ContractDomainService(this.AggregateService.Object, this.EventStoreContext.Object); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.DomainService = new ContractDomainService(AggregateServiceResolver, this.EventStoreContext.Object); } [Fact] diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/EstateDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/EstateDomainServiceTests.cs index c06a171..6e3386f 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/EstateDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/EstateDomainServiceTests.cs @@ -24,7 +24,8 @@ public class EstateDomainServiceTests { public EstateDomainServiceTests() { this.AggregateService= new Mock(); this.SecurityServiceClient = new Mock(); - this.DomainService = new EstateDomainService(this.AggregateService.Object, this.SecurityServiceClient.Object); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.DomainService = new EstateDomainService(AggregateServiceResolver, this.SecurityServiceClient.Object); } [Fact] diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/FloatDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/FloatDomainServiceTests.cs index b5988be..5dc88ca 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/FloatDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/FloatDomainServiceTests.cs @@ -31,8 +31,8 @@ public FloatDomainServiceTests(){ Logger.Initialise(NullLogger.Instance); this.AggregateService = new Mock(); - - this.FloatDomainService = new FloatDomainService(this.AggregateService.Object); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.FloatDomainService = new FloatDomainService(AggregateServiceResolver); } [Fact] diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs index da874fa..ea03a41 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/MerchantDomainServiceTests.cs @@ -42,10 +42,13 @@ public MerchantDomainServiceTests() { Logger.Initialise(new NullLogger()); + + this.AggregateService = new Mock(); this.SecurityServiceClient = new Mock(); this.EventStoreContext = new Mock(); - this.DomainService = new MerchantDomainService(this.AggregateService.Object, + Func aggregateServiceResolver = () => this.AggregateService.Object; + this.DomainService = new MerchantDomainService(aggregateServiceResolver, this.SecurityServiceClient.Object, this.EventStoreContext.Object); } @@ -425,11 +428,8 @@ public async Task MerchantDomainService_MakeMerchantDeposit_DepositIsMade() this.AggregateService.Setup(e => e.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.Aggregates.CreatedEstateAggregate()); - this.AggregateService.Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) + this.AggregateService.Setup(m => m.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedMerchantAggregate())); - this.AggregateService - .Setup(m => m.Save(It.IsAny(), It.IsAny())) - .ReturnsAsync(Result.Success); this.AggregateService .Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) @@ -523,12 +523,9 @@ public async Task MerchantDomainService_MakeMerchantDeposit_NoDepositsYet_Deposi this.AggregateService.Setup(e => e.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.Aggregates.CreatedEstateAggregate()); - this.AggregateService.Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) + this.AggregateService.Setup(m => m.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedMerchantAggregate())); - this.AggregateService - .Setup(m => m.Save(It.IsAny(), It.IsAny())) - .ReturnsAsync(Result.Success); - + this.AggregateService .Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(TestData.Aggregates.EmptyMerchantDepositListAggregate)); @@ -581,7 +578,7 @@ public async Task MerchantDomainService_SwapMerchantDevice_EstateNotCreated_Erro .Setup(m => m.Save(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success); - var result = await this.DomainService.SwapMerchantDevice(TestData.Commands.SwapMerchantDeviceCommand, CancellationToken.None); + Result result = await this.DomainService.SwapMerchantDevice(TestData.Commands.SwapMerchantDeviceCommand, CancellationToken.None); result.IsFailed.ShouldBeTrue(); } @@ -590,12 +587,9 @@ public async Task MerchantDomainService_MakeMerchantWithdrawal_WithdrawalIsMade( this.AggregateService.Setup(e => e.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(TestData.Aggregates.CreatedEstateAggregate()); - this.AggregateService.Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) + this.AggregateService.Setup(m => m.Get(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedMerchantAggregate())); - this.AggregateService - .Setup(m => m.Save(It.IsAny(), It.IsAny())) - .ReturnsAsync(Result.Success); - + this.AggregateService .Setup(m => m.GetLatest(It.IsAny(), It.IsAny())) .ReturnsAsync(Result.Success(TestData.Aggregates.CreatedMerchantDepositListAggregate())); diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs index 63f6a8a..09e5690 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/MerchantStatementDomainServiceTests.cs @@ -31,7 +31,8 @@ public MerchantStatementDomainServiceTests() { this.StatementBuilder = new Mock(); this.MessagingServiceClient = new Mock(); this.SecurityServiceClient = new Mock(); - this.DomainService = new MerchantStatementDomainService(this.AggregateService.Object, this.StatementBuilder.Object, this.MessagingServiceClient.Object, this.SecurityServiceClient.Object); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.DomainService = new MerchantStatementDomainService(AggregateServiceResolver, this.StatementBuilder.Object, this.MessagingServiceClient.Object, this.SecurityServiceClient.Object); IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/OperatorDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/OperatorDomainServiceTests.cs index 9c10522..f46ac28 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/OperatorDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/OperatorDomainServiceTests.cs @@ -20,7 +20,8 @@ public class OperatorDomainServiceTests{ public OperatorDomainServiceTests(){ this.AggregateService = new Mock(); - this.OperatorDomainService = new OperatorDomainService(this.AggregateService.Object); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.OperatorDomainService = new OperatorDomainService(AggregateServiceResolver); } [Fact] diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs index 92b2027..3075ed0 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/SettlementDomainServiceTests.cs @@ -28,9 +28,9 @@ public class SettlementDomainServiceTests public SettlementDomainServiceTests() { this.AggregateService = new Mock(); - + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; this.settlementDomainService = - new SettlementDomainService(this.AggregateService.Object); + new SettlementDomainService(AggregateServiceResolver); IConfigurationRoot configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(TestData.DefaultAppSettings).Build(); ConfigurationReader.Initialise(configurationRoot); diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index 5c908ef..28d80f8 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -58,8 +58,8 @@ public TransactionDomainServiceTests(){ this.FeeCalculationManager = new Mock(); this.TransactionReceiptBuilder = new Mock(); this.MessagingServiceClient = new Mock(); - - this.TransactionDomainService = new TransactionDomainService(this.AggregateService.Object, + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; + this.TransactionDomainService = new TransactionDomainService(AggregateServiceResolver, operatorProxyResolver, this.TransactionValidationService.Object, this.SecurityServiceClient.Object, diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionValidationServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionValidationServiceTests.cs index 2048559..5b17929 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionValidationServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionValidationServiceTests.cs @@ -37,10 +37,10 @@ public TransactionValidationServiceTests() { this.SecurityServiceClient = new Mock(); this.EventStoreContext = new Mock(); this.AggregateService = new Mock(); - + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; this.SecurityServiceClient.Setup(s => s.GetToken(It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Result.Success(TestData.TokenResponse())); - this.TransactionValidationService = new TransactionValidationService(this.EventStoreContext.Object, this.AggregateService.Object); + this.TransactionValidationService = new TransactionValidationService(this.EventStoreContext.Object, AggregateServiceResolver); } [Fact] diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/VoucherDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/VoucherDomainServiceTests.cs index cba39e3..c1f4de1 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/VoucherDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/VoucherDomainServiceTests.cs @@ -40,6 +40,7 @@ public VoucherDomainServiceTests() { Logger.Initialise(NullLogger.Instance); this.AggregateService = new Mock(); + IAggregateService AggregateServiceResolver() => this.AggregateService.Object; this.DbContextFactory = new Mock>(); this.Context = this.GetContext(Guid.NewGuid().ToString("N")); var services = new ServiceCollection(); @@ -47,7 +48,7 @@ public VoucherDomainServiceTests() { var serviceProvider = services.BuildServiceProvider(); var scope = serviceProvider.CreateScope(); this.DbContextFactory.Setup(d => d.Resolve(It.IsAny(), It.IsAny())).Returns(new ResolvedDbContext(scope)); - this.VoucherDomainService = new VoucherDomainService(this.AggregateService.Object, DbContextFactory.Object); + this.VoucherDomainService = new VoucherDomainService(AggregateServiceResolver, DbContextFactory.Object); } [Fact] diff --git a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs index b708f44..5be8f7d 100644 --- a/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/ContractDomainService.cs @@ -42,8 +42,8 @@ public class ContractDomainService : IContractDomainService #region Constructors - public ContractDomainService(IAggregateService aggregateService, IEventStoreContext context) { - this.AggregateService = aggregateService; + public ContractDomainService(Func aggregateService, IEventStoreContext context) { + this.AggregateService = aggregateService(); this.Context = context; } diff --git a/TransactionProcessor.BusinessLogic/Services/EstateDomainService.cs b/TransactionProcessor.BusinessLogic/Services/EstateDomainService.cs index 89b14a6..b5125f5 100644 --- a/TransactionProcessor.BusinessLogic/Services/EstateDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/EstateDomainService.cs @@ -42,9 +42,9 @@ public class EstateDomainService : IEstateDomainService #region Constructors - public EstateDomainService(IAggregateService aggregateService, + public EstateDomainService(Func aggregateService, ISecurityServiceClient securityServiceClient) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); this.SecurityServiceClient = securityServiceClient; } diff --git a/TransactionProcessor.BusinessLogic/Services/FloatDomainService.cs b/TransactionProcessor.BusinessLogic/Services/FloatDomainService.cs index 59d043c..f76e375 100644 --- a/TransactionProcessor.BusinessLogic/Services/FloatDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/FloatDomainService.cs @@ -29,9 +29,9 @@ Task RecordTransaction(FloatActivityCommands.RecordTransactionCommand co public class FloatDomainService : IFloatDomainService{ private readonly IAggregateService AggregateService; - public FloatDomainService(IAggregateService aggregateService) + public FloatDomainService(Func aggregateService) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); } private async Task ApplyFloatUpdates(Func action, Guid floatId, CancellationToken cancellationToken, Boolean isNotFoundError = true) diff --git a/TransactionProcessor.BusinessLogic/Services/MerchantDomainService.cs b/TransactionProcessor.BusinessLogic/Services/MerchantDomainService.cs index ab5dc20..793f923 100644 --- a/TransactionProcessor.BusinessLogic/Services/MerchantDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/MerchantDomainService.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; +using Newtonsoft.Json; using SecurityService.Client; using SecurityService.DataTransferObjects; using Shared.DomainDrivenDesign.EventSourcing; @@ -13,11 +8,19 @@ using Shared.Results; using Shared.ValueObjects; using SimpleResults; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using SecurityService.DataTransferObjects.Responses; using TransactionProcessor.Aggregates; using TransactionProcessor.BusinessLogic.Requests; +using TransactionProcessor.Database.Entities; using TransactionProcessor.Models.Estate; using TransactionProcessor.Models.Merchant; using TransactionProcessor.ProjectionEngine.State; +using Estate = TransactionProcessor.Models.Estate.Estate; namespace TransactionProcessor.BusinessLogic.Services { @@ -55,11 +58,11 @@ public class MerchantDomainService : IMerchantDomainService #region Constructors - public MerchantDomainService(IAggregateService aggregateService, + public MerchantDomainService(Func aggregateService, ISecurityServiceClient securityServiceClient, IEventStoreContext eventStoreContext) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); this.SecurityServiceClient = securityServiceClient; this.EventStoreContext = eventStoreContext; } @@ -68,111 +71,105 @@ public MerchantDomainService(IAggregateService aggregateService, #region Methods - private async Task ApplyUpdates(Func<(EstateAggregate estateAggregate, MerchantAggregate merchantAggregate), Task> action, Guid estateId, Guid merchantId, CancellationToken cancellationToken, Boolean isNotFoundError = true) + private async Task> GetAggregateOrFailure(Func>> fetchFunc, + Guid aggregateId, + CancellationToken cancellationToken, + Boolean isNotFoundError = true) where TAggregate : Aggregate, new() { - try - { - Result getEstateResult = await this.AggregateService.Get(estateId, cancellationToken); - if (getEstateResult.IsFailed) { - return ResultHelpers.CreateFailure(getEstateResult); - } - EstateAggregate estateAggregate = getEstateResult.Data; - Result getMerchantResult = await this.AggregateService.GetLatest(merchantId, cancellationToken); - Result merchantAggregateResult = - DomainServiceHelper.HandleGetAggregateResult(getMerchantResult, merchantId, isNotFoundError); - if (merchantAggregateResult.IsFailed) - return ResultHelpers.CreateFailure(merchantAggregateResult); + Result result = await fetchFunc(cancellationToken); + return result.IsFailed switch { + true => DomainServiceHelper.HandleGetAggregateResult(result, aggregateId, isNotFoundError), + _ => Result.Success(result.Data) + }; + } + + public async Task AddDeviceToMerchant(MerchantCommands.AddMerchantDeviceCommand command, CancellationToken cancellationToken) { + try { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); + + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - MerchantAggregate merchantAggregate = merchantAggregateResult.Data; + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - Result result = await action((estateAggregate, merchantAggregate)); + Result result = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); if (result.IsFailed) return ResultHelpers.CreateFailure(result); + Guid deviceId = Guid.NewGuid(); + merchantAggregate.AddDevice(deviceId, command.RequestDto.DeviceIdentifier); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); if (saveResult.IsFailed) return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; } - catch (Exception ex) - { + catch (Exception ex) { return Result.Failure(ex.GetExceptionMessages()); } } - public async Task AddDeviceToMerchant(MerchantCommands.AddMerchantDeviceCommand command, CancellationToken cancellationToken) + public async Task AssignOperatorToMerchant(MerchantCommands.AssignOperatorToMerchantCommand command, + CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { - - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + try { + Result estateResult = await GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Guid deviceId = Guid.NewGuid(); - aggregates.merchantAggregate.AddDevice(deviceId, command.RequestDto.DeviceIdentifier); + Result merchantResult = await GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - return Result.Success(); - }, - command.EstateId, command.MerchantId, cancellationToken); + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return Result.Success(); - } - - public async Task AssignOperatorToMerchant(MerchantCommands.AssignOperatorToMerchantCommand command, - CancellationToken cancellationToken) - { + Result result = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + // Is the operator valid for this estate + Estate estate = estateAggregate.GetEstate(); + Models.Estate.Operator @operator = estate.Operators?.SingleOrDefault(o => o.OperatorId == command.RequestDto.OperatorId); + if (@operator == null) { + return Result.Invalid($"Operator Id {command.RequestDto.OperatorId} is not supported on Estate [{estate.Name}]"); + } - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + // TODO: Reintroduce when we have an Operator Aggregate + // https://github.com/TransactionProcessing/EstateManagement/issues/558 + // Operator has been validated, now check the rules of the operator against the passed in data + //if (@operator.RequireCustomMerchantNumber) { + // // requested addition must have a merchant number supplied + // if (String.IsNullOrEmpty(command.RequestDto.MerchantNumber)) { + // throw new InvalidOperationException($"Operator Id {command.RequestDto.OperatorId} requires that a merchant number is provided"); + // } + //} + + //if (@operator.RequireCustomTerminalNumber) { + // // requested addition must have a terminal number supplied + // if (String.IsNullOrEmpty(command.RequestDto.TerminalNumber)) { + // throw new InvalidOperationException($"Operator Id {command.RequestDto.OperatorId} requires that a terminal number is provided"); + // } + //} + + // Assign the operator + // TODO: Swap second parameter to name + merchantAggregate.AssignOperator(command.RequestDto.OperatorId, @operator.Name, command.RequestDto.MerchantNumber, command.RequestDto.TerminalNumber); - // Is the operator valid for this estate - Estate estate = aggregates.estateAggregate.GetEstate(); - Models.Estate.Operator @operator = estate.Operators?.SingleOrDefault(o => o.OperatorId == command.RequestDto.OperatorId); - if (@operator == null) - { - return Result.Invalid($"Operator Id {command.RequestDto.OperatorId} is not supported on Estate [{estate.Name}]"); - } - - // TODO: Reintroduce when we have an Operator Aggregate - // https://github.com/TransactionProcessing/EstateManagement/issues/558 - // Operator has been validated, now check the rules of the operator against the passed in data - //if (@operator.RequireCustomMerchantNumber) { - // // requested addition must have a merchant number supplied - // if (String.IsNullOrEmpty(command.RequestDto.MerchantNumber)) { - // throw new InvalidOperationException($"Operator Id {command.RequestDto.OperatorId} requires that a merchant number is provided"); - // } - //} - - //if (@operator.RequireCustomTerminalNumber) { - // // requested addition must have a terminal number supplied - // if (String.IsNullOrEmpty(command.RequestDto.TerminalNumber)) { - // throw new InvalidOperationException($"Operator Id {command.RequestDto.OperatorId} requires that a terminal number is provided"); - // } - //} - - // Assign the operator - // TODO: Swap second parameter to name - aggregates.merchantAggregate.AssignOperator(command.RequestDto.OperatorId, @operator.Name, command.RequestDto.MerchantNumber, command.RequestDto.TerminalNumber); - - return Result.Success(); - }, - command.EstateId, command.MerchantId, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } } private SettlementSchedule ConvertSettlementSchedule(DataTransferObjects.Responses.Merchant.SettlementSchedule settlementSchedule) => @@ -186,274 +183,326 @@ private SettlementSchedule ConvertSettlementSchedule(DataTransferObjects.Respons public async Task CreateMerchant(MerchantCommands.CreateMerchantCommand command, CancellationToken cancellationToken) { - // Check if we have been sent a merchant id to use - Guid merchantId = command.RequestDto.MerchantId ?? Guid.NewGuid(); - - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { - if (aggregates.estateAggregate.IsCreated == false) - { - return Result.Forbidden($"Estate Id {command.EstateId} has not been created"); - } - - if (aggregates.merchantAggregate.IsCreated) - { - aggregates.merchantAggregate.Create(command.EstateId, command.RequestDto.Name, aggregates.merchantAggregate.DateCreated); - } - else - { - aggregates.merchantAggregate.Create(command.EstateId, command.RequestDto.Name, command.RequestDto.CreatedDateTime.GetValueOrDefault(DateTime.Now)); - aggregates.merchantAggregate.GenerateReference(); - - // Add the address - aggregates.merchantAggregate.AddAddress(command.RequestDto.Address.AddressLine1, command.RequestDto.Address.AddressLine2, command.RequestDto.Address.AddressLine3, - command.RequestDto.Address.AddressLine4, command.RequestDto.Address.Town, command.RequestDto.Address.Region, - command.RequestDto.Address.PostalCode, command.RequestDto.Address.Country); - - // Add the contact - aggregates.merchantAggregate.AddContact(command.RequestDto.Contact.ContactName, command.RequestDto.Contact.PhoneNumber, command.RequestDto.Contact.EmailAddress); - - // Set the settlement schedule - SettlementSchedule settlementSchedule = ConvertSettlementSchedule(command.RequestDto.SettlementSchedule); - aggregates.merchantAggregate.SetSettlementSchedule(settlementSchedule); - } - return Result.Success(); - }, - command.EstateId, merchantId, cancellationToken, false); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - return Result.Success(); - } + try { + // Check if we have been sent a merchant id to use + Guid merchantId = command.RequestDto.MerchantId ?? Guid.NewGuid(); - public async Task CreateMerchantUser(MerchantCommands.CreateMerchantUserCommand command, CancellationToken cancellationToken) - { + Result estateResult = await GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - CreateUserRequest createUserRequest = new CreateUserRequest - { - EmailAddress = command.RequestDto.EmailAddress, - FamilyName = command.RequestDto.FamilyName, - GivenName = command.RequestDto.GivenName, - MiddleName = command.RequestDto.MiddleName, - Password = command.RequestDto.Password, - PhoneNumber = "123456", // Is this really needed :| - Roles = new List(), - Claims = new Dictionary() - }; + EstateAggregate estateAggregate = estateResult.Data; + // Estate Id is a valid estate + if (estateAggregate.IsCreated == false) + { + return Result.Invalid($"Estate Id {estateAggregate.AggregateId} has not been created"); + } - String merchantRoleName = Environment.GetEnvironmentVariable("MerchantRoleName"); - createUserRequest.Roles.Add(String.IsNullOrEmpty(merchantRoleName) ? "Merchant" : merchantRoleName); - createUserRequest.Claims.Add("estateId", command.EstateId.ToString()); - createUserRequest.Claims.Add("merchantId", command.MerchantId.ToString()); + Result merchantResult = await GetAggregateOrFailure(ct => this.AggregateService.GetLatest(merchantId, ct), merchantId, cancellationToken, false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + MerchantAggregate merchantAggregate = merchantResult.Data; - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.Create(command.EstateId, command.RequestDto.Name, command.RequestDto.CreatedDateTime.GetValueOrDefault(DateTime.Now)); + merchantAggregate.GenerateReference(); - Result createUserResult = await this.SecurityServiceClient.CreateUser(createUserRequest, cancellationToken); - if (createUserResult.IsFailed) - return ResultHelpers.CreateFailure(createUserResult); + // Add the address + merchantAggregate.AddAddress(command.RequestDto.Address.AddressLine1, command.RequestDto.Address.AddressLine2, command.RequestDto.Address.AddressLine3, command.RequestDto.Address.AddressLine4, command.RequestDto.Address.Town, command.RequestDto.Address.Region, command.RequestDto.Address.PostalCode, command.RequestDto.Address.Country); - var userDetailsResult = await this.SecurityServiceClient.GetUsers(createUserRequest.EmailAddress, cancellationToken); - if (userDetailsResult.IsFailed) - return ResultHelpers.CreateFailure(userDetailsResult); + // Add the contact + merchantAggregate.AddContact(command.RequestDto.Contact.ContactName, command.RequestDto.Contact.PhoneNumber, command.RequestDto.Contact.EmailAddress); - var user = userDetailsResult.Data.SingleOrDefault(); - if (user == null) - return Result.Failure($"Unable to get user details for username {createUserRequest.EmailAddress}"); + // Set the settlement schedule + SettlementSchedule settlementSchedule = ConvertSettlementSchedule(command.RequestDto.SettlementSchedule); + merchantAggregate.SetSettlementSchedule(settlementSchedule); - // Add the user to the aggregate - aggregates.merchantAggregate.AddSecurityUser(user.UserId, - command.RequestDto.EmailAddress); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - // TODO: add a delete user here in case the aggregate add fails... + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } + } - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + public async Task CreateMerchantUser(MerchantCommands.CreateMerchantUserCommand command, CancellationToken cancellationToken) + { + try { + CreateUserRequest createUserRequest = new() { + EmailAddress = command.RequestDto.EmailAddress, + FamilyName = command.RequestDto.FamilyName, + GivenName = command.RequestDto.GivenName, + MiddleName = command.RequestDto.MiddleName, + Password = command.RequestDto.Password, + PhoneNumber = "123456", // Is this really needed :| + Roles = new List(), + Claims = new Dictionary() + }; + + String merchantRoleName = Environment.GetEnvironmentVariable("MerchantRoleName"); + createUserRequest.Roles.Add(String.IsNullOrEmpty(merchantRoleName) ? "Merchant" : merchantRoleName); + createUserRequest.Claims.Add("estateId", command.EstateId.ToString()); + createUserRequest.Claims.Add("merchantId", command.MerchantId.ToString()); + + Result estateResult = await GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); + + Result merchantResult = await GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; + + Result validateResult = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (validateResult.IsFailed) + return ResultHelpers.CreateFailure(validateResult); + + Result createUserResult = await this.SecurityServiceClient.CreateUser(createUserRequest, cancellationToken); + if (createUserResult.IsFailed) + return ResultHelpers.CreateFailure(createUserResult); + + Result> userDetailsResult = await this.SecurityServiceClient.GetUsers(createUserRequest.EmailAddress, cancellationToken); + if (userDetailsResult.IsFailed) + return ResultHelpers.CreateFailure(userDetailsResult); + + UserDetails user = userDetailsResult.Data.SingleOrDefault(); + if (user == null) + return Result.Failure($"Unable to get user details for username {createUserRequest.EmailAddress}"); + + // Add the user to the aggregate + merchantAggregate.AddSecurityUser(user.UserId, command.RequestDto.EmailAddress); + + // TODO: add a delete user here in case the aggregate add fails... - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task MakeMerchantDeposit(MerchantCommands.MakeMerchantDepositCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - Result getDepositListResult = await this.AggregateService.GetLatest(command.MerchantId, cancellationToken); - Result merchantDepositListAggregateResult = - DomainServiceHelper.HandleGetAggregateResult(getDepositListResult, command.MerchantId, false); - if (merchantDepositListAggregateResult.IsFailed) - return ResultHelpers.CreateFailure(merchantDepositListAggregateResult); + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - MerchantDepositListAggregate merchantDepositListAggregate = merchantDepositListAggregateResult.Data; - if (merchantDepositListAggregate.IsCreated == false) - { - merchantDepositListAggregate.Create(aggregates.merchantAggregate, command.RequestDto.DepositDateTime); - } + Result validateResult = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (validateResult.IsFailed) + return ResultHelpers.CreateFailure(validateResult); - PositiveMoney amount = PositiveMoney.Create(Money.Create(command.RequestDto.Amount)); - MerchantDepositSource depositSource = command.DepositSource switch - { - DataTransferObjects.Requests.Merchant.MerchantDepositSource.Manual => Models.Merchant.MerchantDepositSource.Manual, - _ => Models.Merchant.MerchantDepositSource.Automatic, - }; - merchantDepositListAggregate.MakeDeposit(depositSource, command.RequestDto.Reference, command.RequestDto.DepositDateTime, amount); + Result getDepositListResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken, false).ConfigureAwait(false); + if (getDepositListResult.IsFailed) + return ResultHelpers.CreateFailure(getDepositListResult); - Result saveResult = await this.AggregateService.Save(merchantDepositListAggregate, cancellationToken); - if (saveResult.IsFailed) - return ResultHelpers.CreateFailure(saveResult); + MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult.Data; + if (merchantDepositListAggregate.IsCreated == false) + { + merchantDepositListAggregate.Create(merchantAggregate, command.RequestDto.DepositDateTime); + } - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + PositiveMoney amount = PositiveMoney.Create(Money.Create(command.RequestDto.Amount)); + MerchantDepositSource depositSource = command.DepositSource switch + { + DataTransferObjects.Requests.Merchant.MerchantDepositSource.Manual => Models.Merchant.MerchantDepositSource.Manual, + _ => Models.Merchant.MerchantDepositSource.Automatic, + }; + merchantDepositListAggregate.MakeDeposit(depositSource, command.RequestDto.Reference, command.RequestDto.DepositDateTime, amount); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantDepositListAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task MakeMerchantWithdrawal(MerchantCommands.MakeMerchantWithdrawalCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); + + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; + + Result validateResult = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (validateResult.IsFailed) + return ResultHelpers.CreateFailure(validateResult); + + Result getDepositListResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken).ConfigureAwait(false); + if (getDepositListResult.IsFailed) + return ResultHelpers.CreateFailure(getDepositListResult); + + MerchantDepositListAggregate merchantDepositListAggregate = getDepositListResult.Data; + if (merchantDepositListAggregate.IsCreated == false) { + return Result.Invalid($"Merchant [{command.MerchantId}] has not made any deposits yet"); + } - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); - - Result getDepositListResult = await this.AggregateService.GetLatest(command.MerchantId, cancellationToken); - Result merchantDepositListAggregateResult = - DomainServiceHelper.HandleGetAggregateResult(getDepositListResult, command.MerchantId, false); - if (merchantDepositListAggregateResult.IsFailed) - return ResultHelpers.CreateFailure(merchantDepositListAggregateResult); - - MerchantDepositListAggregate merchantDepositListAggregate = merchantDepositListAggregateResult.Data; - if (merchantDepositListAggregate.IsCreated == false) - { - return Result.Invalid($"Merchant [{command.MerchantId}] has not made any deposits yet"); - } - - // Now we need to check the merchants balance to ensure they have funds to withdraw - Result getBalanceResult = await this.EventStoreContext.GetPartitionStateFromProjection("MerchantBalanceProjection", $"MerchantBalance-{command.MerchantId:N}", cancellationToken); - if (getBalanceResult.IsFailed) - { - Result.Invalid($"Failed to get Merchant Balance."); - } - - MerchantBalanceProjectionState1 projectionState = JsonConvert.DeserializeObject(getBalanceResult.Data); + // Now we need to check the merchants balance to ensure they have funds to withdraw + Result getBalanceResult = await this.EventStoreContext.GetPartitionStateFromProjection("MerchantBalanceProjection", $"MerchantBalance-{command.MerchantId:N}", cancellationToken); + if (getBalanceResult.IsFailed) + { + Result.Invalid($"Failed to get Merchant Balance."); + } - if (command.RequestDto.Amount > projectionState.merchant.balance) - { - return Result.Invalid($"Not enough credit available for withdrawal of [{command.RequestDto.Amount}]. Balance is {projectionState.merchant.balance}"); - } + MerchantBalanceProjectionState1 projectionState = JsonConvert.DeserializeObject(getBalanceResult.Data); - // If we are here we have enough credit to withdraw - PositiveMoney amount = PositiveMoney.Create(Money.Create(command.RequestDto.Amount)); + if (command.RequestDto.Amount > projectionState.merchant.balance) + { + return Result.Invalid($"Not enough credit available for withdrawal of [{command.RequestDto.Amount}]. Balance is {projectionState.merchant.balance}"); + } - merchantDepositListAggregate.MakeWithdrawal(command.RequestDto.WithdrawalDateTime, amount); - Result saveResult = await this.AggregateService.Save(merchantDepositListAggregate, cancellationToken); - if (saveResult.IsFailed) - return ResultHelpers.CreateFailure(saveResult); + // If we are here we have enough credit to withdraw + PositiveMoney amount = PositiveMoney.Create(Money.Create(command.RequestDto.Amount)); - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + merchantDepositListAggregate.MakeWithdrawal(command.RequestDto.WithdrawalDateTime, amount); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantDepositListAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } - /// - /// The token response - /// - //private TokenResponse TokenResponse; - public async Task AddContractToMerchant(MerchantCommands.AddMerchantContractCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - var getContractResult = await this.AggregateService.Get(command.RequestDto.ContractId, cancellationToken); - if (getContractResult.IsFailed) { - return ResultHelpers.CreateFailure(getContractResult); - } + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - ContractAggregate contractAggregate = getContractResult.Data; - if (contractAggregate.IsCreated == false) - { - return Result.Invalid($"Contract Id {command.RequestDto.ContractId} has not been created"); - } + Result validateResult = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (validateResult.IsFailed) + return ResultHelpers.CreateFailure(validateResult); - aggregates.merchantAggregate.AddContract(contractAggregate); + Result getContractResult = await this.AggregateService.Get(command.RequestDto.ContractId, cancellationToken); + if (getContractResult.IsFailed) { + return ResultHelpers.CreateFailure(getContractResult); + } - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + ContractAggregate contractAggregate = getContractResult.Data; + if (contractAggregate.IsCreated == false) { + return Result.Invalid($"Contract Id {command.RequestDto.ContractId} has not been created"); + } - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.AddContract(contractAggregate); - return Result.Success(); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task UpdateMerchant(MerchantCommands.UpdateMerchantCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; + + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); if (result.IsFailed) return ResultHelpers.CreateFailure(result); - aggregates.merchantAggregate.UpdateMerchant(command.RequestDto.Name); + merchantAggregate.UpdateMerchant(command.RequestDto.Name); SettlementSchedule settlementSchedule = ConvertSettlementSchedule(command.RequestDto.SettlementSchedule); - aggregates.merchantAggregate.SetSettlementSchedule(settlementSchedule); + merchantAggregate.SetSettlementSchedule(settlementSchedule); - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task AddMerchantAddress(MerchantCommands.AddMerchantAddressCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; + + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - aggregates.merchantAggregate.AddAddress(command.RequestDto.AddressLine1, + merchantAggregate.AddAddress(command.RequestDto.AddressLine1, command.RequestDto.AddressLine2, command.RequestDto.AddressLine3, command.RequestDto.AddressLine4, @@ -462,26 +511,39 @@ public async Task AddMerchantAddress(MerchantCommands.AddMerchantAddress command.RequestDto.PostalCode, command.RequestDto.Country); - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task UpdateMerchantAddress(MerchantCommands.UpdateMerchantAddressCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; + + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - aggregates.merchantAggregate.UpdateAddress(command.AddressId, + merchantAggregate.UpdateAddress(command.AddressId, command.RequestDto.AddressLine1, command.RequestDto.AddressLine2, command.RequestDto.AddressLine3, @@ -491,103 +553,149 @@ public async Task UpdateMerchantAddress(MerchantCommands.UpdateMerchantA command.RequestDto.PostalCode, command.RequestDto.Country); - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task AddMerchantContact(MerchantCommands.AddMerchantContactCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); + + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - aggregates.merchantAggregate.AddContact(command.RequestDto.ContactName, + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); + + merchantAggregate.AddContact(command.RequestDto.ContactName, command.RequestDto.PhoneNumber, command.RequestDto.EmailAddress); - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); - - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } - public async Task UpdateMerchantContact(MerchantCommands.UpdateMerchantContactCommand command, CancellationToken cancellationToken) - { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + public async Task UpdateMerchantContact(MerchantCommands.UpdateMerchantContactCommand command, + CancellationToken cancellationToken) { + try { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - aggregates.merchantAggregate.UpdateContact(command.ContactId, - command.RequestDto.ContactName, - command.RequestDto.EmailAddress, - command.RequestDto.PhoneNumber - ); ; + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + Result result = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.UpdateContact(command.ContactId, command.RequestDto.ContactName, command.RequestDto.EmailAddress, command.RequestDto.PhoneNumber); + ; - return Result.Success(); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task RemoveOperatorFromMerchant(MerchantCommands.RemoveOperatorFromMerchantCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - aggregates.merchantAggregate.RemoveOperator(command.OperatorId); + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + Result result = this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.RemoveOperator(command.OperatorId); - return Result.Success(); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + + return saveResult; + } + catch (Exception ex) { + return Result.Failure(ex.GetExceptionMessages()); + } } public async Task RemoveContractFromMerchant(MerchantCommands.RemoveMerchantContractCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - aggregates.merchantAggregate.RemoveContract(command.ContractId); + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.RemoveContract(command.ContractId); - return Result.Success(); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } private Result ValidateEstateAndMerchant(EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) @@ -610,23 +718,35 @@ private Result ValidateEstateAndMerchant(EstateAggregate estateAggregate, Mercha public async Task SwapMerchantDevice(MerchantCommands.SwapMerchantDeviceCommand command, CancellationToken cancellationToken) { - Result result = await ApplyUpdates( - async ((EstateAggregate estateAggregate, MerchantAggregate merchantAggregate) aggregates) => { + try + { + Result estateResult = await this.GetAggregateOrFailure(ct => this.AggregateService.Get(command.EstateId, ct), command.EstateId, cancellationToken).ConfigureAwait(false); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(estateResult); - Result result = - this.ValidateEstateAndMerchant(aggregates.estateAggregate, aggregates.merchantAggregate); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + Result merchantResult = await this.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(command.MerchantId, ct), command.MerchantId, cancellationToken); + if (estateResult.IsFailed) + return ResultHelpers.CreateFailure(merchantResult); - aggregates.merchantAggregate.SwapDevice(command.DeviceIdentifier, command.RequestDto.NewDeviceIdentifier); + EstateAggregate estateAggregate = estateResult.Data; + MerchantAggregate merchantAggregate = merchantResult.Data; - return Result.Success(); - }, command.EstateId, command.MerchantId, cancellationToken); + Result result = + this.ValidateEstateAndMerchant(estateAggregate, merchantAggregate); + if (result.IsFailed) + return ResultHelpers.CreateFailure(result); - if (result.IsFailed) - return ResultHelpers.CreateFailure(result); + merchantAggregate.SwapDevice(command.DeviceIdentifier, command.RequestDto.NewDeviceIdentifier); + Result saveResult = await this.AggregateService.Save(merchantAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); - return Result.Success(); + return saveResult; + } + catch (Exception ex) + { + return Result.Failure(ex.GetExceptionMessages()); + } } #endregion diff --git a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs index 062c4dd..c2ea717 100644 --- a/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/MerchantStatementDomainService.cs @@ -58,9 +58,9 @@ public class MerchantStatementDomainService : IMerchantStatementDomainService #region Constructors - public MerchantStatementDomainService(IAggregateService aggregateService, IStatementBuilder statementBuilder, + public MerchantStatementDomainService(Func aggregateService, IStatementBuilder statementBuilder, IMessagingServiceClient messagingServiceClient, ISecurityServiceClient securityServiceClient) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); this.StatementBuilder = statementBuilder; this.MessagingServiceClient = messagingServiceClient; this.SecurityServiceClient = securityServiceClient; diff --git a/TransactionProcessor.BusinessLogic/Services/OperatorDomainService.cs b/TransactionProcessor.BusinessLogic/Services/OperatorDomainService.cs index a7173e8..0dc4000 100644 --- a/TransactionProcessor.BusinessLogic/Services/OperatorDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/OperatorDomainService.cs @@ -23,8 +23,8 @@ public class OperatorDomainService : IOperatorDomainService { private readonly IAggregateService AggregateService; - public OperatorDomainService(IAggregateService aggregateService) { - this.AggregateService = aggregateService; + public OperatorDomainService(Func aggregateService) { + this.AggregateService = aggregateService(); } private async Task ApplyUpdates(Func<(EstateAggregate, OperatorAggregate), Result> action, Guid estateId, Guid operatorId, CancellationToken cancellationToken, Boolean isNotFoundError = true) diff --git a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs index 7146fa8..9a3d3b0 100644 --- a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs @@ -228,9 +228,9 @@ public async Task AddSettledFeeToSettlement(SettlementCommands.AddSettle return result; } - public SettlementDomainService(IAggregateService aggregateService) + public SettlementDomainService(Func aggregateService) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); } } } \ No newline at end of file diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index 7d9d6eb..f947c59 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -78,7 +78,7 @@ public class TransactionDomainService : ITransactionDomainService { #region Constructors - public TransactionDomainService(IAggregateService aggregateService, + public TransactionDomainService(Func aggregateService, Func operatorProxyResolver, ITransactionValidationService transactionValidationService, ISecurityServiceClient securityServiceClient, @@ -86,7 +86,7 @@ public TransactionDomainService(IAggregateService aggregateService, IFeeCalculationManager feeCalculationManager, ITransactionReceiptBuilder transactionReceiptBuilder, IMessagingServiceClient messagingServiceClient) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); this.OperatorProxyResolver = operatorProxyResolver; this.TransactionValidationService = transactionValidationService; this.SecurityServiceClient = securityServiceClient; diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs index a682901..77abb22 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionValidationService.cs @@ -48,10 +48,10 @@ public class TransactionValidationService : ITransactionValidationService{ #endregion public TransactionValidationService(IEventStoreContext eventStoreContext, - IAggregateService aggregateService) + Func aggregateService) { this.EventStoreContext = eventStoreContext; - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); } #region Methods diff --git a/TransactionProcessor.BusinessLogic/Services/VoucherDomainService.cs b/TransactionProcessor.BusinessLogic/Services/VoucherDomainService.cs index de16dac..952a78f 100644 --- a/TransactionProcessor.BusinessLogic/Services/VoucherDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/VoucherDomainService.cs @@ -73,10 +73,10 @@ public class VoucherDomainService : IVoucherDomainService #region Constructors - public VoucherDomainService(IAggregateService aggregateService, + public VoucherDomainService(Func aggregateService, IDbContextResolver resolver) { - this.AggregateService = aggregateService; + this.AggregateService = aggregateService(); this.Resolver = resolver; } #endregion diff --git a/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs b/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs index 854185b..e435b43 100644 --- a/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs +++ b/TransactionProcessor/Bootstrapper/RepositoryRegistry.cs @@ -38,6 +38,7 @@ public class RepositoryRegistry : ServiceRegistry { #region Constructors + private static Boolean CachedAggregatesAdded; /// /// Initializes a new instance of the class. /// @@ -66,7 +67,20 @@ public RepositoryRegistry() } this.AddTransient(); - + this.AddSingleton>(c => () => { + + IAggregateService aggregateService = Startup.ServiceProvider.GetService(); + if (CachedAggregatesAdded == false) + { + aggregateService.AddCachedAggregate(typeof(EstateAggregate), null); + aggregateService.AddCachedAggregate(typeof(ContractAggregate), null); + aggregateService.AddCachedAggregate(typeof(OperatorAggregate), null); + aggregateService.AddCachedAggregate(typeof(MerchantAggregate), null); + CachedAggregatesAdded=true; + } + + return aggregateService; + }); this.AddSingleton(); this.AddSingleton(); this.AddSingleton, diff --git a/TransactionProcessor/Extensions.cs b/TransactionProcessor/Extensions.cs index 26201da..1045627 100644 --- a/TransactionProcessor/Extensions.cs +++ b/TransactionProcessor/Extensions.cs @@ -85,10 +85,8 @@ public static void PreWarm(this IApplicationBuilder applicationBuilder) { applicationBuilder.ConfigureSubscriptionService(subscriptionWorkersRoot, eventStoreConnectionString, eventHandlerResolvers, Extensions.log, subscriptionRepositoryResolver).Wait(CancellationToken.None); // Setup the aggregate service caching - IAggregateService aggregateService = Startup.ServiceProvider.GetService(); - aggregateService.AddCachedAggregate(typeof(EstateAggregate),null); - aggregateService.AddCachedAggregate(typeof(ContractAggregate), null); - aggregateService.AddCachedAggregate(typeof(OperatorAggregate), null); + + IMediator mediator = Startup.Container.GetInstance(); StatementFilePollerService statementFilePollerService = new(mediator);