Browse Source

Merge

pull/809/head
Ramón Tomás 7 years ago
parent
commit
4e79875d26
39 changed files with 547 additions and 131 deletions
  1. +1
    -1
      docker-compose.vs.debug.yml
  2. +1
    -1
      docker-compose.yml
  3. +6
    -9
      src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/ConfirmOrderStockCommandMsgHandler.cs
  4. +42
    -0
      src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/DecrementOrderStockCommandMsgHandler.cs
  5. +3
    -3
      src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs
  6. +18
    -0
      src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs
  7. +1
    -1
      src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs
  8. +4
    -7
      src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs
  9. +16
    -1
      src/Services/Catalog/Catalog.API/Startup.cs
  10. +1
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
  11. +46
    -0
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs
  12. +51
    -0
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs
  13. +10
    -16
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs
  14. +3
    -3
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs
  15. +18
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs
  16. +14
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ShipOrderCommandMsg.cs
  17. +9
    -0
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs
  18. +22
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs
  19. +2
    -15
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs
  20. +6
    -11
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockNotConfirmedIntegrationEventHandler.cs
  21. +5
    -11
      src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs
  22. +36
    -0
      src/Services/Ordering/Ordering.API/Application/Sagas/OrderSaga.cs
  23. +2
    -11
      src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs
  24. +1
    -1
      src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs
  25. +3
    -1
      src/Services/Ordering/Ordering.API/Startup.cs
  26. +62
    -9
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
  27. +2
    -2
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs
  28. +23
    -0
      src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs
  29. +23
    -0
      src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs
  30. +16
    -0
      src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs
  31. +0
    -22
      src/Services/Ordering/Ordering.Domain/Events/OrderStockMethodVerifiedDomainEvent.cs
  32. +19
    -0
      src/Services/Payment/Payment.API/IntegrationCommands/CommandHandlers/PayOrderCommandMsgHandler.cs
  33. +11
    -0
      src/Services/Payment/Payment.API/IntegrationCommands/Commands/PayOrderCommandMsg.cs
  34. +11
    -0
      src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs
  35. +11
    -0
      src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSuccededIntegrationEvent.cs
  36. +5
    -0
      src/Services/Payment/Payment.API/Payment.API.csproj
  37. +30
    -0
      src/Services/Payment/Payment.API/Startup.cs
  38. +8
    -2
      src/Services/SagaManager/SagaManager/Program.cs
  39. +5
    -2
      test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs

+ 1
- 1
docker-compose.vs.debug.yml View File

@ -107,7 +107,7 @@ services:
- "com.microsoft.visualstudio.targetoperatingsystem=linux" - "com.microsoft.visualstudio.targetoperatingsystem=linux"
payment.api: payment.api:
image: payment.api:dev
image: eshop/payment.api:dev
build: build:
args: args:
source: ${DOCKER_BUILD_SOURCE} source: ${DOCKER_BUILD_SOURCE}


+ 1
- 1
docker-compose.yml View File

@ -86,7 +86,7 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
payment.api: payment.api:
image: payment.api
image: eshop/payment.api
build: build:
context: ./src/Services/Payment/Payment.API context: ./src/Services/Payment/Payment.API
dockerfile: Dockerfile dockerfile: Dockerfile

src/Services/Catalog/Catalog.API/IntegrationEvents/EventHandling/ConfirmOrderStockIntegrationEventHandler.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/ConfirmOrderStockCommandMsgHandler.cs View File

@ -1,4 +1,4 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.EventHandling
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.CommandHandlers
{ {
using BuildingBlocks.EventBus.Abstractions; using BuildingBlocks.EventBus.Abstractions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -9,21 +9,22 @@
using global::Catalog.API.Infrastructure.Exceptions; using global::Catalog.API.Infrastructure.Exceptions;
using global::Catalog.API.IntegrationEvents; using global::Catalog.API.IntegrationEvents;
using Model; using Model;
using Events;
using Commands;
using IntegrationEvents.Events;
public class ConfirmOrderStockIntegrationEventHandler : IIntegrationEventHandler<ConfirmOrderStockIntegrationEvent>
public class ConfirmOrderStockCommandMsgHandler : IIntegrationEventHandler<ConfirmOrderStockCommandMsg>
{ {
private readonly CatalogContext _catalogContext; private readonly CatalogContext _catalogContext;
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
public ConfirmOrderStockIntegrationEventHandler(CatalogContext catalogContext,
public ConfirmOrderStockCommandMsgHandler(CatalogContext catalogContext,
ICatalogIntegrationEventService catalogIntegrationEventService) ICatalogIntegrationEventService catalogIntegrationEventService)
{ {
_catalogContext = catalogContext; _catalogContext = catalogContext;
_catalogIntegrationEventService = catalogIntegrationEventService; _catalogIntegrationEventService = catalogIntegrationEventService;
} }
public async Task Handle(ConfirmOrderStockIntegrationEvent @event)
public async Task Handle(ConfirmOrderStockCommandMsg @event)
{ {
var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>(); var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>();
@ -38,15 +39,11 @@
confirmedOrderStockItems.Add(confirmedOrderStockItem); confirmedOrderStockItems.Add(confirmedOrderStockItem);
} }
//Create Integration Event to be published through the Event Bus
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.Confirmed) var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.Confirmed)
? (IntegrationEvent) new OrderStockNotConfirmedIntegrationEvent(@event.OrderId, confirmedOrderStockItems) ? (IntegrationEvent) new OrderStockNotConfirmedIntegrationEvent(@event.OrderId, confirmedOrderStockItems)
: new OrderStockConfirmedIntegrationEvent(@event.OrderId); : new OrderStockConfirmedIntegrationEvent(@event.OrderId);
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
// Publish through the Event Bus and mark the saved event as published
await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
} }

+ 42
- 0
src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/DecrementOrderStockCommandMsgHandler.cs View File

@ -0,0 +1,42 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.CommandHandlers
{
using BuildingBlocks.EventBus.Abstractions;
using System.Threading.Tasks;
using Infrastructure;
using global::Catalog.API.Infrastructure.Exceptions;
using global::Catalog.API.IntegrationEvents;
using Model;
using Commands;
public class DecrementOrderStockCommandMsgHandler : IIntegrationEventHandler<DecrementOrderStockCommandMsg>
{
private readonly CatalogContext _catalogContext;
public DecrementOrderStockCommandMsgHandler(CatalogContext catalogContext)
{
_catalogContext = catalogContext;
}
public async Task Handle(DecrementOrderStockCommandMsg @event)
{
//we're not blocking stock/inventory
foreach (var orderStockItem in @event.OrderStockItems)
{
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
CheckValidcatalogItemId(catalogItem);
catalogItem.RemoveStock(orderStockItem.Units);
}
await _catalogContext.SaveChangesAsync();
}
private void CheckValidcatalogItemId(CatalogItem catalogItem)
{
if (catalogItem is null)
{
throw new CatalogDomainException("Not able to process catalog event. Reason: no valid catalogItemId");
}
}
}
}

src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ConfirmOrderStockIntegrationEvent.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs View File

@ -1,14 +1,14 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.Commands
{ {
using BuildingBlocks.EventBus.Events; using BuildingBlocks.EventBus.Events;
using System.Collections.Generic; using System.Collections.Generic;
public class ConfirmOrderStockIntegrationEvent : IntegrationEvent
public class ConfirmOrderStockCommandMsg : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; } public IEnumerable<OrderStockItem> OrderStockItems { get; }
public ConfirmOrderStockIntegrationEvent(int orderId,
public ConfirmOrderStockCommandMsg(int orderId,
IEnumerable<OrderStockItem> orderStockItems) IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;

+ 18
- 0
src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs View File

@ -0,0 +1,18 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.Commands
{
using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class DecrementOrderStockCommandMsg : IntegrationEvent
{
public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; }
public DecrementOrderStockCommandMsg(int orderId,
IEnumerable<OrderStockItem> orderStockItems)
{
OrderId = orderId;
OrderStockItems = orderStockItems;
}
}
}

+ 1
- 1
src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockConfirmedIntegrationEvent.cs View File

@ -1,6 +1,6 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
{ {
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using BuildingBlocks.EventBus.Events;
public class OrderStockConfirmedIntegrationEvent : IntegrationEvent public class OrderStockConfirmedIntegrationEvent : IntegrationEvent
{ {


+ 4
- 7
src/Services/Catalog/Catalog.API/IntegrationEvents/Events/ProductPriceChangedIntegrationEvent.cs View File

@ -1,10 +1,7 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
{ {
using BuildingBlocks.EventBus.Events;
// Integration Events notes: // Integration Events notes:
// An Event is “something that has happened in the past”, therefore its name has to be // An Event is “something that has happened in the past”, therefore its name has to be
// An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems. // An Integration Event is an event that can cause side effects to other microsrvices, Bounded-Contexts or external systems.
@ -23,4 +20,4 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Eve
OldPrice = oldPrice; OldPrice = oldPrice;
} }
} }
}
}

+ 16
- 1
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -14,6 +14,9 @@
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services; using Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure; using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.Commands;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents;
using Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationCommands.CommandHandlers;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.HealthChecks; using Microsoft.Extensions.HealthChecks;
@ -24,7 +27,7 @@
using System.Data.Common; using System.Data.Common;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.Reflection; using System.Reflection;
public class Startup public class Startup
{ {
public IConfigurationRoot Configuration { get; } public IConfigurationRoot Configuration { get; }
@ -120,6 +123,8 @@
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddSingleton<IEventBus, EventBusRabbitMQ>(); services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddTransient<IIntegrationEventHandler<ConfirmOrderStockCommandMsg>, ConfirmOrderStockCommandMsgHandler>();
services.AddTransient<IIntegrationEventHandler<DecrementOrderStockCommandMsg>, DecrementOrderStockCommandMsgHandler>();
var container = new ContainerBuilder(); var container = new ContainerBuilder();
container.Populate(services); container.Populate(services);
@ -149,6 +154,8 @@
CatalogContextSeed.SeedAsync(app, loggerFactory) CatalogContextSeed.SeedAsync(app, loggerFactory)
.Wait(); .Wait();
ConfigureEventBus(app);
var integrationEventLogContext = new IntegrationEventLogContext( var integrationEventLogContext = new IntegrationEventLogContext(
new DbContextOptionsBuilder<IntegrationEventLogContext>() new DbContextOptionsBuilder<IntegrationEventLogContext>()
.UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API")) .UseSqlServer(Configuration["ConnectionString"], b => b.MigrationsAssembly("Catalog.API"))
@ -180,5 +187,13 @@
ctx.Database.CloseConnection(); ctx.Database.CloseConnection();
} }
} }
private void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ConfirmOrderStockCommandMsg, IIntegrationEventHandler<ConfirmOrderStockCommandMsg>>();
eventBus.Subscribe<DecrementOrderStockCommandMsg, IIntegrationEventHandler<DecrementOrderStockCommandMsg>>();
}
} }
} }

+ 1
- 1
src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs View File

@ -43,7 +43,7 @@
// make sure that consistency is preserved across the whole aggregate // make sure that consistency is preserved across the whole aggregate
var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode); var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode);
var order = new Order(message.UserId, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); var order = new Order(message.UserId, address, message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
order.SetOrderStatusId(OrderStatus.Submited.Id);
order.SetSubmitedStatus();
foreach (var item in message.OrderItems) foreach (var item in message.OrderItems)
{ {
order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units);


+ 46
- 0
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs View File

@ -0,0 +1,46 @@
namespace Ordering.API.Application.DomainEventHandlers.OrderGracePeriodConfirmed
{
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.Extensions.Logging;
using Domain.Events;
using System;
using System.Threading.Tasks;
using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.API.Application.IntegrationEvents;
using System.Linq;
public class OrderStatusChangedToAwaitingValidationDomainEventHandler
: IAsyncNotificationHandler<OrderStatusChangedToAwaitingValidationDomainEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly ILoggerFactory _logger;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
public OrderStatusChangedToAwaitingValidationDomainEventHandler(
IOrderRepository orderRepository, ILoggerFactory logger,
IOrderingIntegrationEventService orderingIntegrationEventService)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_orderingIntegrationEventService = orderingIntegrationEventService;
}
public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent)
{
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent))
.LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.AwaitingValidation.Id}");
var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
var confirmOrderStockEvent = new ConfirmOrderStockCommandMsg(orderStatusChangedToAwaitingValidationDomainEvent.OrderId,
orderStockList);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(confirmOrderStockEvent);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(confirmOrderStockEvent);
}
}
}

+ 51
- 0
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs View File

@ -0,0 +1,51 @@
namespace Ordering.API.Application.DomainEventHandlers.OrderPaid
{
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.Extensions.Logging;
using Domain.Events;
using System;
using System.Threading.Tasks;
using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.API.Application.IntegrationEvents;
using System.Linq;
public class OrderStatusChangedToPaidDomainEventHandler
: IAsyncNotificationHandler<OrderStatusChangedToPaidDomainEvent>
{
private readonly IOrderRepository _orderRepository;
private readonly ILoggerFactory _logger;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
public OrderStatusChangedToPaidDomainEventHandler(
IOrderRepository orderRepository, ILoggerFactory logger,
IOrderingIntegrationEventService orderingIntegrationEventService)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_orderingIntegrationEventService = orderingIntegrationEventService;
}
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent)
{
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.Paid.Id}");
var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
var decrementOrderStockCommandMsg = new DecrementOrderStockCommandMsg(orderStatusChangedToPaidDomainEvent.OrderId,
orderStockList);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(decrementOrderStockCommandMsg);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(decrementOrderStockCommandMsg);
//is it necessary get a DecrementOrderStockSuccessIntegrationEvent/DecrementOrderStockFailedIntegrationEvent before to call ShipOrderCommandMsg???
var shipOrderCommandMsg = new ShipOrderCommandMsg(orderStatusChangedToPaidDomainEvent.OrderId);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(shipOrderCommandMsg);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(shipOrderCommandMsg);
}
}
}

src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmation/UpdateOrderWhenOrderStockMethodVerifiedDomainEventHandler.cs → src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs View File

@ -1,4 +1,4 @@
namespace Ordering.API.Application.DomainEventHandlers.OrderStartedEvent
namespace Ordering.API.Application.DomainEventHandlers.OrderStockConfirmed
{ {
using MediatR; using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
@ -9,14 +9,14 @@
using Ordering.API.Application.IntegrationCommands.Commands; using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.API.Application.IntegrationEvents; using Ordering.API.Application.IntegrationEvents;
public class UpdateOrderWhenOrderStockMethodVerifiedDomainEventHandler
: IAsyncNotificationHandler<OrderStockMethodVerifiedDomainEvent>
public class OrderStatusChangedToStockConfirmedDomainEventHandler
: IAsyncNotificationHandler<OrderStatusChangedToStockConfirmedDomainEvent>
{ {
private readonly IOrderRepository _orderRepository; private readonly IOrderRepository _orderRepository;
private readonly ILoggerFactory _logger; private readonly ILoggerFactory _logger;
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService; private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
public UpdateOrderWhenOrderStockMethodVerifiedDomainEventHandler(
public OrderStatusChangedToStockConfirmedDomainEventHandler(
IOrderRepository orderRepository, ILoggerFactory logger, IOrderRepository orderRepository, ILoggerFactory logger,
IOrderingIntegrationEventService orderingIntegrationEventService) IOrderingIntegrationEventService orderingIntegrationEventService)
{ {
@ -25,21 +25,15 @@
_orderingIntegrationEventService = orderingIntegrationEventService; _orderingIntegrationEventService = orderingIntegrationEventService;
} }
public async Task Handle(OrderStockMethodVerifiedDomainEvent orderStockMethodVerifiedDomainEvent)
public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent)
{ {
var orderToUpdate = await _orderRepository.GetAsync(orderStockMethodVerifiedDomainEvent.OrderId);
orderToUpdate.SetOrderStatusId(orderStockMethodVerifiedDomainEvent.OrderStatus.Id);
_orderRepository.Update(orderToUpdate);
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
await _orderRepository.UnitOfWork
.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.StockConfirmed.Id}");
_logger.CreateLogger(nameof(UpdateOrderWhenOrderStockMethodVerifiedDomainEventHandler))
.LogTrace($"Order with Id: {orderStockMethodVerifiedDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: { orderStockMethodVerifiedDomainEvent.OrderStatus.Id }");
var payOrderCommandMsg = new PayOrderCommandMsg(orderToUpdate.Id);
var payOrderCommandMsg = new PayOrderCommandMsg(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg); await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg); await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg);
} }

+ 3
- 3
src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs View File

@ -6,13 +6,13 @@
public class ConfirmOrderStockCommandMsg : IntegrationEvent public class ConfirmOrderStockCommandMsg : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItem { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; }
public ConfirmOrderStockCommandMsg(int orderId, public ConfirmOrderStockCommandMsg(int orderId,
IEnumerable<OrderStockItem> orderStockItem)
IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;
OrderStockItem = orderStockItem;
OrderStockItems = orderStockItems;
} }
} }


+ 18
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs View File

@ -0,0 +1,18 @@
namespace Ordering.API.Application.IntegrationCommands.Commands
{
using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class DecrementOrderStockCommandMsg : IntegrationEvent
{
public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; }
public DecrementOrderStockCommandMsg(int orderId,
IEnumerable<OrderStockItem> orderStockItems)
{
OrderId = orderId;
OrderStockItems = orderStockItems;
}
}
}

+ 14
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ShipOrderCommandMsg.cs View File

@ -0,0 +1,14 @@
namespace Ordering.API.Application.IntegrationCommands.Commands
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class ShipOrderCommandMsg : IntegrationEvent
{
public int OrderId { get; }
public ShipOrderCommandMsg(int orderId)
{
OrderId = orderId;
}
}
}

+ 9
- 0
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs View File

@ -1,14 +1,23 @@
namespace Ordering.API.Application.IntegrationEvents.EventHandling namespace Ordering.API.Application.IntegrationEvents.EventHandling
{ {
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Ordering.API.Application.IntegrationEvents.Events; using Ordering.API.Application.IntegrationEvents.Events;
using System.Threading.Tasks; using System.Threading.Tasks;
public class OrderPaymentFailedIntegrationEventHandler : public class OrderPaymentFailedIntegrationEventHandler :
IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent> IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>
{ {
private readonly IOrderRepository _orderRepository;
public OrderPaymentFailedIntegrationEventHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task Handle(OrderPaymentFailedIntegrationEvent @event) public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
{ {
//TODO: Cancel Order
} }
} }
} }

+ 22
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs View File

@ -1,14 +1,35 @@
namespace Ordering.API.Application.IntegrationEvents.EventHandling namespace Ordering.API.Application.IntegrationEvents.EventHandling
{ {
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Ordering.API.Application.IntegrationEvents.Events; using Ordering.API.Application.IntegrationEvents.Events;
using Ordering.Domain.Exceptions;
using System.Threading.Tasks; using System.Threading.Tasks;
public class OrderPaymentSuccededIntegrationEventHandler : public class OrderPaymentSuccededIntegrationEventHandler :
IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent> IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>
{ {
private readonly IOrderRepository _orderRepository;
public OrderPaymentSuccededIntegrationEventHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task Handle(OrderPaymentSuccededIntegrationEvent @event) public async Task Handle(OrderPaymentSuccededIntegrationEvent @event)
{ {
var order = await _orderRepository.GetAsync(@event.OrderId);
CheckValidSagaId(order);
order.SetPaidStatus();
}
private void CheckValidSagaId(Order orderSaga)
{
if (orderSaga is null)
{
throw new OrderingDomainException("Not able to process order saga event. Reason: no valid orderId");
}
} }
} }
}
}

+ 2
- 15
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs View File

@ -10,32 +10,19 @@
public class OrderStockConfirmedIntegrationEventHandler : public class OrderStockConfirmedIntegrationEventHandler :
IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent> IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>
{ {
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
private readonly IOrderRepository _orderRepository; private readonly IOrderRepository _orderRepository;
public OrderStockConfirmedIntegrationEventHandler(IOrderRepository orderRepository,
IOrderingIntegrationEventService orderingIntegrationEventService)
public OrderStockConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
{ {
_orderRepository = orderRepository; _orderRepository = orderRepository;
_orderingIntegrationEventService = orderingIntegrationEventService;
} }
public async Task Handle(OrderStockConfirmedIntegrationEvent @event) public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
{ {
//TODO: 1) Updates the state to "StockValidated" and any meaningful OrderContextDescription message saying that all the items were confirmed with available stock, etc
var order = await _orderRepository.GetAsync(@event.OrderId); var order = await _orderRepository.GetAsync(@event.OrderId);
CheckValidSagaId(order); CheckValidSagaId(order);
order.SetOrderStockConfirmed();
//Create Integration Event to be published through the Event Bus
var payOrderCommandMsg = new PayOrderCommandMsg(order.Id);
// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg);
// Publish through the Event Bus and mark the saved event as published
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg);
order.SetStockConfirmedStatus();
} }
private void CheckValidSagaId(Order orderSaga) private void CheckValidSagaId(Order orderSaga)


+ 6
- 11
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockNotConfirmedIntegrationEventHandler.cs View File

@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Ordering.API.Application.IntegrationCommands.Commands;
namespace Ordering.API.Application.IntegrationEvents.EventHandling namespace Ordering.API.Application.IntegrationEvents.EventHandling
{ {
@ -7,33 +8,27 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
using System.Threading.Tasks; using System.Threading.Tasks;
using Events; using Events;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Ordering.API.Application.Sagas;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
using Ordering.Domain.Exceptions;
using Domain.Exceptions;
public class OrderStockNotConfirmedIntegrationEventHandler : IIntegrationEventHandler<OrderStockNotConfirmedIntegrationEvent> public class OrderStockNotConfirmedIntegrationEventHandler : IIntegrationEventHandler<OrderStockNotConfirmedIntegrationEvent>
{ {
private readonly IOrderingIntegrationEventService _orderingIntegrationEventService;
private readonly IOrderRepository _orderRepository; private readonly IOrderRepository _orderRepository;
public OrderStockNotConfirmedIntegrationEventHandler(IOrderRepository orderRepository,
IOrderingIntegrationEventService orderingIntegrationEventService)
public OrderStockNotConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
{ {
_orderRepository = orderRepository; _orderRepository = orderRepository;
_orderingIntegrationEventService = orderingIntegrationEventService;
} }
public async Task Handle(OrderStockNotConfirmedIntegrationEvent @event) public async Task Handle(OrderStockNotConfirmedIntegrationEvent @event)
{ {
//TODO: must update the order state to cancelled and the CurrentOrderStateContextDescription with the reasons of no-stock confirm
var order = await _orderRepository.GetAsync(@event.OrderId);
CheckValidSagaId(order);
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
CheckValidSagaId(orderToUpdate);
var orderStockNotConfirmedItems = @event.OrderStockItems var orderStockNotConfirmedItems = @event.OrderStockItems
.FindAll(c => !c.Confirmed) .FindAll(c => !c.Confirmed)
.Select(c => c.ProductId); .Select(c => c.ProductId);
order.SetOrderStockConfirmed(orderStockNotConfirmedItems);
orderToUpdate.SetStockConfirmedStatus(orderStockNotConfirmedItems);
} }
private void CheckValidSagaId(Order orderSaga) private void CheckValidSagaId(Order orderSaga)


+ 5
- 11
src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs View File

@ -8,8 +8,10 @@ using Ordering.API.Application.Commands;
using Ordering.API.Application.IntegrationCommands.Commands; using Ordering.API.Application.IntegrationCommands.Commands;
using Ordering.API.Application.IntegrationEvents; using Ordering.API.Application.IntegrationEvents;
using Ordering.Domain.Exceptions; using Ordering.Domain.Exceptions;
using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ordering.API.Application.IntegrationEvents;
namespace Ordering.API.Application.Sagas namespace Ordering.API.Application.Sagas
{ {
@ -21,7 +23,7 @@ namespace Ordering.API.Application.Sagas
/// the opportunity to cancel the order before proceeding /// the opportunity to cancel the order before proceeding
/// with the validations. /// with the validations.
/// </summary> /// </summary>
public class OrderProcessSaga : Saga<Order>,
public class OrderProcessSaga : OrderSaga,
IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>, IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>,
IAsyncRequestHandler<CancelOrderCommand, bool>, IAsyncRequestHandler<CancelOrderCommand, bool>,
IAsyncRequestHandler<ShipOrderCommand, bool> IAsyncRequestHandler<ShipOrderCommand, bool>
@ -54,17 +56,9 @@ namespace Ordering.API.Application.Sagas
if (orderSaga.OrderStatus != OrderStatus.Cancelled) if (orderSaga.OrderStatus != OrderStatus.Cancelled)
{ {
orderSaga.SetOrderStatusId(OrderStatus.AwaitingValidation.Id);
await SaveChangesAsync();
var orderStockList = orderSaga.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
orderSaga.SetAwaitingValidationStatus();
var confirmOrderStockEvent = new ConfirmOrderStockCommandMsg(orderSaga.Id, orderStockList);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(confirmOrderStockEvent);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(confirmOrderStockEvent);
await SaveChangesAsync();
} }
} }


+ 36
- 0
src/Services/Ordering/Ordering.API/Application/Sagas/OrderSaga.cs View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
namespace Ordering.API.Application.Sagas
{
public abstract class OrderSaga : Saga<Order>
{
private OrderingContext _orderingContext;
public OrderSaga(OrderingContext orderingContext) : base(orderingContext)
{
_orderingContext = orderingContext;
}
public override Order FindSagaById(int id)
{
var order = _orderingContext.Orders
.Single(c => c.Id == id);
_orderingContext.Entry(order)
.Member("OrderStatus");
return order;
}
public override async Task<bool> SaveChangesAsync()
{
return await _orderingContext.SaveEntitiesAsync();
}
}
}

+ 2
- 11
src/Services/Ordering/Ordering.API/Application/Sagas/Saga.cs View File

@ -14,17 +14,8 @@ namespace Ordering.API.Application.Sagas
_dbContext = dbContext; _dbContext = dbContext;
} }
protected TEntity FindSagaById(int id, DbContext context = null)
{
var ctx = context ?? _dbContext;
return ctx.Set<TEntity>().Where(x => x.Id == id).SingleOrDefault();
}
public abstract TEntity FindSagaById(int id);
protected async Task<bool> SaveChangesAsync(DbContext context = null)
{
var ctx = context ?? _dbContext;
var result = await ctx.SaveChangesAsync();
return result > 0;
}
public abstract Task<bool> SaveChangesAsync();
} }
} }

+ 1
- 1
src/Services/Ordering/Ordering.API/Infrastructure/OrderingContextSeed.cs View File

@ -33,7 +33,7 @@
{ {
context.OrderStatus.Add(OrderStatus.Submited); context.OrderStatus.Add(OrderStatus.Submited);
context.OrderStatus.Add(OrderStatus.AwaitingValidation); context.OrderStatus.Add(OrderStatus.AwaitingValidation);
context.OrderStatus.Add(OrderStatus.StockValidated);
context.OrderStatus.Add(OrderStatus.StockConfirmed);
context.OrderStatus.Add(OrderStatus.Paid); context.OrderStatus.Add(OrderStatus.Paid);
context.OrderStatus.Add(OrderStatus.Shipped); context.OrderStatus.Add(OrderStatus.Shipped);
context.OrderStatus.Add(OrderStatus.Cancelled); context.OrderStatus.Add(OrderStatus.Cancelled);


+ 3
- 1
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -8,6 +8,8 @@
using global::Ordering.API.Application.IntegrationEvents.Events; using global::Ordering.API.Application.IntegrationEvents.Events;
using global::Ordering.API.Application.Sagas; using global::Ordering.API.Application.Sagas;
using global::Ordering.API.Infrastructure.Middlewares; using global::Ordering.API.Infrastructure.Middlewares;
using global::Ordering.API.Application.IntegrationCommands.Commands;
using global::Ordering.API.Application.Sagas;
using Infrastructure; using Infrastructure;
using Infrastructure.Auth; using Infrastructure.Auth;
using Infrastructure.AutofacModules; using Infrastructure.AutofacModules;
@ -108,7 +110,7 @@
services.AddTransient<IIdentityService, IdentityService>(); services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>( services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c)); sp => (DbConnection c) => new IntegrationEventLogService(c));
var serviceProvider = services.BuildServiceProvider();
services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>(); services.AddTransient<IOrderingIntegrationEventService, OrderingIntegrationEventService>();
services.AddSingleton<IRabbitMQPersistentConnection>(sp => services.AddSingleton<IRabbitMQPersistentConnection>(sp =>


+ 62
- 9
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs View File

@ -1,5 +1,6 @@
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
using Ordering.Domain.Events; using Ordering.Domain.Events;
using Ordering.Domain.Exceptions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -93,33 +94,80 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
_buyerId = id; _buyerId = id;
} }
public void SetOrderStatusId(int id)
#region Status Changes
public void SetSubmitedStatus()
{ {
_orderStatusId = id;
_orderStatusId = OrderStatus.Submited.Id;
}
public void SetAwaitingValidationStatus()
{
if (_orderStatusId != OrderStatus.Submited.Id)
{
StatusChangeException();
}
_orderStatusId = OrderStatus.AwaitingValidation.Id;
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, OrderItems));
} }
public void SetOrderStockConfirmed(IEnumerable<int> orderStockNotConfirmedItems = null)
public void SetStockConfirmedStatus(IEnumerable<int> orderStockNotConfirmedItems = null)
{ {
if(orderStockNotConfirmedItems is null)
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
{
StatusChangeException();
}
if (orderStockNotConfirmedItems is null)
{ {
OrderStatus = OrderStatus.StockValidated;
OrderStatus = OrderStatus.StockConfirmed;
_description = "All the items were confirmed with available stock."; _description = "All the items were confirmed with available stock.";
//AddDomainEvent(new OrderStockMethodVerifiedDomainEvent(Id, OrderStatus.StockValidated));
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
} }
else else
{ {
OrderStatus = OrderStatus.Cancelled;
var itemsStockNotConfirmedProductNames = OrderItems var itemsStockNotConfirmedProductNames = OrderItems
.Where(c => orderStockNotConfirmedItems.Contains(c.ProductId)) .Where(c => orderStockNotConfirmedItems.Contains(c.ProductId))
.Select(c => c.GetOrderItemProductName()); .Select(c => c.GetOrderItemProductName());
var itemsStockNotConfirmedDescription = string.Join(", ", itemsStockNotConfirmedProductNames); var itemsStockNotConfirmedDescription = string.Join(", ", itemsStockNotConfirmedProductNames);
_description = $"The product items don't have stock: ({itemsStockNotConfirmedDescription}).";
}
}
OrderStatus = OrderStatus.Cancelled;
_description = $"The product items don't have stock: ({itemsStockNotConfirmedDescription}).";
//AddDomainEvent(new OrderStockMethodVerifiedDomainEvent(Id, OrderStatus.Cancelled));
public void SetPaidStatus()
{
if (_orderStatusId != OrderStatus.StockConfirmed.Id)
{
StatusChangeException();
}
_orderStatusId = OrderStatus.Paid.Id;
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
}
public void SetShippedStatus()
{
if (_orderStatusId != OrderStatus.Paid.Id)
{
StatusChangeException();
} }
_orderStatusId = OrderStatus.Shipped.Id;
_description = "";
//Call Domain Event
} }
#endregion
public int GetOrderStatusId() public int GetOrderStatusId()
{ {
return _orderStatusId; return _orderStatusId;
@ -134,6 +182,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
this.AddDomainEvent(orderStartedDomainEvent); this.AddDomainEvent(orderStartedDomainEvent);
} }
private void StatusChangeException()
{
throw new OrderingDomainException("Not able to process order event. Reason: no valid order status change");
}
} }
} }

+ 2
- 2
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/OrderStatus.cs View File

@ -12,7 +12,7 @@
{ {
public static OrderStatus Submited = new OrderStatus(1, nameof(Submited).ToLowerInvariant()); public static OrderStatus Submited = new OrderStatus(1, nameof(Submited).ToLowerInvariant());
public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant()); public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
public static OrderStatus StockValidated = new OrderStatus(3, nameof(StockValidated).ToLowerInvariant());
public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant()); public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant()); public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant()); public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
@ -27,7 +27,7 @@
} }
public static IEnumerable<OrderStatus> List() => public static IEnumerable<OrderStatus> List() =>
new[] { Submited, AwaitingValidation, StockValidated, Paid, Shipped, Cancelled };
new[] { Submited, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled };
public static OrderStatus FromName(string name) public static OrderStatus FromName(string name)
{ {


+ 23
- 0
src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToAwaitingValidationDomainEvent.cs View File

@ -0,0 +1,23 @@
namespace Ordering.Domain.Events
{
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using System.Collections.Generic;
/// <summary>
/// Event used when the grace period order is confirmed
/// </summary>
public class OrderStatusChangedToAwaitingValidationDomainEvent
: IAsyncNotification
{
public int OrderId { get; }
public IEnumerable<OrderItem> OrderItems { get; }
public OrderStatusChangedToAwaitingValidationDomainEvent(int orderId,
IEnumerable<OrderItem> orderItems)
{
OrderId = orderId;
OrderItems = orderItems;
}
}
}

+ 23
- 0
src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToPaidDomainEvent.cs View File

@ -0,0 +1,23 @@
namespace Ordering.Domain.Events
{
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
using System.Collections.Generic;
/// <summary>
/// Event used when the order is paid
/// </summary>
public class OrderStatusChangedToPaidDomainEvent
: IAsyncNotification
{
public int OrderId { get; }
public IEnumerable<OrderItem> OrderItems { get; }
public OrderStatusChangedToPaidDomainEvent(int orderId,
IEnumerable<OrderItem> orderItems)
{
OrderId = orderId;
OrderItems = orderItems;
}
}
}

+ 16
- 0
src/Services/Ordering/Ordering.Domain/Events/OrderStatusChangedToStockConfirmedDomainEvent.cs View File

@ -0,0 +1,16 @@
namespace Ordering.Domain.Events
{
using MediatR;
/// <summary>
/// Event used when the order stock items are confirmed
/// </summary>
public class OrderStatusChangedToStockConfirmedDomainEvent
: IAsyncNotification
{
public int OrderId { get; }
public OrderStatusChangedToStockConfirmedDomainEvent(int orderId)
=> OrderId = orderId;
}
}

+ 0
- 22
src/Services/Ordering/Ordering.Domain/Events/OrderStockMethodVerifiedDomainEvent.cs View File

@ -1,22 +0,0 @@
namespace Ordering.Domain.Events
{
using MediatR;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
/// <summary>
/// Event used when the order stock items are verified
/// </summary>
public class OrderStockMethodVerifiedDomainEvent
: IAsyncNotification
{
public int OrderId { get; }
public OrderStatus OrderStatus { get; }
public OrderStockMethodVerifiedDomainEvent(int orderId,
OrderStatus orderStatus)
{
OrderId = orderId;
OrderStatus = orderStatus;
}
}
}

+ 19
- 0
src/Services/Payment/Payment.API/IntegrationCommands/CommandHandlers/PayOrderCommandMsgHandler.cs View File

@ -0,0 +1,19 @@
namespace Payment.API.IntegrationCommands.CommandHandlers
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Payment.API.IntegrationCommands.Commands;
using System.Threading.Tasks;
using System;
public class PayOrderCommandMsgHandler : IIntegrationEventHandler<PayOrderCommandMsg>
{
public PayOrderCommandMsgHandler()
{
}
public async Task Handle(PayOrderCommandMsg @event)
{
throw new NotImplementedException();
}
}
}

+ 11
- 0
src/Services/Payment/Payment.API/IntegrationCommands/Commands/PayOrderCommandMsg.cs View File

@ -0,0 +1,11 @@
namespace Payment.API.IntegrationCommands.Commands
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class PayOrderCommandMsg : IntegrationEvent
{
public int OrderId { get; }
public PayOrderCommandMsg(int orderId) => OrderId = orderId;
}
}

+ 11
- 0
src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentFailedIntegrationEvent.cs View File

@ -0,0 +1,11 @@
namespace Payment.API.IntegrationEvents.Events
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class OrderPaymentFailedIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }
public OrderPaymentFailedIntegrationEvent(int orderId) => OrderId = orderId;
}
}

+ 11
- 0
src/Services/Payment/Payment.API/IntegrationEvents/Events/OrderPaymentSuccededIntegrationEvent.cs View File

@ -0,0 +1,11 @@
namespace Payment.API.IntegrationEvents.Events
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class OrderPaymentSuccededIntegrationEvent : IntegrationEvent
{
public int OrderId { get; }
public OrderPaymentSuccededIntegrationEvent(int orderId) => OrderId = orderId;
}
}

+ 5
- 0
src/Services/Payment/Payment.API/Payment.API.csproj View File

@ -25,5 +25,10 @@
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
</ItemGroup>
</Project> </Project>

+ 30
- 0
src/Services/Payment/Payment.API/Startup.cs View File

@ -6,6 +6,11 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Payment.API.IntegrationCommands.Commands;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using RabbitMQ.Client;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
namespace Payment.API namespace Payment.API
{ {
@ -29,6 +34,21 @@ namespace Payment.API
// Add framework services. // Add framework services.
services.AddMvc(); services.AddMvc();
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = Configuration["EventBusConnection"]
};
return new DefaultRabbitMQPersistentConnection(factory, logger);
});
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddSwaggerGen(); services.AddSwaggerGen();
services.ConfigureSwaggerGen(options => services.ConfigureSwaggerGen(options =>
{ {
@ -47,6 +67,8 @@ namespace Payment.API
return new AutofacServiceProvider(container.Build()); return new AutofacServiceProvider(container.Build());
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
@ -57,6 +79,14 @@ namespace Payment.API
app.UseSwagger() app.UseSwagger()
.UseSwaggerUi(); .UseSwaggerUi();
ConfigureEventBus(app);
}
private void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<PayOrderCommandMsg, IIntegrationEventHandler<PayOrderCommandMsg>>();
} }
} }
} }

+ 8
- 2
src/Services/SagaManager/SagaManager/Program.cs View File

@ -18,6 +18,7 @@ namespace SagaManager
using Services; using Services;
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using Autofac; using Autofac;
using System.Threading.Tasks;
public class Program public class Program
{ {
@ -25,7 +26,12 @@ namespace SagaManager
public static void Main(string[] args) public static void Main(string[] args)
{ {
StartUp();
MainAsync().Wait();
}
static async Task MainAsync()
{
StartUp();
IServiceCollection services = new ServiceCollection(); IServiceCollection services = new ServiceCollection();
var serviceProvider = ConfigureServices(services); var serviceProvider = ConfigureServices(services);
@ -39,7 +45,7 @@ namespace SagaManager
while (true) while (true)
{ {
sagaManagerService.CheckFinishedGracePeriodOrders(); sagaManagerService.CheckFinishedGracePeriodOrders();
System.Threading.Thread.Sleep(30000);
await Task.Delay(30000);
} }
} }


+ 5
- 2
test/Services/UnitTest/Ordering/Domain/OrderAggregateTest.cs View File

@ -96,7 +96,8 @@ public class OrderAggregateTest
[Fact] [Fact]
public void Add_new_Order_raises_new_event() public void Add_new_Order_raises_new_event()
{ {
//Arrange
//Arrange
var userId = new Guid();
var street = "fakeStreet"; var street = "fakeStreet";
var city = "FakeCity"; var city = "FakeCity";
var state = "fakeState"; var state = "fakeState";
@ -119,7 +120,8 @@ public class OrderAggregateTest
[Fact] [Fact]
public void Add_event_Order_explicitly_raises_new_event() public void Add_event_Order_explicitly_raises_new_event()
{ {
//Arrange
//Arrange
var userId = new Guid();
var street = "fakeStreet"; var street = "fakeStreet";
var city = "FakeCity"; var city = "FakeCity";
var state = "fakeState"; var state = "fakeState";
@ -143,6 +145,7 @@ public class OrderAggregateTest
public void Remove_event_Order_explicitly() public void Remove_event_Order_explicitly()
{ {
//Arrange //Arrange
var userId = new Guid();
var street = "fakeStreet"; var street = "fakeStreet";
var city = "FakeCity"; var city = "FakeCity";
var state = "fakeState"; var state = "fakeState";


Loading…
Cancel
Save