@ -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; | |||||
} | |||||
} | |||||
} |
@ -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 | 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"); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | |||||
} |
@ -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; | |||||
} | |||||
} |