@ -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 System.Collections.Generic; | |||
public class ConfirmOrderStockIntegrationEvent : IntegrationEvent | |||
public class ConfirmOrderStockCommandMsg : IntegrationEvent | |||
{ | |||
public int OrderId { get; } | |||
public IEnumerable<OrderStockItem> OrderStockItems { get; } | |||
public ConfirmOrderStockIntegrationEvent(int orderId, | |||
public ConfirmOrderStockCommandMsg(int orderId, | |||
IEnumerable<OrderStockItem> orderStockItems) | |||
{ | |||
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; | |||
} | |||
} | |||
} |
@ -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); | |||
} | |||
} | |||
} |
@ -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 | |||
{ | |||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
using System.Threading.Tasks; | |||
public class OrderPaymentFailedIntegrationEventHandler : | |||
IIntegrationEventHandler<OrderPaymentFailedIntegrationEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public OrderPaymentFailedIntegrationEventHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
public async Task Handle(OrderPaymentFailedIntegrationEvent @event) | |||
{ | |||
//TODO: Cancel Order | |||
} | |||
} | |||
} |
@ -1,14 +1,35 @@ | |||
namespace Ordering.API.Application.IntegrationEvents.EventHandling | |||
{ | |||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Abstractions; | |||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | |||
using Ordering.API.Application.IntegrationEvents.Events; | |||
using Ordering.Domain.Exceptions; | |||
using System.Threading.Tasks; | |||
public class OrderPaymentSuccededIntegrationEventHandler : | |||
IIntegrationEventHandler<OrderPaymentSuccededIntegrationEvent> | |||
{ | |||
private readonly IOrderRepository _orderRepository; | |||
public OrderPaymentSuccededIntegrationEventHandler(IOrderRepository orderRepository) | |||
{ | |||
_orderRepository = orderRepository; | |||
} | |||
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"); | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -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(); | |||
} | |||
} | |||
} |
@ -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; | |||
} | |||
} |