From 30d890512544388f9b390b4b989bdf5a63660e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Fri, 28 Apr 2017 14:25:52 +0200 Subject: [PATCH] Moved Integrationevent to Application folder Created basic structure for order process saga --- .../Commands/CancelOrderCommand.cs | 21 +++ .../Commands/ConfirmGracePeriodCommandMsg.cs | 15 +++ .../Commands/SubmitOrderCommandMsg.cs | 14 ++ .../Events/OrderStartedIntegrationEvent.cs | 2 +- .../IOrderingIntegrationEventService.cs | 5 +- .../OrderingIntegrationEventService.cs | 6 +- .../Application/Sagas/OrderProcessSaga.cs | 126 ++++++++++++++++++ .../Ordering.API/Application/Sagas/Saga.cs | 30 +++++ .../Ordering/Ordering.API/Ordering.API.csproj | 3 +- 9 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/SubmitOrderCommandMsg.cs rename src/Services/Ordering/Ordering.API/{ => Application}/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs (91%) rename src/Services/Ordering/Ordering.API/{ => Application}/IntegrationEvents/IOrderingIntegrationEventService.cs (74%) rename src/Services/Ordering/Ordering.API/{ => Application}/IntegrationEvents/OrderingIntegrationEventService.cs (89%) create mode 100644 src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs new file mode 100644 index 000000000..97ea6e5ef --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CancelOrderCommand.cs @@ -0,0 +1,21 @@ +using MediatR; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Threading.Tasks; + +namespace Ordering.API.Application.Commands +{ + public class CancelOrderCommand : IAsyncRequest + { + + [DataMember] + public int OrderNumber { get; private set; } + + public CancelOrderCommand(int orderNumber) + { + OrderNumber = orderNumber; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs new file mode 100644 index 000000000..a0767459e --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs @@ -0,0 +1,15 @@ +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.Application.IntegrationCommands.Commands +{ + public class ConfirmGracePeriodCommandMsg : IntegrationEvent + { + public int OrderNumber { get; private set; } + + //TODO: message should change to Integration command type once command bus is implemented + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/SubmitOrderCommandMsg.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/SubmitOrderCommandMsg.cs new file mode 100644 index 000000000..5a64a352d --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/SubmitOrderCommandMsg.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; + +namespace Ordering.API.Application.IntegrationCommands.Commands +{ + public class SubmitOrderCommandMsg : IntegrationEvent + { + public int OrderNumber { get; private set; } + //TODO: message should change to Integration command type once command bus is implemented + } +} diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs similarity index 91% rename from src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs rename to src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs index 8184483e5..82416db46 100644 --- a/src/Services/Ordering/Ordering.API/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStartedIntegrationEvent.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace Ordering.API.IntegrationEvents.Events +namespace Ordering.API.Application.IntegrationEvents.Events { // Integration Events notes: // An Event is “something that has happened in the past”, therefore its name has to be diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs similarity index 74% rename from src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs rename to src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs index 6538afbfd..28227a66f 100644 --- a/src/Services/Ordering/Ordering.API/IntegrationEvents/IOrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/IOrderingIntegrationEventService.cs @@ -1,10 +1,7 @@ using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; -namespace Ordering.API.IntegrationEvents +namespace Ordering.API.Application.IntegrationEvents { public interface IOrderingIntegrationEventService { diff --git a/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs similarity index 89% rename from src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs rename to src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs index 810c07bc9..95e0c379b 100644 --- a/src/Services/Ordering/Ordering.API/IntegrationEvents/OrderingIntegrationEventService.cs +++ b/src/Services/Ordering/Ordering.API/Application/IntegrationEvents/OrderingIntegrationEventService.cs @@ -1,16 +1,14 @@ using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Storage; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities; -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; using System; using System.Data.Common; using System.Threading.Tasks; -namespace Ordering.API.IntegrationEvents +namespace Ordering.API.Application.IntegrationEvents { public class OrderingIntegrationEventService : IOrderingIntegrationEventService { @@ -19,7 +17,7 @@ namespace Ordering.API.IntegrationEvents private readonly OrderingContext _orderingContext; private readonly IIntegrationEventLogService _eventLogService; - public OrderingIntegrationEventService (IEventBus eventBus, OrderingContext orderingContext, + public OrderingIntegrationEventService(IEventBus eventBus, OrderingContext orderingContext, Func integrationEventLogServiceFactory) { _orderingContext = orderingContext ?? throw new ArgumentNullException(nameof(orderingContext)); diff --git a/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs b/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs new file mode 100644 index 000000000..32df5b327 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs @@ -0,0 +1,126 @@ +using Autofac.Features.OwnedInstances; +using MediatR; +using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; +using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Idempotency; +using Ordering.API.Application.Commands; +using Ordering.API.Application.IntegrationCommands.Commands; +using Ordering.Domain.Exceptions; +using System; +using System.Threading.Tasks; + +namespace Ordering.API.Application.Sagas +{ + /// + /// Saga for handling the place order process + /// and which is started once the basket.api has + /// successfully processed the items ordered. + /// Saga provides a period of grace to give the customer + /// the opportunity to cancel the order before proceeding + /// with the validations. + /// + public class OrderProcessSaga : Saga, + IIntegrationEventHandler, + IIntegrationEventHandler, + IAsyncRequestHandler + { + private readonly IMediator _mediator; + private readonly Func> _dbContextFactory; + + public OrderProcessSaga( + Func> dbContextFactory, OrderingContext orderingContext, + IMediator mediator) + : base(orderingContext) + { + _dbContextFactory = dbContextFactory; + _mediator = mediator; + } + + /// + /// Command handler which starts the create order process + /// and initializes the saga + /// + /// + /// Integration command message which is sent by the + /// basket.api once it has successfully process the + /// order items. + /// + /// + public async Task Handle(SubmitOrderCommandMsg command) + { + var orderSaga = FindSagaById(command.OrderNumber); + CheckValidSagaId(orderSaga); + + // TODO: This handler should change to Integration command handler type once command bus is implemented + + // TODO: Send createOrder Command + + // TODO: Set saga timeout + } + + /// + /// Command handler which confirms that the grace period + /// has been completed and order has not been cancelled. + /// If so, the process continues for validation. + /// + /// + /// Integration command message which is sent by a saga + /// scheduler which provides the sagas that its grace + /// period has completed. + /// + /// + public async Task Handle(ConfirmGracePeriodCommandMsg command) + { + var orderSaga = FindSagaById(command.OrderNumber); + CheckValidSagaId(orderSaga); + + // TODO: This handler should change to Integration command handler type once command bus is implemented + + // TODO: If order status is not cancelled, change state to awaitingValidation and + // send ConfirmOrderStockCommandMsg to Inventory api + } + + + /// + /// Handler which processes the command when + /// customer executes cancel order from app + /// + /// + /// + public async Task Handle(CancelOrderCommand command) + { + var orderSaga = FindSagaById(command.OrderNumber); + CheckValidSagaId(orderSaga); + + // Set order status tu cancelled + + return true; + } + + private void CheckValidSagaId(Order orderSaga) + { + if (orderSaga is null) + { + throw new OrderingDomainException("Not able to process order saga event. Reason: no valid orderId"); + } + } + + #region CommandHandlerIdentifiers + + public class CancelOrderCommandIdentifiedHandler : IdentifierCommandHandler + { + public CancelOrderCommandIdentifiedHandler(IMediator mediator, IRequestManager requestManager) : base(mediator, requestManager) + { + } + + protected override bool CreateResultForDuplicateRequest() + { + return true; // Ignore duplicate requests for processing order. + } + } + + #endregion + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs b/src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs new file mode 100644 index 000000000..14925f93f --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.API.Application.Sagas +{ + public abstract class Saga where TEntity : Entity + { + private readonly DbContext _dbContext; + + public Saga(DbContext dbContext) + { + _dbContext = dbContext; + } + + protected TEntity FindSagaById(int id, DbContext context = null) + { + var ctx = context ?? _dbContext; + return ctx.Set().Where(x => x.Id == id).SingleOrDefault(); + } + + protected async Task SaveChangesAsync(DbContext context = null) + { + var ctx = context ?? _dbContext; + var result = await ctx.SaveChangesAsync(); + return result > 0; + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Ordering.API.csproj b/src/Services/Ordering/Ordering.API/Ordering.API.csproj index 6eed3a13c..ba35d75e2 100644 --- a/src/Services/Ordering/Ordering.API/Ordering.API.csproj +++ b/src/Services/Ordering/Ordering.API/Ordering.API.csproj @@ -79,8 +79,9 @@ + + -