Skip to content

Commit c8f0776

Browse files
committed
Added domain events in Ordering Api
1 parent c38e078 commit c8f0776

25 files changed

Lines changed: 685 additions & 137 deletions

File tree

eShopOnContainers-ServicesAndWebApps.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ EndProject
4141
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{621E7211-58D0-45FD-9600-1CB490BD930E}"
4242
EndProject
4343
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\UnitTest\UnitTest.csproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}"
44+
ProjectSection(ProjectDependencies) = postProject
45+
{A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B}
46+
{621E7211-58D0-45FD-9600-1CB490BD930E} = {621E7211-58D0-45FD-9600-1CB490BD930E}
47+
{FEA0C318-FFED-4D39-8781-265718CA43DD} = {FEA0C318-FFED-4D39-8781-265718CA43DD}
48+
{F16E3C6A-1C94-4EAB-BE91-099618060B68} = {F16E3C6A-1C94-4EAB-BE91-099618060B68}
49+
EndProjectSection
4450
EndProject
4551
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}"
4652
EndProject

src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using MediatR;
33
using System.Collections.Generic;
44
using System.Runtime.Serialization;
5+
using System.Collections;
56

67
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
78
{
@@ -51,6 +52,12 @@ public class CreateOrderCommand
5152
[DataMember]
5253
public int CardTypeId { get; private set; }
5354

55+
[DataMember]
56+
public int PaymentId { get; private set; }
57+
58+
[DataMember]
59+
public int BuyerId { get; private set; }
60+
5461
[DataMember]
5562
public IEnumerable<OrderItemDTO> OrderItems => _orderItems;
5663

@@ -66,7 +73,7 @@ public CreateOrderCommand()
6673

6774
public CreateOrderCommand(string city, string street, string state, string country, string zipcode,
6875
string cardNumber, string cardHolderName, DateTime cardExpiration,
69-
string cardSecurityNumber, int cardTypeId) : this()
76+
string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this()
7077
{
7178
City = city;
7279
Street = street;
@@ -75,12 +82,14 @@ public CreateOrderCommand(string city, string street, string state, string count
7582
ZipCode = zipcode;
7683
CardNumber = cardNumber;
7784
CardHolderName = cardHolderName;
85+
CardExpiration = cardExpiration;
7886
CardSecurityNumber = cardSecurityNumber;
7987
CardTypeId = cardTypeId;
8088
CardExpiration = cardExpiration;
89+
PaymentId = paymentId;
90+
BuyerId = buyerId;
8191
}
8292

83-
8493
public class OrderItemDTO
8594
{
8695
public int ProductId { get; set; }

src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands
22
{
3-
using Domain.AggregatesModel.BuyerAggregate;
43
using Domain.AggregatesModel.OrderAggregate;
54
using MediatR;
65
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
@@ -24,16 +23,16 @@ protected override bool CreateResultForDuplicateRequest()
2423
public class CreateOrderCommandHandler
2524
: IAsyncRequestHandler<CreateOrderCommand, bool>
2625
{
27-
private readonly IBuyerRepository<Buyer> _buyerRepository;
2826
private readonly IOrderRepository<Order> _orderRepository;
2927
private readonly IIdentityService _identityService;
28+
private readonly IMediator _mediator;
3029

3130
// Using DI to inject infrastructure persistence Repositories
32-
public CreateOrderCommandHandler(IBuyerRepository<Buyer> buyerRepository, IOrderRepository<Order> orderRepository, IIdentityService identityService)
31+
public CreateOrderCommandHandler(IMediator mediator, IOrderRepository<Order> orderRepository, IIdentityService identityService)
3332
{
34-
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
3533
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
3634
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
35+
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
3736
}
3837

3938
public async Task<bool> Handle(CreateOrderCommand message)
@@ -42,45 +41,18 @@ public async Task<bool> Handle(CreateOrderCommand message)
4241
// DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root
4342
// methods and constructor so validations, invariants and business logic
4443
// make sure that consistency is preserved across the whole aggregate
45-
46-
var cardTypeId = message.CardTypeId != 0 ? message.CardTypeId : 1;
47-
48-
var buyerGuid = _identityService.GetUserIdentity();
49-
var buyer = await _buyerRepository.FindAsync(buyerGuid);
50-
51-
if (buyer == null)
52-
{
53-
buyer = new Buyer(buyerGuid);
54-
}
55-
56-
var payment = buyer.AddPaymentMethod(cardTypeId,
57-
$"Payment Method on {DateTime.UtcNow}",
58-
message.CardNumber,
59-
message.CardSecurityNumber,
60-
message.CardHolderName,
61-
message.CardExpiration);
62-
63-
_buyerRepository.Add(buyer);
64-
65-
await _buyerRepository.UnitOfWork
66-
.SaveChangesAsync();
67-
68-
// Create the Order AggregateRoot
69-
// DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root
70-
// methods and constructor so validations, invariants and business logic
71-
// make sure that consistency is preserved across the whole aggregate
72-
73-
var order = new Order(buyer.Id, payment.Id, new Address(message.Street, message.City, message.State, message.Country, message.ZipCode));
44+
var address = new Address(message.Street, message.City, message.State, message.Country, message.ZipCode);
45+
var order = new Order(address , message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration);
7446

7547
foreach (var item in message.OrderItems)
7648
{
7749
order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units);
7850
}
7951

80-
_orderRepository.Add(order);
52+
_orderRepository.Add(order);
8153

8254
var result = await _orderRepository.UnitOfWork
83-
.SaveChangesAsync();
55+
.SaveEntitiesAsync();
8456

8557
return result > 0;
8658

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using MediatR;
2+
using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services;
3+
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
4+
using Microsoft.Extensions.Logging;
5+
using Ordering.Domain.Events;
6+
using System;
7+
using System.Threading.Tasks;
8+
9+
namespace Ordering.API.Application.EventHandlers
10+
{
11+
public class OrderCreatedEventHandler : IAsyncNotificationHandler<OrderCreated>
12+
{
13+
private readonly ILoggerFactory _logger;
14+
private readonly IBuyerRepository<Buyer> _buyerRepository;
15+
private readonly IIdentityService _identityService;
16+
17+
public OrderCreatedEventHandler(ILoggerFactory logger, IBuyerRepository<Buyer> buyerRepository, IIdentityService identityService)
18+
{
19+
_buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository));
20+
_identityService = identityService ?? throw new ArgumentNullException(nameof(identityService));
21+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
22+
}
23+
24+
public async Task Handle(OrderCreated orderNotification)
25+
{
26+
var cardTypeId = orderNotification.CardTypeId != 0 ? orderNotification.CardTypeId : 1;
27+
28+
var buyerGuid = _identityService.GetUserIdentity();
29+
var buyer = await _buyerRepository.FindAsync(buyerGuid);
30+
31+
if (buyer == null)
32+
{
33+
buyer = new Buyer(buyerGuid);
34+
}
35+
36+
var payment = buyer.AddPaymentMethod(cardTypeId,
37+
$"Payment Method on {DateTime.UtcNow}",
38+
orderNotification.CardNumber,
39+
orderNotification.CardSecurityNumber,
40+
orderNotification.CardHolderName,
41+
orderNotification.CardExpiration,
42+
orderNotification.Order.Id);
43+
44+
_buyerRepository.Add(buyer);
45+
46+
await _buyerRepository.UnitOfWork
47+
.SaveEntitiesAsync();
48+
49+
_logger.CreateLogger(nameof(OrderCreatedEventHandler)).LogTrace($"A new payment method has been successfully added for orderId: {orderNotification.Order.Id}.");
50+
51+
}
52+
}
53+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using MediatR;
2+
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
3+
using Microsoft.Extensions.Logging;
4+
using Ordering.Domain.Events;
5+
using System;
6+
using System.Threading.Tasks;
7+
8+
namespace Ordering.API.Application.EventHandlers
9+
{
10+
public class PaymentMethodCheckedEventHandler : IAsyncNotificationHandler<PaymentMethodChecked>
11+
{
12+
private readonly IOrderRepository<Order> _orderRepository;
13+
private readonly ILoggerFactory _logger;
14+
public PaymentMethodCheckedEventHandler(IOrderRepository<Order> orderRepository, ILoggerFactory logger)
15+
{
16+
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
17+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
18+
}
19+
20+
public async Task Handle(PaymentMethodChecked paymentMethodNotification)
21+
{
22+
var orderToUpdate = await _orderRepository.GetAsync(paymentMethodNotification.OrderId);
23+
orderToUpdate.SetBuyerId(paymentMethodNotification.Buyer.Id);
24+
orderToUpdate.SetPaymentId(paymentMethodNotification.Payment.Id);
25+
26+
await _orderRepository.UnitOfWork
27+
.SaveEntitiesAsync();
28+
29+
_logger.CreateLogger(nameof(PaymentMethodCheckedEventHandler))
30+
.LogTrace($"Order with Id: {paymentMethodNotification.OrderId} has been successfully updated with a new payment method id: { paymentMethodNotification.Payment.Id }");
31+
}
32+
}
33+
}

src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using MediatR;
2-
using Microsoft.AspNetCore.Authorization;
32
using Microsoft.AspNetCore.Mvc;
43
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
54
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries;
@@ -28,19 +27,19 @@ public OrdersController(IMediator mediator, IOrderQueries orderQueries, IIdentit
2827

2928
[Route("new")]
3029
[HttpPost]
31-
public async Task<IActionResult> CreateOrder([FromBody]CreateOrderCommand createOrderCommand, [FromHeader(Name = "x-requestid")] string requestId)
30+
public async Task<IActionResult> CreateOrder([FromBody]CreateOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId)
3231
{
3332
bool result = false;
3433
if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty)
3534
{
36-
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(createOrderCommand, guid);
35+
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(command, guid);
3736
result = await _mediator.SendAsync(requestCreateOrder);
3837
}
3938
else
4039
{
4140
// If no x-requestid header is found we process the order anyway. This is just temporary to not break existing clients
4241
// that aren't still updated. When all clients were updated this could be removed.
43-
result = await _mediator.SendAsync(createOrderCommand);
42+
result = await _mediator.SendAsync(command);
4443
}
4544

4645
if (result)
@@ -82,7 +81,7 @@ public async Task<IActionResult> GetCardTypes()
8281
var cardTypes = await _orderQueries.GetCardTypes();
8382

8483
return Ok(cardTypes);
85-
}
84+
}
8685
}
8786
}
8887

src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using MediatR;
44
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands;
55
using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Decorators;
6+
using Ordering.API.Application.EventHandlers;
7+
using Ordering.Domain.Events;
68
using System.Collections.Generic;
79
using System.Linq;
810
using System.Reflection;
@@ -14,12 +16,17 @@ public class MediatorModule : Autofac.Module
1416
protected override void Load(ContainerBuilder builder)
1517
{
1618
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly)
17-
.AsImplementedInterfaces();
19+
.AsImplementedInterfaces();
1820

1921
builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly)
2022
.As(o => o.GetInterfaces()
2123
.Where(i => i.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>)))
2224
.Select(i => new KeyedService("IAsyncRequestHandler", i)));
25+
26+
builder
27+
.RegisterAssemblyTypes(typeof(OrderCreatedEventHandler).GetTypeInfo().Assembly)
28+
.Where(t => t.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>)))
29+
.AsImplementedInterfaces();
2330

2431
builder.Register<SingleInstanceFactory>(context =>
2532
{
@@ -37,7 +44,7 @@ protected override void Load(ContainerBuilder builder)
3744

3845
builder.RegisterGenericDecorator(typeof(LogDecorator<,>),
3946
typeof(IAsyncRequestHandler<,>),
40-
"IAsyncRequestHandler");
47+
"IAsyncRequestHandler");
4148
}
4249
}
4350
}

0 commit comments

Comments
 (0)