Browse Source

Merge

pull/223/head
Ramón Tomás 7 years ago
parent
commit
c7e88d49f1
39 changed files with 202 additions and 223 deletions
  1. +2
    -0
      docker-compose.yml
  2. +0
    -6
      src/Services/Basket/Basket.API/Startup.cs
  3. +12
    -12
      src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs
  4. +7
    -7
      src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/ConfirmOrderStockCommandHandler.cs
  5. +4
    -7
      src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/DecrementOrderStockCommandHandler.cs
  6. +2
    -2
      src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/ConfirmOrderStockCommand.cs
  7. +2
    -2
      src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/DecrementOrderStockCommand.cs
  8. +7
    -7
      src/Services/Catalog/Catalog.API/IntegrationEvents/Events/OrderStockNotConfirmedIntegrationEvent.cs
  9. +15
    -6
      src/Services/Catalog/Catalog.API/Startup.cs
  10. +1
    -1
      src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs
  11. +3
    -5
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs
  12. +3
    -10
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs
  13. +3
    -5
      src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs
  14. +2
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommand.cs
  15. +2
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmOrderStockCommand.cs
  16. +2
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/DecrementOrderStockCommand.cs
  17. +2
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/PayOrderCommand.cs
  18. +0
    -14
      src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ShipOrderCommandMsg.cs
  19. +3
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs
  20. +3
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentSuccededIntegrationEventHandler.cs
  21. +3
    -1
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs
  22. +4
    -2
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockNotConfirmedIntegrationEventHandler.cs
  23. +3
    -3
      src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockNotConfirmedIntegrationEvent.cs
  24. +11
    -24
      src/Services/Ordering/Ordering.API/Application/Sagas/OrderProcessSaga.cs
  25. +4
    -7
      src/Services/Ordering/Ordering.API/Application/Sagas/OrderSaga.cs
  26. +25
    -0
      src/Services/Ordering/Ordering.API/Startup.cs
  27. +2
    -0
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs
  28. +11
    -22
      src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs
  29. +8
    -0
      src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs
  30. +4
    -5
      src/Services/Payment/Payment.API/IntegrationCommands/CommandHandlers/PayOrderCommandHandler.cs
  31. +2
    -2
      src/Services/Payment/Payment.API/IntegrationCommands/Commands/PayOrderCommand.cs
  32. +3
    -0
      src/Services/Payment/Payment.API/Payment.API.csproj
  33. +13
    -3
      src/Services/Payment/Payment.API/Startup.cs
  34. +11
    -0
      src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodCommand.cs
  35. +0
    -14
      src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodCommandMsg.cs
  36. +3
    -7
      src/Services/SagaManager/SagaManager/IntegrationEvents/SagaManagerIntegrationEventService.cs
  37. +12
    -21
      src/Services/SagaManager/SagaManager/Program.cs
  38. +1
    -1
      src/Services/SagaManager/SagaManager/Services/ISagaManagerService.cs
  39. +7
    -17
      src/Services/SagaManager/SagaManager/Services/SagaManagerService.cs

+ 2
- 0
docker-compose.yml View File

@ -90,3 +90,5 @@ services:
build: build:
context: ./src/Services/Payment/Payment.API context: ./src/Services/Payment/Payment.API
dockerfile: Dockerfile dockerfile: Dockerfile
depends_on:
- rabbitmq

+ 0
- 6
src/Services/Basket/Basket.API/Startup.cs View File

@ -165,12 +165,6 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
protected virtual void ConfigureEventBus(IApplicationBuilder app) protected virtual void ConfigureEventBus(IApplicationBuilder app)
{ {
//var catalogPriceHandler = app.ApplicationServices
// .GetService<IIntegrationEventHandler<ProductPriceChangedIntegrationEvent>>();
//var orderStartedHandler = app.ApplicationServices
// .GetService<IIntegrationEventHandler<OrderStartedIntegrationEvent>>();
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>(); eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>(); eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();


+ 12
- 12
src/Services/Catalog/Catalog.API/Infrastructure/CatalogContextSeed.cs View File

@ -70,18 +70,18 @@
{ {
return new List<CatalogItem>() return new List<CatalogItem>()
{ {
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10" },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12" }
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1", AvailableStock = 1},
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11", AvailableStock = 1 },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12", AvailableStock = 1 }
}; };
} }
} }


src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/ConfirmOrderStockCommandMsgHandler.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/ConfirmOrderStockCommandHandler.cs View File

@ -12,23 +12,23 @@
using Commands; using Commands;
using IntegrationEvents.Events; using IntegrationEvents.Events;
public class ConfirmOrderStockCommandMsgHandler : IIntegrationEventHandler<ConfirmOrderStockCommandMsg>
public class ConfirmOrderStockCommandHandler : IIntegrationEventHandler<ConfirmOrderStockCommand>
{ {
private readonly CatalogContext _catalogContext; private readonly CatalogContext _catalogContext;
private readonly ICatalogIntegrationEventService _catalogIntegrationEventService; private readonly ICatalogIntegrationEventService _catalogIntegrationEventService;
public ConfirmOrderStockCommandMsgHandler(CatalogContext catalogContext,
public ConfirmOrderStockCommandHandler(CatalogContext catalogContext,
ICatalogIntegrationEventService catalogIntegrationEventService) ICatalogIntegrationEventService catalogIntegrationEventService)
{ {
_catalogContext = catalogContext; _catalogContext = catalogContext;
_catalogIntegrationEventService = catalogIntegrationEventService; _catalogIntegrationEventService = catalogIntegrationEventService;
} }
public async Task Handle(ConfirmOrderStockCommandMsg @event)
public async Task Handle(ConfirmOrderStockCommand command)
{ {
var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>(); var confirmedOrderStockItems = new List<ConfirmedOrderStockItem>();
foreach (var orderStockItem in @event.OrderStockItems)
foreach (var orderStockItem in command.OrderStockItems)
{ {
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
CheckValidcatalogItemId(catalogItem); CheckValidcatalogItemId(catalogItem);
@ -39,9 +39,9 @@
confirmedOrderStockItems.Add(confirmedOrderStockItem); confirmedOrderStockItems.Add(confirmedOrderStockItem);
} }
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.Confirmed)
? (IntegrationEvent) new OrderStockNotConfirmedIntegrationEvent(@event.OrderId, confirmedOrderStockItems)
: new OrderStockConfirmedIntegrationEvent(@event.OrderId);
var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
? (IntegrationEvent) new OrderStockNotConfirmedIntegrationEvent(command.OrderId, confirmedOrderStockItems)
: new OrderStockConfirmedIntegrationEvent(command.OrderId);
await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent); await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent); await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);

src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/DecrementOrderStockCommandMsgHandler.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/CommandHandlers/DecrementOrderStockCommandHandler.cs View File

@ -3,24 +3,21 @@
using BuildingBlocks.EventBus.Abstractions; using BuildingBlocks.EventBus.Abstractions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Infrastructure; using Infrastructure;
using global::Catalog.API.Infrastructure.Exceptions;
using global::Catalog.API.IntegrationEvents;
using Model;
using Commands; using Commands;
public class DecrementOrderStockCommandMsgHandler : IIntegrationEventHandler<DecrementOrderStockCommandMsg>
public class DecrementOrderStockCommandHandler : IIntegrationEventHandler<DecrementOrderStockCommand>
{ {
private readonly CatalogContext _catalogContext; private readonly CatalogContext _catalogContext;
public DecrementOrderStockCommandMsgHandler(CatalogContext catalogContext)
public DecrementOrderStockCommandHandler(CatalogContext catalogContext)
{ {
_catalogContext = catalogContext; _catalogContext = catalogContext;
} }
public async Task Handle(DecrementOrderStockCommandMsg @event)
public async Task Handle(DecrementOrderStockCommand command)
{ {
//we're not blocking stock/inventory //we're not blocking stock/inventory
foreach (var orderStockItem in @event.OrderStockItems)
foreach (var orderStockItem in command.OrderStockItems)
{ {
var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId); var catalogItem = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);

src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/ConfirmOrderStockCommand.cs View File

@ -3,12 +3,12 @@
using BuildingBlocks.EventBus.Events; using BuildingBlocks.EventBus.Events;
using System.Collections.Generic; using System.Collections.Generic;
public class ConfirmOrderStockCommandMsg : IntegrationEvent
public class ConfirmOrderStockCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; } public IEnumerable<OrderStockItem> OrderStockItems { get; }
public ConfirmOrderStockCommandMsg(int orderId,
public ConfirmOrderStockCommand(int orderId,
IEnumerable<OrderStockItem> orderStockItems) IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;

src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs → src/Services/Catalog/Catalog.API/IntegrationCommands/Commands/DecrementOrderStockCommand.cs View File

@ -3,12 +3,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class DecrementOrderStockCommandMsg : IntegrationEvent
public class DecrementOrderStockCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; } public IEnumerable<OrderStockItem> OrderStockItems { get; }
public DecrementOrderStockCommandMsg(int orderId,
public DecrementOrderStockCommand(int orderId,
IEnumerable<OrderStockItem> orderStockItems) IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;

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

@ -7,25 +7,25 @@
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<ConfirmedOrderStockItem> OrderStockItem { get; }
public List<ConfirmedOrderStockItem> OrderStockItems { get; }
public OrderStockNotConfirmedIntegrationEvent(int orderId,
IEnumerable<ConfirmedOrderStockItem> orderStockItem)
public OrderStockNotConfirmedIntegrationEvent(int orderId,
List<ConfirmedOrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;
OrderStockItem = orderStockItem;
OrderStockItems = orderStockItems;
} }
} }
public class ConfirmedOrderStockItem public class ConfirmedOrderStockItem
{ {
public int ProductId { get; } public int ProductId { get; }
public bool Confirmed { get; }
public bool HasStock { get; }
public ConfirmedOrderStockItem(int productId, bool confirmed)
public ConfirmedOrderStockItem(int productId, bool hasStock)
{ {
ProductId = productId; ProductId = productId;
Confirmed = confirmed;
HasStock = hasStock;
} }
} }
} }

+ 15
- 6
src/Services/Catalog/Catalog.API/Startup.cs View File

@ -121,10 +121,7 @@
return new DefaultRabbitMQPersistentConnection(factory, logger); return new DefaultRabbitMQPersistentConnection(factory, logger);
}); });
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddTransient<IIntegrationEventHandler<ConfirmOrderStockCommandMsg>, ConfirmOrderStockCommandMsgHandler>();
services.AddTransient<IIntegrationEventHandler<DecrementOrderStockCommandMsg>, DecrementOrderStockCommandMsgHandler>();
RegisterServiceBus(services);
var container = new ContainerBuilder(); var container = new ContainerBuilder();
container.Populate(services); container.Populate(services);
@ -188,12 +185,24 @@
} }
} }
private void RegisterServiceBus(IServiceCollection services)
{
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddTransient<IIntegrationEventHandler<ConfirmOrderStockCommand>,
ConfirmOrderStockCommandHandler>();
services.AddTransient<IIntegrationEventHandler<DecrementOrderStockCommand>,
DecrementOrderStockCommandHandler>();
}
private void ConfigureEventBus(IApplicationBuilder app) private void ConfigureEventBus(IApplicationBuilder app)
{ {
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ConfirmOrderStockCommandMsg, IIntegrationEventHandler<ConfirmOrderStockCommandMsg>>();
eventBus.Subscribe<DecrementOrderStockCommandMsg, IIntegrationEventHandler<DecrementOrderStockCommandMsg>>();
eventBus.Subscribe<ConfirmOrderStockCommand, IIntegrationEventHandler<ConfirmOrderStockCommand>>();
eventBus.Subscribe<DecrementOrderStockCommand, IIntegrationEventHandler<DecrementOrderStockCommand>>();
} }
} }
} }

+ 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.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);


+ 3
- 5
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderGracePeriodConfirmed/OrderStatusChangedToAwaitingValidationDomainEventHandler.cs View File

@ -28,8 +28,6 @@
public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent) public async Task Handle(OrderStatusChangedToAwaitingValidationDomainEvent orderStatusChangedToAwaitingValidationDomainEvent)
{ {
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent)) _logger.CreateLogger(nameof(OrderStatusChangedToAwaitingValidationDomainEvent))
.LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " + .LogTrace($"Order with Id: {orderStatusChangedToAwaitingValidationDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.AwaitingValidation.Id}"); $"a status order id: {OrderStatus.AwaitingValidation.Id}");
@ -37,10 +35,10 @@
var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems var orderStockList = orderStatusChangedToAwaitingValidationDomainEvent.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
var confirmOrderStockEvent = new ConfirmOrderStockCommandMsg(orderStatusChangedToAwaitingValidationDomainEvent.OrderId,
var confirmOrderStockCommand = new ConfirmOrderStockCommand(orderStatusChangedToAwaitingValidationDomainEvent.OrderId,
orderStockList); orderStockList);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(confirmOrderStockEvent);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(confirmOrderStockEvent);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(confirmOrderStockCommand);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(confirmOrderStockCommand);
} }
} }
} }

+ 3
- 10
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderPaid/OrderStatusChangedToPaidDomainEventHandler.cs View File

@ -28,8 +28,6 @@
public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent) public async Task Handle(OrderStatusChangedToPaidDomainEvent orderStatusChangedToPaidDomainEvent)
{ {
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler)) _logger.CreateLogger(nameof(OrderStatusChangedToPaidDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " + .LogTrace($"Order with Id: {orderStatusChangedToPaidDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.Paid.Id}"); $"a status order id: {OrderStatus.Paid.Id}");
@ -37,15 +35,10 @@
var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems var orderStockList = orderStatusChangedToPaidDomainEvent.OrderItems
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits())); .Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
var decrementOrderStockCommandMsg = new DecrementOrderStockCommandMsg(orderStatusChangedToPaidDomainEvent.OrderId,
var decrementOrderStockCommand = new DecrementOrderStockCommand(orderStatusChangedToPaidDomainEvent.OrderId,
orderStockList); 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);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(decrementOrderStockCommand);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(decrementOrderStockCommand);
} }
} }
} }

+ 3
- 5
src/Services/Ordering/Ordering.API/Application/DomainEventHandlers/OrderStockConfirmed/OrderStatusChangedToStockConfirmedDomainEventHandler.cs View File

@ -27,15 +27,13 @@
public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent) public async Task Handle(OrderStatusChangedToStockConfirmedDomainEvent orderStatusChangedToStockConfirmedDomainEvent)
{ {
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
_logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler)) _logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler))
.LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " + .LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " +
$"a status order id: {OrderStatus.StockConfirmed.Id}"); $"a status order id: {OrderStatus.StockConfirmed.Id}");
var payOrderCommandMsg = new PayOrderCommandMsg(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg);
var payOrderCommand = new PayOrderCommand(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommand);
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommand);
} }
} }
} }

src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommandMsg.cs → src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmGracePeriodCommand.cs View File

@ -2,11 +2,11 @@
namespace Ordering.API.Application.IntegrationCommands.Commands namespace Ordering.API.Application.IntegrationCommands.Commands
{ {
public class ConfirmGracePeriodCommandMsg : IntegrationEvent
public class ConfirmGracePeriodCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public ConfirmGracePeriodCommandMsg(int orderId) =>
public ConfirmGracePeriodCommand(int orderId) =>
OrderId = orderId; OrderId = orderId;
} }
} }

src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmOrderStockCommandMsg.cs → src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/ConfirmOrderStockCommand.cs View File

@ -3,12 +3,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class ConfirmOrderStockCommandMsg : IntegrationEvent
public class ConfirmOrderStockCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; } public IEnumerable<OrderStockItem> OrderStockItems { get; }
public ConfirmOrderStockCommandMsg(int orderId,
public ConfirmOrderStockCommand(int orderId,
IEnumerable<OrderStockItem> orderStockItems) IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;

src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/DecrementOrderStockCommandMsg.cs → src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/DecrementOrderStockCommand.cs View File

@ -3,12 +3,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class DecrementOrderStockCommandMsg : IntegrationEvent
public class DecrementOrderStockCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public IEnumerable<OrderStockItem> OrderStockItems { get; } public IEnumerable<OrderStockItem> OrderStockItems { get; }
public DecrementOrderStockCommandMsg(int orderId,
public DecrementOrderStockCommand(int orderId,
IEnumerable<OrderStockItem> orderStockItems) IEnumerable<OrderStockItem> orderStockItems)
{ {
OrderId = orderId; OrderId = orderId;

src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/PayOrderCommandMsg.cs → src/Services/Ordering/Ordering.API/Application/IntegrationCommands/Commands/PayOrderCommand.cs View File

@ -2,11 +2,11 @@
{ {
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class PayOrderCommandMsg : IntegrationEvent
public class PayOrderCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public PayOrderCommandMsg(int orderId)
public PayOrderCommand(int orderId)
{ {
OrderId = orderId; OrderId = orderId;
} }

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

@ -1,14 +0,0 @@
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;
}
}
}

+ 3
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderPaymentFailedIntegrationEventHandler.cs View File

@ -17,9 +17,11 @@
public async Task Handle(OrderPaymentFailedIntegrationEvent @event) public async Task Handle(OrderPaymentFailedIntegrationEvent @event)
{ {
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
var orderToUpdate = await _orderRepository.GetWithDependenciesAsync(@event.OrderId);
orderToUpdate.SetCancelledStatus(); orderToUpdate.SetCancelledStatus();
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
} }
} }
} }

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

@ -17,9 +17,11 @@
public async Task Handle(OrderPaymentSuccededIntegrationEvent @event) public async Task Handle(OrderPaymentSuccededIntegrationEvent @event)
{ {
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
var orderToUpdate = await _orderRepository.GetWithDependenciesAsync(@event.OrderId);
orderToUpdate.SetPaidStatus(); orderToUpdate.SetPaidStatus();
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
} }
} }
} }

+ 3
- 1
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockConfirmedIntegrationEventHandler.cs View File

@ -17,9 +17,11 @@
public async Task Handle(OrderStockConfirmedIntegrationEvent @event) public async Task Handle(OrderStockConfirmedIntegrationEvent @event)
{ {
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
var orderToUpdate = await _orderRepository.GetWithDependenciesAsync(@event.OrderId);
orderToUpdate.SetStockConfirmedStatus(); orderToUpdate.SetStockConfirmedStatus();
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
} }
} }
} }

+ 4
- 2
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/EventHandling/OrderStockNotConfirmedIntegrationEventHandler.cs View File

@ -19,13 +19,15 @@ namespace Ordering.API.Application.IntegrationEvents.EventHandling
public async Task Handle(OrderStockNotConfirmedIntegrationEvent @event) public async Task Handle(OrderStockNotConfirmedIntegrationEvent @event)
{ {
var orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
var orderToUpdate = await _orderRepository.GetWithDependenciesAsync(@event.OrderId);
var orderStockNotConfirmedItems = @event.OrderStockItems var orderStockNotConfirmedItems = @event.OrderStockItems
.FindAll(c => !c.Confirmed)
.FindAll(c => !c.HasStock)
.Select(c => c.ProductId); .Select(c => c.ProductId);
orderToUpdate.SetStockConfirmedStatus(orderStockNotConfirmedItems); orderToUpdate.SetStockConfirmedStatus(orderStockNotConfirmedItems);
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
} }
} }
} }

+ 3
- 3
src/Services/Ordering/Ordering.API/Application/IntegrationEvents/Events/OrderStockNotConfirmedIntegrationEvent.cs View File

@ -21,12 +21,12 @@ namespace Ordering.API.Application.IntegrationEvents.Events
public class ConfirmedOrderStockItem public class ConfirmedOrderStockItem
{ {
public int ProductId { get; } public int ProductId { get; }
public bool Confirmed { get; }
public bool HasStock { get; }
public ConfirmedOrderStockItem(int productId, bool confirmed)
public ConfirmedOrderStockItem(int productId, bool hasStock)
{ {
ProductId = productId; ProductId = productId;
Confirmed = confirmed;
HasStock = hasStock;
} }
} }
} }

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

@ -21,7 +21,7 @@ namespace Ordering.API.Application.Sagas
/// with the validations. /// with the validations.
/// </summary> /// </summary>
public class OrderProcessSaga : OrderSaga, public class OrderProcessSaga : OrderSaga,
IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>,
IIntegrationEventHandler<ConfirmGracePeriodCommand>,
IAsyncRequestHandler<CancelOrderCommand, bool>, IAsyncRequestHandler<CancelOrderCommand, bool>,
IAsyncRequestHandler<ShipOrderCommand, bool> IAsyncRequestHandler<ShipOrderCommand, bool>
{ {
@ -43,17 +43,13 @@ namespace Ordering.API.Application.Sagas
/// period has completed. /// period has completed.
/// </param> /// </param>
/// <returns></returns> /// <returns></returns>
public async Task Handle(ConfirmGracePeriodCommandMsg @event)
public async Task Handle(ConfirmGracePeriodCommand command)
{ {
var orderSaga = FindSagaById(@event.OrderId);
var orderSaga = FindSagaById(command.OrderId);
CheckValidSagaId(orderSaga); CheckValidSagaId(orderSaga);
if (orderSaga.OrderStatus != OrderStatus.Cancelled)
{
orderSaga.SetAwaitingValidationStatus();
await SaveChangesAsync();
}
orderSaga.SetAwaitingValidationStatus();
await SaveChangesAsync();
} }
/// <summary> /// <summary>
@ -68,14 +64,9 @@ namespace Ordering.API.Application.Sagas
var orderSaga = FindSagaById(command.OrderNumber); var orderSaga = FindSagaById(command.OrderNumber);
CheckValidSagaId(orderSaga); CheckValidSagaId(orderSaga);
// Not possible to cancel order when
// it has already been shipped
if (orderSaga.GetOrderStatusId() != OrderStatus.Cancelled.Id
|| orderSaga.GetOrderStatusId() != OrderStatus.Shipped.Id)
{
orderSaga.SetCancelledStatus();
result = await SaveChangesAsync();
}
orderSaga.SetCancelledStatus();
result = await SaveChangesAsync();
return result; return result;
} }
@ -91,13 +82,9 @@ namespace Ordering.API.Application.Sagas
var orderSaga = FindSagaById(command.OrderNumber); var orderSaga = FindSagaById(command.OrderNumber);
CheckValidSagaId(orderSaga); CheckValidSagaId(orderSaga);
// Only ship order when
// its status is paid
if (orderSaga.GetOrderStatusId() == OrderStatus.Paid.Id)
{
orderSaga.SetShippedStatus();
result = await SaveChangesAsync();
}
orderSaga.SetShippedStatus();
result = await SaveChangesAsync();
return result; return result;
} }


+ 4
- 7
src/Services/Ordering/Ordering.API/Application/Sagas/OrderSaga.cs View File

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
@ -20,10 +18,9 @@ namespace Ordering.API.Application.Sagas
public override Order FindSagaById(int id) public override Order FindSagaById(int id)
{ {
var order = _orderingContext.Orders var order = _orderingContext.Orders
.Single(c => c.Id == id);
_orderingContext.Entry(order)
.Member("OrderStatus");
.Include(c => c.OrderStatus)
.Include(c => c.OrderItems)
.Single(c => c.Id == id);
return order; return order;
} }


+ 25
- 0
src/Services/Ordering/Ordering.API/Startup.cs View File

@ -31,6 +31,7 @@
using System; using System;
using System.Data.Common; using System.Data.Common;
using System.Reflection; using System.Reflection;
using global::Ordering.API.Application.IntegrationEvents.EventHandling;
public class Startup public class Startup
{ {
@ -124,6 +125,7 @@
return new DefaultRabbitMQPersistentConnection(factory, logger); return new DefaultRabbitMQPersistentConnection(factory, logger);
}); });
RegisterServiceBus(services);
services.AddSingleton<IEventBus, EventBusRabbitMQ>(); services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>(); services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<UserCheckoutAcceptedIntegrationEventHandler>(); services.AddTransient<UserCheckoutAcceptedIntegrationEventHandler>();
@ -170,10 +172,33 @@
} }
private void RegisterServiceBus(IServiceCollection services)
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>();
services.AddTransient<IIntegrationEventHandler<ConfirmGracePeriodCommand>, OrderProcessSaga>();
services.AddTransient<IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>,
OrderStockConfirmedIntegrationEventHandler>();
services.AddTransient<IIntegrationEventHandler<OrderStockNotConfirmedIntegrationEvent>,
OrderStockNotConfirmedIntegrationEventHandler>();
services.AddTransient<IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>,
OrderPaymentFailedIntegrationEventHandler>();
services.AddTransient<IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>,
OrderPaymentSuccededIntegrationEventHandler>();
}
private void ConfigureEventBus(IApplicationBuilder app) private void ConfigureEventBus(IApplicationBuilder app)
{ {
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, IIntegrationEventHandler<UserCheckoutAcceptedIntegrationEvent>>();
eventBus.Subscribe<ConfirmGracePeriodCommand, IIntegrationEventHandler<ConfirmGracePeriodCommand>>();
eventBus.Subscribe<OrderStockConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockConfirmedIntegrationEvent>>();
eventBus.Subscribe<OrderStockNotConfirmedIntegrationEvent, IIntegrationEventHandler<OrderStockNotConfirmedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentFailedIntegrationEvent, IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent>>();
eventBus.Subscribe<OrderPaymentSuccededIntegrationEvent, IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent>>();
eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, UserCheckoutAcceptedIntegrationEventHandler>(); eventBus.Subscribe<UserCheckoutAcceptedIntegrationEvent, UserCheckoutAcceptedIntegrationEventHandler>();
eventBus.Subscribe<ConfirmGracePeriodCommandMsg, IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>>(); eventBus.Subscribe<ConfirmGracePeriodCommandMsg, IIntegrationEventHandler<ConfirmGracePeriodCommandMsg>>();


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

@ -13,5 +13,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
void Update(Order order); void Update(Order order);
Task<Order> GetAsync(int orderId); Task<Order> GetAsync(int orderId);
Task<Order> GetWithDependenciesAsync(int orderId);
} }
} }

+ 11
- 22
src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs View File

@ -30,7 +30,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
private readonly List<OrderItem> _orderItems; private readonly List<OrderItem> _orderItems;
public IEnumerable<OrderItem> OrderItems => _orderItems.AsReadOnly();
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
// Using List<>.AsReadOnly() // Using List<>.AsReadOnly()
// This will create a read only wrapper around the private list so is protected against "external updates". // This will create a read only wrapper around the private list so is protected against "external updates".
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
@ -95,21 +95,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
} }
#region Status Changes #region Status Changes
public void SetSubmitedStatus()
{
_orderStatusId = OrderStatus.Submited.Id;
}
public void SetAwaitingValidationStatus() public void SetAwaitingValidationStatus()
{ {
if (_orderStatusId != OrderStatus.Submited.Id)
if (_orderStatusId != OrderStatus.Submited.Id &&
_orderStatusId != OrderStatus.Cancelled.Id)
{ {
StatusChangeException(); StatusChangeException();
} }
_orderStatusId = OrderStatus.AwaitingValidation.Id;
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, OrderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id;
} }
public void SetStockConfirmedStatus(IEnumerable<int> orderStockNotConfirmedItems = null) public void SetStockConfirmedStatus(IEnumerable<int> orderStockNotConfirmedItems = null)
@ -121,15 +118,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
if (orderStockNotConfirmedItems is null) if (orderStockNotConfirmedItems is null)
{ {
OrderStatus = OrderStatus.StockConfirmed;
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock."; _description = "All the items were confirmed with available stock.";
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
} }
else else
{ {
OrderStatus = OrderStatus.Cancelled;
_orderStatusId = OrderStatus.Cancelled.Id;
var itemsStockNotConfirmedProductNames = OrderItems var itemsStockNotConfirmedProductNames = OrderItems
.Where(c => orderStockNotConfirmedItems.Contains(c.ProductId)) .Where(c => orderStockNotConfirmedItems.Contains(c.ProductId))
@ -147,10 +143,10 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
StatusChangeException(); StatusChangeException();
} }
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
_orderStatusId = OrderStatus.Paid.Id; _orderStatusId = OrderStatus.Paid.Id;
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\""; _description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
} }
public void SetShippedStatus() public void SetShippedStatus()
@ -162,15 +158,13 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
_orderStatusId = OrderStatus.Shipped.Id; _orderStatusId = OrderStatus.Shipped.Id;
_description = ""; _description = "";
//Call Domain Event
} }
public void SetCancelledStatus() public void SetCancelledStatus()
{ {
if (_orderStatusId == OrderStatus.Submited.Id) if (_orderStatusId == OrderStatus.Submited.Id)
{ {
_description = "The order was cancelled before the grace period was confirm.";
_description = "The order was cancelled before the grace period was confirmed.";
} }
else if (_orderStatusId == OrderStatus.AwaitingValidation.Id) else if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{ {
@ -194,11 +188,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O
#endregion #endregion
public int GetOrderStatusId()
{
return _orderStatusId;
}
private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber, private void AddOrderStartedDomainEvent(string userId, int cardTypeId, string cardNumber,
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
{ {


+ 8
- 0
src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs View File

@ -36,6 +36,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor
return await _context.Orders.FindAsync(orderId); return await _context.Orders.FindAsync(orderId);
} }
public async Task<Order> GetWithDependenciesAsync(int orderId)
{
return await _context.Orders
.Include(c => c.OrderStatus)
.Include(c => c.OrderItems)
.SingleAsync(c => c.Id == orderId);
}
public void Update(Order order) public void Update(Order order)
{ {
_context.Entry(order).State = EntityState.Modified; _context.Entry(order).State = EntityState.Modified;


src/Services/Payment/Payment.API/IntegrationCommands/CommandHandlers/PayOrderCommandMsgHandler.cs → src/Services/Payment/Payment.API/IntegrationCommands/CommandHandlers/PayOrderCommandHandler.cs View File

@ -3,18 +3,17 @@
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Payment.API.IntegrationCommands.Commands; using Payment.API.IntegrationCommands.Commands;
using System.Threading.Tasks; using System.Threading.Tasks;
using System;
using Payment.API.IntegrationEvents; using Payment.API.IntegrationEvents;
using Payment.API.IntegrationEvents.Events; using Payment.API.IntegrationEvents.Events;
public class PayOrderCommandMsgHandler : IIntegrationEventHandler<PayOrderCommandMsg>
public class PayOrderCommandHandler : IIntegrationEventHandler<PayOrderCommand>
{ {
private readonly IPaymentIntegrationEventService _paymentIntegrationEventService; private readonly IPaymentIntegrationEventService _paymentIntegrationEventService;
public PayOrderCommandMsgHandler(IPaymentIntegrationEventService paymentIntegrationEventService)
public PayOrderCommandHandler(IPaymentIntegrationEventService paymentIntegrationEventService)
=> _paymentIntegrationEventService = paymentIntegrationEventService; => _paymentIntegrationEventService = paymentIntegrationEventService;
public async Task Handle(PayOrderCommandMsg @event)
public async Task Handle(PayOrderCommand @event)
{ {
//PAYMENT SUCCESSED //PAYMENT SUCCESSED
var orderPaymentSuccededIntegrationEvent = new OrderPaymentSuccededIntegrationEvent(@event.OrderId); var orderPaymentSuccededIntegrationEvent = new OrderPaymentSuccededIntegrationEvent(@event.OrderId);
@ -25,4 +24,4 @@
//_paymentIntegrationEventService.PublishThroughEventBus(orderPaymentFailedIntegrationEvent); //_paymentIntegrationEventService.PublishThroughEventBus(orderPaymentFailedIntegrationEvent);
} }
} }
}
}

src/Services/Payment/Payment.API/IntegrationCommands/Commands/PayOrderCommandMsg.cs → src/Services/Payment/Payment.API/IntegrationCommands/Commands/PayOrderCommand.cs View File

@ -2,10 +2,10 @@
{ {
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
public class PayOrderCommandMsg : IntegrationEvent
public class PayOrderCommand : IntegrationEvent
{ {
public int OrderId { get; } public int OrderId { get; }
public PayOrderCommandMsg(int orderId) => OrderId = orderId;
public PayOrderCommand(int orderId) => OrderId = orderId;
} }
} }

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

@ -29,6 +29,9 @@
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBus\EventBus.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" /> <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\IntegrationEventLogEF\IntegrationEventLogEF.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.AspNetCore.HealthChecks\Microsoft.AspNetCore.HealthChecks.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks.SqlServer\Microsoft.Extensions.HealthChecks.SqlServer.csproj" />
<ProjectReference Include="..\..\..\BuildingBlocks\HealthChecks\src\Microsoft.Extensions.HealthChecks\Microsoft.Extensions.HealthChecks.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

+ 13
- 3
src/Services/Payment/Payment.API/Startup.cs View File

@ -11,6 +11,8 @@ using Payment.API.IntegrationCommands.Commands;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using RabbitMQ.Client; using RabbitMQ.Client;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Payment.API.IntegrationEvents;
using Payment.API.IntegrationCommands.CommandHandlers;
namespace Payment.API namespace Payment.API
{ {
@ -34,6 +36,7 @@ namespace Payment.API
// Add framework services. // Add framework services.
services.AddMvc(); services.AddMvc();
services.AddTransient<IPaymentIntegrationEventService, PaymentIntegrationEventService>();
services.AddSingleton<IRabbitMQPersistentConnection>(sp => services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{ {
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>(); var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
@ -46,8 +49,7 @@ namespace Payment.API
return new DefaultRabbitMQPersistentConnection(factory, logger); return new DefaultRabbitMQPersistentConnection(factory, logger);
}); });
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
RegisterServiceBus(services);
services.AddSwaggerGen(); services.AddSwaggerGen();
services.ConfigureSwaggerGen(options => services.ConfigureSwaggerGen(options =>
@ -81,10 +83,18 @@ namespace Payment.API
ConfigureEventBus(app); ConfigureEventBus(app);
} }
private void RegisterServiceBus(IServiceCollection services)
{
services.AddSingleton<IEventBus, EventBusRabbitMQ>();
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
services.AddTransient<IIntegrationEventHandler<PayOrderCommand>, PayOrderCommandHandler>();
}
private void ConfigureEventBus(IApplicationBuilder app) private void ConfigureEventBus(IApplicationBuilder app)
{ {
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>(); var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<PayOrderCommandMsg, IIntegrationEventHandler<PayOrderCommandMsg>>();
eventBus.Subscribe<PayOrderCommand, IIntegrationEventHandler<PayOrderCommand>>();
} }
} }
} }

+ 11
- 0
src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodCommand.cs View File

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

+ 0
- 14
src/Services/SagaManager/SagaManager/IntegrationEvents/Events/ConfirmGracePeriodCommandMsg.cs View File

@ -1,14 +0,0 @@
namespace SagaManager.IntegrationEvents.Events
{
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
// Integration Events notes:
// 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.
public class ConfirmGracePeriodCommandMsg : IntegrationEvent
{
public int OrderId { get;}
public ConfirmGracePeriodCommandMsg(int orderId) => OrderId = orderId;
}
}

+ 3
- 7
src/Services/SagaManager/SagaManager/IntegrationEvents/SagaManagerIntegrationEventService.cs View File

@ -9,13 +9,9 @@
private readonly IEventBus _eventBus; private readonly IEventBus _eventBus;
public SagaManagerIntegrationEventService(IEventBus eventBus) public SagaManagerIntegrationEventService(IEventBus eventBus)
{
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
}
=> _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
public void PublishThroughEventBus(IntegrationEvent evt)
{
_eventBus.Publish(evt);
}
public void PublishThroughEventBus(IntegrationEvent evt) => _eventBus.Publish(evt);
} }
} }

+ 12
- 21
src/Services/SagaManager/SagaManager/Program.cs View File

@ -1,34 +1,27 @@
using System.Reflection;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
using Microsoft.EntityFrameworkCore;
using SagaManager.IntegrationEvents;
namespace SagaManager
namespace SagaManager
{ {
using System.IO; using System.IO;
using System; using System;
using System.Threading.Tasks;
using Autofac.Extensions.DependencyInjection;
using Autofac;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ; using Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ;
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using RabbitMQ.Client; using RabbitMQ.Client;
using Services; using Services;
using Autofac.Extensions.DependencyInjection;
using Autofac;
using System.Threading.Tasks;
using IntegrationEvents;
public class Program public class Program
{ {
public static IConfigurationRoot Configuration { get; set; } public static IConfigurationRoot Configuration { get; set; }
public static void Main(string[] args)
{
MainAsync().Wait();
}
public static void Main(string[] args) => MainAsync().Wait();
static async Task MainAsync() static async Task MainAsync()
{ {
StartUp(); StartUp();
@ -44,8 +37,8 @@ namespace SagaManager
while (true) while (true)
{ {
sagaManagerService.CheckFinishedGracePeriodOrders();
await Task.Delay(30000);
sagaManagerService.CheckConfirmedGracePeriodOrders();
await Task.Delay(90000);
} }
} }
@ -66,8 +59,7 @@ namespace SagaManager
.Configure<SagaManagerSettings>(Configuration) .Configure<SagaManagerSettings>(Configuration)
.AddSingleton<ISagaManagerService, SagaManagerService>() .AddSingleton<ISagaManagerService, SagaManagerService>()
.AddSingleton<ISagaManagerIntegrationEventService, SagaManagerIntegrationEventService>() .AddSingleton<ISagaManagerIntegrationEventService, SagaManagerIntegrationEventService>()
.AddSingleton<IEventBus, EventBusRabbitMQ>()
.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>()
.AddSingleton<IRabbitMQPersistentConnection>(sp => .AddSingleton<IRabbitMQPersistentConnection>(sp =>
{ {
var settings = sp.GetRequiredService<IOptions<SagaManagerSettings>>().Value; var settings = sp.GetRequiredService<IOptions<SagaManagerSettings>>().Value;
@ -78,8 +70,7 @@ namespace SagaManager
}; };
return new DefaultRabbitMQPersistentConnection(factory, logger); return new DefaultRabbitMQPersistentConnection(factory, logger);
})
.AddSingleton<IEventBus, EventBusRabbitMQ>();
});
RegisterServiceBus(services); RegisterServiceBus(services);


+ 1
- 1
src/Services/SagaManager/SagaManager/Services/ISagaManagerService.cs View File

@ -2,6 +2,6 @@
{ {
public interface ISagaManagerService public interface ISagaManagerService
{ {
void CheckFinishedGracePeriodOrders();
void CheckConfirmedGracePeriodOrders();
} }
} }

+ 7
- 17
src/Services/SagaManager/SagaManager/Services/SagaManagerService.cs View File

@ -1,12 +1,9 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace SagaManager.Services
namespace SagaManager.Services
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SqlClient; using System.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using Dapper; using Dapper;
using IntegrationEvents; using IntegrationEvents;
using IntegrationEvents.Events; using IntegrationEvents.Events;
@ -26,17 +23,18 @@ namespace SagaManager.Services
_logger = logger; _logger = logger;
} }
public void CheckFinishedGracePeriodOrders()
public void CheckConfirmedGracePeriodOrders()
{ {
var orderIds = GetFinishedGracePeriodOrders();
var orderIds = GetConfirmedGracePeriodOrders();
foreach (var orderId in orderIds) foreach (var orderId in orderIds)
{ {
Publish(orderId);
var confirmGracePeriodEvent = new ConfirmGracePeriodCommand(orderId);
_sagaManagerIntegrationEventService.PublishThroughEventBus(confirmGracePeriodEvent);
} }
} }
private IEnumerable<int> GetFinishedGracePeriodOrders()
private IEnumerable<int> GetConfirmedGracePeriodOrders()
{ {
IEnumerable<int> orderIds = new List<int>(); IEnumerable<int> orderIds = new List<int>();
using (var conn = new SqlConnection(_settings.ConnectionString)) using (var conn = new SqlConnection(_settings.ConnectionString))
@ -60,13 +58,5 @@ namespace SagaManager.Services
return orderIds; return orderIds;
} }
private void Publish(int orderId)
{
var confirmGracePeriodEvent = new ConfirmGracePeriodCommandMsg(orderId);
// Publish through the Event Bus
_sagaManagerIntegrationEventService.PublishThroughEventBus(confirmGracePeriodEvent);
}
} }
} }

Loading…
Cancel
Save