Merge
This commit is contained in:
commit
4e79875d26
@ -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}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -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,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
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
namespace Microsoft.eShopOnContainers.Services.Catalog.API.IntegrationEvents.Events
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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>>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
public class OrderStatusChangedToStockConfirmedDomainEventHandler
|
||||||
: IAsyncNotificationHandler<OrderStockMethodVerifiedDomainEvent>
|
: 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);
|
await _orderRepository.UnitOfWork.SaveEntitiesAsync();
|
||||||
orderToUpdate.SetOrderStatusId(orderStockMethodVerifiedDomainEvent.OrderStatus.Id);
|
|
||||||
|
|
||||||
_orderRepository.Update(orderToUpdate);
|
|
||||||
|
|
||||||
await _orderRepository.UnitOfWork
|
_logger.CreateLogger(nameof(OrderStatusChangedToStockConfirmedDomainEventHandler))
|
||||||
.SaveEntitiesAsync();
|
.LogTrace($"Order with Id: {orderStatusChangedToStockConfirmedDomainEvent.OrderId} has been successfully updated with " +
|
||||||
|
$"a status order id: {OrderStatus.StockConfirmed.Id}");
|
||||||
|
|
||||||
_logger.CreateLogger(nameof(UpdateOrderWhenOrderStockMethodVerifiedDomainEventHandler))
|
var payOrderCommandMsg = new PayOrderCommandMsg(orderStatusChangedToStockConfirmedDomainEvent.OrderId);
|
||||||
.LogTrace($"Order with Id: {orderStockMethodVerifiedDomainEvent.OrderId} has been successfully updated with " +
|
|
||||||
$"a status order id: { orderStockMethodVerifiedDomainEvent.OrderStatus.Id }");
|
|
||||||
|
|
||||||
var payOrderCommandMsg = new PayOrderCommandMsg(orderToUpdate.Id);
|
|
||||||
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg);
|
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(payOrderCommandMsg);
|
||||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg);
|
await _orderingIntegrationEventService.PublishThroughEventBusAsync(payOrderCommandMsg);
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,
|
public OrderStockConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
|
||||||
{
|
{
|
||||||
_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();
|
order.SetStockConfirmedStatus();
|
||||||
|
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckValidSagaId(Order orderSaga)
|
private void CheckValidSagaId(Order orderSaga)
|
||||||
|
@ -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 Domain.Exceptions;
|
||||||
using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure;
|
|
||||||
using Ordering.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,
|
public OrderStockNotConfirmedIntegrationEventHandler(IOrderRepository orderRepository)
|
||||||
IOrderingIntegrationEventService orderingIntegrationEventService)
|
|
||||||
{
|
{
|
||||||
_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 orderToUpdate = await _orderRepository.GetAsync(@event.OrderId);
|
||||||
var order = await _orderRepository.GetAsync(@event.OrderId);
|
CheckValidSagaId(orderToUpdate);
|
||||||
CheckValidSagaId(order);
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -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);
|
orderSaga.SetAwaitingValidationStatus();
|
||||||
|
|
||||||
await SaveChangesAsync();
|
await SaveChangesAsync();
|
||||||
|
|
||||||
var orderStockList = orderSaga.OrderItems
|
|
||||||
.Select(orderItem => new OrderStockItem(orderItem.ProductId, orderItem.GetUnits()));
|
|
||||||
|
|
||||||
var confirmOrderStockEvent = new ConfirmOrderStockCommandMsg(orderSaga.Id, orderStockList);
|
|
||||||
|
|
||||||
await _orderingIntegrationEventService.SaveEventAndOrderingContextChangesAsync(confirmOrderStockEvent);
|
|
||||||
|
|
||||||
await _orderingIntegrationEventService.PublishThroughEventBusAsync(confirmOrderStockEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,17 +14,8 @@ namespace Ordering.API.Application.Sagas
|
|||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TEntity FindSagaById(int id, DbContext context = null)
|
public abstract TEntity FindSagaById(int id);
|
||||||
{
|
|
||||||
var ctx = context ?? _dbContext;
|
|
||||||
return ctx.Set<TEntity>().Where(x => x.Id == id).SingleOrDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task<bool> SaveChangesAsync(DbContext context = null)
|
public abstract Task<bool> SaveChangesAsync();
|
||||||
{
|
|
||||||
var ctx = context ?? _dbContext;
|
|
||||||
var result = await ctx.SaveChangesAsync();
|
|
||||||
return result > 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 =>
|
||||||
|
@ -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 SetOrderStockConfirmed(IEnumerable<int> orderStockNotConfirmedItems = null)
|
public void SetAwaitingValidationStatus()
|
||||||
{
|
{
|
||||||
if(orderStockNotConfirmedItems is null)
|
if (_orderStatusId != OrderStatus.Submited.Id)
|
||||||
{
|
{
|
||||||
OrderStatus = OrderStatus.StockValidated;
|
StatusChangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
||||||
|
|
||||||
|
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, OrderItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStockConfirmedStatus(IEnumerable<int> orderStockNotConfirmedItems = null)
|
||||||
|
{
|
||||||
|
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
|
||||||
|
{
|
||||||
|
StatusChangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderStockNotConfirmedItems is null)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user