From c8f0776f1fffefcb957d59d0dd1add7eb4b2d66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ram=C3=B3n=20Tom=C3=A1s?= Date: Tue, 14 Mar 2017 18:02:28 +0100 Subject: [PATCH] Added domain events in Ordering Api --- eShopOnContainers-ServicesAndWebApps.sln | 6 + .../Commands/CreateOrderCommand.cs | 13 +- .../Commands/CreateOrderCommandHandler.cs | 42 +-- .../EventHandlers/OrderCreatedEventHandler.cs | 53 ++++ .../PaymentMethodCheckedEventHandler.cs | 33 +++ .../Controllers/OrdersController.cs | 9 +- .../AutofacModules/MediatorModule.cs | 11 +- .../20170313100034_Domain_events.Designer.cs | 244 ++++++++++++++++++ .../20170313100034_Domain_events.cs | 75 ++++++ .../OrderingContextModelSnapshot.cs | 9 +- .../AggregatesModel/BuyerAggregate/Buyer.cs | 14 +- .../OrderAggregate/IOrderRepository.cs | 5 + .../AggregatesModel/OrderAggregate/Order.cs | 34 ++- .../Ordering.Domain/Events/OrderCreated.cs | 35 +++ .../Events/PaymentMethodChecked.cs | 23 ++ .../Ordering.Domain/Ordering.Domain.csproj | 5 + .../Ordering.Domain/SeedWork/Entity.cs | 18 +- .../Ordering.Domain/SeedWork/IUnitOfWork.cs | 3 +- .../MediatorExtension.cs | 25 ++ .../OrderingContext.cs | 36 ++- .../Repositories/OrderRepository.cs | 18 +- .../Services/Ordering/OrderingScenarios.cs | 8 +- .../IdentifierCommandHandlerTest.cs | 4 +- .../Application/NewOrderCommandHandlerTest.cs | 96 +++---- .../Ordering/Domain/BuyerAggregateTest.cs | 3 +- 25 files changed, 685 insertions(+), 137 deletions(-) create mode 100644 src/Services/Ordering/Ordering.API/Application/EventHandlers/OrderCreatedEventHandler.cs create mode 100644 src/Services/Ordering/Ordering.API/Application/EventHandlers/PaymentMethodCheckedEventHandler.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.Designer.cs create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.cs create mode 100644 src/Services/Ordering/Ordering.Domain/Events/OrderCreated.cs create mode 100644 src/Services/Ordering/Ordering.Domain/Events/PaymentMethodChecked.cs create mode 100644 src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index c92dc8c7d..49fa8b892 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -41,6 +41,12 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "test\Services\FunctionalTests\FunctionalTests.csproj", "{621E7211-58D0-45FD-9600-1CB490BD930E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "test\Services\UnitTest\UnitTest.csproj", "{7796F5D8-31FC-45A4-B673-19DE5BA194CF}" + ProjectSection(ProjectDependencies) = postProject + {A579E108-5445-403D-A407-339AC4D1611B} = {A579E108-5445-403D-A407-339AC4D1611B} + {621E7211-58D0-45FD-9600-1CB490BD930E} = {621E7211-58D0-45FD-9600-1CB490BD930E} + {FEA0C318-FFED-4D39-8781-265718CA43DD} = {FEA0C318-FFED-4D39-8781-265718CA43DD} + {F16E3C6A-1C94-4EAB-BE91-099618060B68} = {F16E3C6A-1C94-4EAB-BE91-099618060B68} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity.API", "src\Services\Identity\Identity.API\Identity.API.csproj", "{A579E108-5445-403D-A407-339AC4D1611B}" EndProject diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs index 495d887ba..1485a536f 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommand.cs @@ -2,6 +2,7 @@ using MediatR; using System.Collections.Generic; using System.Runtime.Serialization; +using System.Collections; namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands { @@ -51,6 +52,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands [DataMember] public int CardTypeId { get; private set; } + [DataMember] + public int PaymentId { get; private set; } + + [DataMember] + public int BuyerId { get; private set; } + [DataMember] public IEnumerable OrderItems => _orderItems; @@ -66,7 +73,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands public CreateOrderCommand(string city, string street, string state, string country, string zipcode, string cardNumber, string cardHolderName, DateTime cardExpiration, - string cardSecurityNumber, int cardTypeId) : this() + string cardSecurityNumber, int cardTypeId, int paymentId, int buyerId) : this() { City = city; Street = street; @@ -75,12 +82,14 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands ZipCode = zipcode; CardNumber = cardNumber; CardHolderName = cardHolderName; + CardExpiration = cardExpiration; CardSecurityNumber = cardSecurityNumber; CardTypeId = cardTypeId; CardExpiration = cardExpiration; + PaymentId = paymentId; + BuyerId = buyerId; } - public class OrderItemDTO { public int ProductId { get; set; } diff --git a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs index bf8e90ea6..6e8ebb381 100644 --- a/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs +++ b/src/Services/Ordering/Ordering.API/Application/Commands/CreateOrderCommandHandler.cs @@ -1,6 +1,5 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands { - using Domain.AggregatesModel.BuyerAggregate; using Domain.AggregatesModel.OrderAggregate; using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; @@ -24,16 +23,16 @@ public class CreateOrderCommandHandler : IAsyncRequestHandler { - private readonly IBuyerRepository _buyerRepository; private readonly IOrderRepository _orderRepository; private readonly IIdentityService _identityService; + private readonly IMediator _mediator; // Using DI to inject infrastructure persistence Repositories - public CreateOrderCommandHandler(IBuyerRepository buyerRepository, IOrderRepository orderRepository, IIdentityService identityService) + public CreateOrderCommandHandler(IMediator mediator, IOrderRepository orderRepository, IIdentityService identityService) { - _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); } public async Task Handle(CreateOrderCommand message) @@ -42,45 +41,18 @@ // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root // methods and constructor so validations, invariants and business logic // make sure that consistency is preserved across the whole aggregate - - var cardTypeId = message.CardTypeId != 0 ? message.CardTypeId : 1; - - var buyerGuid = _identityService.GetUserIdentity(); - var buyer = await _buyerRepository.FindAsync(buyerGuid); - - if (buyer == null) - { - buyer = new Buyer(buyerGuid); - } - - var payment = buyer.AddPaymentMethod(cardTypeId, - $"Payment Method on {DateTime.UtcNow}", - message.CardNumber, - message.CardSecurityNumber, - message.CardHolderName, - message.CardExpiration); - - _buyerRepository.Add(buyer); - - await _buyerRepository.UnitOfWork - .SaveChangesAsync(); - - // Create the Order AggregateRoot - // DDD patterns comment: Add child entities and value-objects through the Order Aggregate-Root - // methods and constructor so validations, invariants and business logic - // make sure that consistency is preserved across the whole aggregate - - var order = new Order(buyer.Id, payment.Id, 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(address , message.CardTypeId, message.CardNumber, message.CardSecurityNumber, message.CardHolderName, message.CardExpiration); foreach (var item in message.OrderItems) { order.AddOrderItem(item.ProductId, item.ProductName, item.UnitPrice, item.Discount, item.PictureUrl, item.Units); } - _orderRepository.Add(order); + _orderRepository.Add(order); var result = await _orderRepository.UnitOfWork - .SaveChangesAsync(); + .SaveEntitiesAsync(); return result > 0; diff --git a/src/Services/Ordering/Ordering.API/Application/EventHandlers/OrderCreatedEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/EventHandlers/OrderCreatedEventHandler.cs new file mode 100644 index 000000000..422a61c9b --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/EventHandlers/OrderCreatedEventHandler.cs @@ -0,0 +1,53 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Services; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +using Microsoft.Extensions.Logging; +using Ordering.Domain.Events; +using System; +using System.Threading.Tasks; + +namespace Ordering.API.Application.EventHandlers +{ + public class OrderCreatedEventHandler : IAsyncNotificationHandler + { + private readonly ILoggerFactory _logger; + private readonly IBuyerRepository _buyerRepository; + private readonly IIdentityService _identityService; + + public OrderCreatedEventHandler(ILoggerFactory logger, IBuyerRepository buyerRepository, IIdentityService identityService) + { + _buyerRepository = buyerRepository ?? throw new ArgumentNullException(nameof(buyerRepository)); + _identityService = identityService ?? throw new ArgumentNullException(nameof(identityService)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public async Task Handle(OrderCreated orderNotification) + { + var cardTypeId = orderNotification.CardTypeId != 0 ? orderNotification.CardTypeId : 1; + + var buyerGuid = _identityService.GetUserIdentity(); + var buyer = await _buyerRepository.FindAsync(buyerGuid); + + if (buyer == null) + { + buyer = new Buyer(buyerGuid); + } + + var payment = buyer.AddPaymentMethod(cardTypeId, + $"Payment Method on {DateTime.UtcNow}", + orderNotification.CardNumber, + orderNotification.CardSecurityNumber, + orderNotification.CardHolderName, + orderNotification.CardExpiration, + orderNotification.Order.Id); + + _buyerRepository.Add(buyer); + + await _buyerRepository.UnitOfWork + .SaveEntitiesAsync(); + + _logger.CreateLogger(nameof(OrderCreatedEventHandler)).LogTrace($"A new payment method has been successfully added for orderId: {orderNotification.Order.Id}."); + + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Application/EventHandlers/PaymentMethodCheckedEventHandler.cs b/src/Services/Ordering/Ordering.API/Application/EventHandlers/PaymentMethodCheckedEventHandler.cs new file mode 100644 index 000000000..b72b89363 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Application/EventHandlers/PaymentMethodCheckedEventHandler.cs @@ -0,0 +1,33 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.Extensions.Logging; +using Ordering.Domain.Events; +using System; +using System.Threading.Tasks; + +namespace Ordering.API.Application.EventHandlers +{ + public class PaymentMethodCheckedEventHandler : IAsyncNotificationHandler + { + private readonly IOrderRepository _orderRepository; + private readonly ILoggerFactory _logger; + public PaymentMethodCheckedEventHandler(IOrderRepository orderRepository, ILoggerFactory logger) + { + _orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public async Task Handle(PaymentMethodChecked paymentMethodNotification) + { + var orderToUpdate = await _orderRepository.GetAsync(paymentMethodNotification.OrderId); + orderToUpdate.SetBuyerId(paymentMethodNotification.Buyer.Id); + orderToUpdate.SetPaymentId(paymentMethodNotification.Payment.Id); + + await _orderRepository.UnitOfWork + .SaveEntitiesAsync(); + + _logger.CreateLogger(nameof(PaymentMethodCheckedEventHandler)) + .LogTrace($"Order with Id: {paymentMethodNotification.OrderId} has been successfully updated with a new payment method id: { paymentMethodNotification.Payment.Id }"); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs index d01150b9c..05f27eeec 100644 --- a/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs +++ b/src/Services/Ordering/Ordering.API/Controllers/OrdersController.cs @@ -1,5 +1,4 @@ using MediatR; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Queries; @@ -28,19 +27,19 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers [Route("new")] [HttpPost] - public async Task CreateOrder([FromBody]CreateOrderCommand createOrderCommand, [FromHeader(Name = "x-requestid")] string requestId) + public async Task CreateOrder([FromBody]CreateOrderCommand command, [FromHeader(Name = "x-requestid")] string requestId) { bool result = false; if (Guid.TryParse(requestId, out Guid guid) && guid != Guid.Empty) { - var requestCreateOrder = new IdentifiedCommand(createOrderCommand, guid); + var requestCreateOrder = new IdentifiedCommand(command, guid); result = await _mediator.SendAsync(requestCreateOrder); } else { // If no x-requestid header is found we process the order anyway. This is just temporary to not break existing clients // that aren't still updated. When all clients were updated this could be removed. - result = await _mediator.SendAsync(createOrderCommand); + result = await _mediator.SendAsync(command); } if (result) @@ -82,7 +81,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers var cardTypes = await _orderQueries.GetCardTypes(); return Ok(cardTypes); - } + } } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs index e5754eeca..8585b8aa9 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/AutofacModules/MediatorModule.cs @@ -3,6 +3,8 @@ using Autofac.Core; using MediatR; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Commands; using Microsoft.eShopOnContainers.Services.Ordering.API.Application.Decorators; +using Ordering.API.Application.EventHandlers; +using Ordering.Domain.Events; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -14,12 +16,17 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof protected override void Load(ContainerBuilder builder) { builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly) - .AsImplementedInterfaces(); + .AsImplementedInterfaces(); builder.RegisterAssemblyTypes(typeof(CreateOrderCommand).GetTypeInfo().Assembly) .As(o => o.GetInterfaces() .Where(i => i.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>))) .Select(i => new KeyedService("IAsyncRequestHandler", i))); + + builder + .RegisterAssemblyTypes(typeof(OrderCreatedEventHandler).GetTypeInfo().Assembly) + .Where(t => t.IsClosedTypeOf(typeof(IAsyncNotificationHandler<>))) + .AsImplementedInterfaces(); builder.Register(context => { @@ -37,7 +44,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof builder.RegisterGenericDecorator(typeof(LogDecorator<,>), typeof(IAsyncRequestHandler<,>), - "IAsyncRequestHandler"); + "IAsyncRequestHandler"); } } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.Designer.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.Designer.cs new file mode 100644 index 000000000..e06efc622 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.Designer.cs @@ -0,0 +1,244 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; + +namespace Ordering.API.Migrations +{ + [DbContext(typeof(OrderingContext))] + [Migration("20170313100034_Domain_events")] + partial class Domain_events + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752") + .HasAnnotation("SqlServer:Sequence:.orderitemseq", "'orderitemseq', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:ordering.buyerseq", "'buyerseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:ordering.orderseq", "'orderseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:ordering.paymentseq", "'paymentseq', 'ordering', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "buyerseq") + .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("IdentityGuid") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.HasIndex("IdentityGuid") + .IsUnique(); + + b.ToTable("buyers","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", b => + { + b.Property("Id") + .HasDefaultValue(1); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("cardtypes","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "paymentseq") + .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Alias") + .IsRequired() + .HasMaxLength(200); + + b.Property("BuyerId"); + + b.Property("CardHolderName") + .IsRequired() + .HasMaxLength(200); + + b.Property("CardNumber") + .IsRequired() + .HasMaxLength(25); + + b.Property("CardTypeId"); + + b.Property("Expiration"); + + b.HasKey("Id"); + + b.HasIndex("BuyerId"); + + b.HasIndex("CardTypeId"); + + b.ToTable("paymentmethods","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("City"); + + b.Property("Country"); + + b.Property("State"); + + b.Property("Street"); + + b.Property("ZipCode"); + + b.HasKey("Id"); + + b.ToTable("address","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "orderseq") + .HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("AddressId"); + + b.Property("BuyerId"); + + b.Property("OrderDate"); + + b.Property("OrderStatusId"); + + b.Property("PaymentMethodId"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("BuyerId"); + + b.HasIndex("OrderStatusId"); + + b.HasIndex("PaymentMethodId"); + + b.ToTable("orders","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "orderitemseq") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Discount"); + + b.Property("OrderId"); + + b.Property("PictureUrl"); + + b.Property("ProductId"); + + b.Property("ProductName") + .IsRequired(); + + b.Property("UnitPrice"); + + b.Property("Units"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("orderItems","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", b => + { + b.Property("Id") + .HasDefaultValue(1); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("orderstatus","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.ClientRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired(); + + b.Property("Time"); + + b.HasKey("Id"); + + b.ToTable("requests","ordering"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer") + .WithMany("PaymentMethods") + .HasForeignKey("BuyerId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.CardType", "CardType") + .WithMany() + .HasForeignKey("CardTypeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Address", "Address") + .WithMany() + .HasForeignKey("AddressId"); + + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer") + .WithMany() + .HasForeignKey("BuyerId"); + + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", "OrderStatus") + .WithMany() + .HasForeignKey("OrderStatusId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", "PaymentMethod") + .WithMany() + .HasForeignKey("PaymentMethodId"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderItem", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order") + .WithMany("OrderItems") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.cs new file mode 100644 index 000000000..d68cedd80 --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/20170313100034_Domain_events.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ordering.API.Migrations +{ + public partial class Domain_events : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_orders_buyers_BuyerId", + schema: "ordering", + table: "orders"); + + migrationBuilder.AlterColumn( + name: "PaymentMethodId", + schema: "ordering", + table: "orders", + nullable: true, + oldClrType: typeof(int)); + + migrationBuilder.AlterColumn( + name: "BuyerId", + schema: "ordering", + table: "orders", + nullable: true, + oldClrType: typeof(int)); + + migrationBuilder.AddForeignKey( + name: "FK_orders_buyers_BuyerId", + schema: "ordering", + table: "orders", + column: "BuyerId", + principalSchema: "ordering", + principalTable: "buyers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_orders_buyers_BuyerId", + schema: "ordering", + table: "orders"); + + migrationBuilder.AlterColumn( + name: "PaymentMethodId", + schema: "ordering", + table: "orders", + nullable: false, + oldClrType: typeof(int), + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "BuyerId", + schema: "ordering", + table: "orders", + nullable: false, + oldClrType: typeof(int), + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_orders_buyers_BuyerId", + schema: "ordering", + table: "orders", + column: "BuyerId", + principalSchema: "ordering", + principalTable: "buyers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs index 654f93d6d..7af892470 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/Migrations/OrderingContextModelSnapshot.cs @@ -119,13 +119,13 @@ namespace Ordering.API.Migrations b.Property("AddressId"); - b.Property("BuyerId"); + b.Property("BuyerId"); b.Property("OrderDate"); b.Property("OrderStatusId"); - b.Property("PaymentMethodId"); + b.Property("PaymentMethodId"); b.HasKey("Id"); @@ -183,7 +183,7 @@ namespace Ordering.API.Migrations b.ToTable("orderstatus","ordering"); }); - modelBuilder.Entity("Ordering.Infrastructure.ClientRequest", b => + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.ClientRequest", b => { b.Property("Id") .ValueGeneratedOnAdd(); @@ -219,8 +219,7 @@ namespace Ordering.API.Migrations b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.Buyer", "Buyer") .WithMany() - .HasForeignKey("BuyerId") - .OnDelete(DeleteBehavior.Cascade); + .HasForeignKey("BuyerId"); b.HasOne("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", "OrderStatus") .WithMany() diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs index 6bd6853b7..64acbf7b7 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/Buyer.cs @@ -1,6 +1,6 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Ordering.Domain.Events; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; @@ -11,7 +11,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B { public string IdentityGuid { get; private set; } - private List _paymentMethods; + private List _paymentMethods; public IEnumerable PaymentMethods => _paymentMethods.AsReadOnly(); @@ -22,16 +22,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B public Buyer(string identity) : this() { IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity)); - } - public PaymentMethod AddPaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) + public PaymentMethod AddPaymentMethod( + int cardTypeId, string alias, string cardNumber, + string securityNumber, string cardHolderName, DateTime expiration, int orderId) { var existingPayment = _paymentMethods.Where(p => p.IsEqualTo(cardTypeId, cardNumber, expiration)) .SingleOrDefault(); if (existingPayment != null) { + AddEvent(new PaymentMethodChecked(this, existingPayment, orderId)); return existingPayment; } else @@ -39,9 +41,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.B var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); _paymentMethods.Add(payment); - + AddEvent(new PaymentMethodChecked(this, payment, orderId)); return payment; } - } + } } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs index 81869a457..dbef153db 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/IOrderRepository.cs @@ -1,4 +1,5 @@ using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate { @@ -8,5 +9,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O public interface IOrderRepository : IRepository where T : IAggregateRoot { Order Add(Order order); + + Task GetAsync(int orderId); + + void Update(Order order); } } diff --git a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs index fecc53d44..a1bb76cee 100644 --- a/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs +++ b/src/Services/Ordering/Ordering.Domain/AggregatesModel/OrderAggregate/Order.cs @@ -1,5 +1,7 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Ordering.Domain.Events; using System; using System.Collections.Generic; using System.Linq; @@ -17,7 +19,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O public Address Address { get; private set; } public Buyer Buyer { get; private set; } - private int _buyerId; + private int? _buyerId; public OrderStatus OrderStatus { get; private set; } private int _orderStatusId; @@ -36,11 +38,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx public PaymentMethod PaymentMethod { get; private set; } - private int _paymentMethodId; + private int? _paymentMethodId; protected Order() { } - public Order(int buyerId, int paymentMethodId, Address address) + public Order(Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, + string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) { _orderItems = new List(); _buyerId = buyerId; @@ -48,6 +51,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O _orderStatusId = OrderStatus.InProcess.Id; _orderDate = DateTime.UtcNow; Address = address; + AddCreatedOrderEvent(cardTypeId, cardNumber, + cardSecurityNumber, cardHolderName, cardExpiration); } // DDD Patterns comment @@ -74,9 +79,28 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.O //add validated new order item var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); - _orderItems.Add(orderItem); } } + + public void SetPaymentId(int id) + { + _paymentMethodId = id; + } + + public void SetBuyerId(int id) + { + _buyerId = id; + } + + private void AddCreatedOrderEvent(int cardTypeId, string cardNumber, + string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) + { + var @orderCreatedEvent = new OrderCreated( + this, cardTypeId, cardNumber, cardSecurityNumber, + cardHolderName, cardExpiration); + + AddEvent(@orderCreatedEvent); + } } } diff --git a/src/Services/Ordering/Ordering.Domain/Events/OrderCreated.cs b/src/Services/Ordering/Ordering.Domain/Events/OrderCreated.cs new file mode 100644 index 000000000..489cc23d0 --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/Events/OrderCreated.cs @@ -0,0 +1,35 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ordering.Domain.Events +{ + /// + /// Event used when an order is created + /// + public class OrderCreated + : IAsyncNotification + { + public int CardTypeId { get; private set; } + public string CardNumber { get; private set; } + public string CardSecurityNumber { get; private set; } + public string CardHolderName { get; private set; } + public DateTime CardExpiration { get; private set; } + public Order Order { get; private set; } + + public OrderCreated(Order order, + int cardTypeId, string cardNumber, + string cardSecurityNumber, string cardHolderName, + DateTime cardExpiration) + { + Order = order; + CardTypeId = cardTypeId; + CardNumber = cardNumber; + CardSecurityNumber = cardSecurityNumber; + CardHolderName = cardHolderName; + CardExpiration = cardExpiration; + } + } +} diff --git a/src/Services/Ordering/Ordering.Domain/Events/PaymentMethodChecked.cs b/src/Services/Ordering/Ordering.Domain/Events/PaymentMethodChecked.cs new file mode 100644 index 000000000..25c8d8111 --- /dev/null +++ b/src/Services/Ordering/Ordering.Domain/Events/PaymentMethodChecked.cs @@ -0,0 +1,23 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ordering.Domain.Events +{ + public class PaymentMethodChecked + : IAsyncNotification + { + public Buyer Buyer { get; private set; } + public PaymentMethod Payment { get; private set; } + public int OrderId { get; private set; } + + public PaymentMethodChecked(Buyer buyer, PaymentMethod payment, int orderId) + { + Buyer = buyer; + Payment = payment; + OrderId = orderId; + } + } +} diff --git a/src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj b/src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj index b96e1884a..ac887b0d4 100644 --- a/src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj +++ b/src/Services/Ordering/Ordering.Domain/Ordering.Domain.csproj @@ -11,4 +11,9 @@ false + + + + + diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs index 2530d8403..e2c602300 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/Entity.cs @@ -1,7 +1,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork { using System; - + using MediatR; + using System.Collections.Generic; public abstract class Entity { @@ -9,6 +10,8 @@ int? _requestedHashCode; int _Id; + private List _events; + public virtual int Id { get @@ -21,6 +24,19 @@ } } + public List Events => _events; + public void AddEvent(IAsyncNotification eventItem) + { + _events = _events ?? new List(); + _events.Add(eventItem); + } + + public void RemoveEvent(IAsyncNotification eventItem) + { + if (_events is null) return; + _events.Remove(eventItem); + } + public bool IsTransient() { return this.Id == default(Int32); diff --git a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs index 85a8efbb0..581f905a6 100644 --- a/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs +++ b/src/Services/Ordering/Ordering.Domain/SeedWork/IUnitOfWork.cs @@ -5,7 +5,8 @@ using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork { public interface IUnitOfWork : IDisposable - { + { Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)); + Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs new file mode 100644 index 000000000..5949f00b4 --- /dev/null +++ b/src/Services/Ordering/Ordering.Infrastructure/MediatorExtension.cs @@ -0,0 +1,25 @@ +using MediatR; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Microsoft.eShopOnContainers.Services.Ordering.Infrastructure; +using System.Linq; +using System.Threading.Tasks; + +namespace Ordering.Infrastructure +{ + public static class MediatorExtension + { + public static async Task RaiseDomainEventsAsync(this IMediator mediator, OrderingContext ctx) + { + var domainEntities = ctx.ChangeTracker.Entries().Where(x => x.Entity.Events != null && x.Entity.Events.Any()); + var domainEvents = domainEntities.SelectMany(x => x.Entity.Events).ToList(); + domainEntities.ToList().ForEach(entity => entity.Entity.Events.Clear()); + + var tasks = domainEvents + .Select(async (domainEvent) => { + await mediator.PublishAsync(domainEvent); + }); + + await Task.WhenAll(tasks); + } + } +} diff --git a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs index 7a9d192e4..504ed0ab7 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/OrderingContext.cs @@ -1,10 +1,14 @@ -using Microsoft.EntityFrameworkCore; +using MediatR; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; +using Ordering.Infrastructure; using System; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure { @@ -26,7 +30,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure public DbSet OrderStatus { get; set; } - public OrderingContext(DbContextOptions options) : base(options) { } + private readonly IMediator _mediator; + + public OrderingContext(DbContextOptions options, IMediator mediator) : base(options) + { + _mediator = mediator; + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -38,7 +47,7 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure modelBuilder.Entity(ConfigureOrderItems); modelBuilder.Entity(ConfigureCardTypes); modelBuilder.Entity(ConfigureOrderStatus); - modelBuilder.Entity(ConfigureBuyer); + modelBuilder.Entity(ConfigureBuyer); } private void ConfigureRequests(EntityTypeBuilder requestConfiguration) @@ -65,6 +74,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure buyerConfiguration.HasKey(b => b.Id); + buyerConfiguration.Ignore(b => b.Events); + buyerConfiguration.Property(b => b.Id) .ForSqlServerUseSequenceHiLo("buyerseq", DEFAULT_SCHEMA); @@ -91,6 +102,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure paymentConfiguration.HasKey(b => b.Id); + paymentConfiguration.Ignore(b => b.Events); + paymentConfiguration.Property(b => b.Id) .ForSqlServerUseSequenceHiLo("paymentseq", DEFAULT_SCHEMA); @@ -126,13 +139,15 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure orderConfiguration.HasKey(o => o.Id); + orderConfiguration.Ignore(b => b.Events); + orderConfiguration.Property(o => o.Id) .ForSqlServerUseSequenceHiLo("orderseq", DEFAULT_SCHEMA); orderConfiguration.Property("OrderDate").IsRequired(); - orderConfiguration.Property("BuyerId").IsRequired(); + orderConfiguration.Property("BuyerId").IsRequired(false); orderConfiguration.Property("OrderStatusId").IsRequired(); - orderConfiguration.Property("PaymentMethodId").IsRequired(); + orderConfiguration.Property("PaymentMethodId").IsRequired(false); var navigation = orderConfiguration.Metadata.FindNavigation(nameof(Order.OrderItems)); // DDD Patterns comment: @@ -142,10 +157,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure orderConfiguration.HasOne(o => o.PaymentMethod) .WithMany() .HasForeignKey("PaymentMethodId") + .IsRequired(false) .OnDelete(DeleteBehavior.Restrict); orderConfiguration.HasOne(o => o.Buyer) .WithMany() + .IsRequired(false) .HasForeignKey("BuyerId"); orderConfiguration.HasOne(o => o.OrderStatus) @@ -159,6 +176,8 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure orderItemConfiguration.HasKey(o => o.Id); + orderItemConfiguration.Ignore(b => b.Events); + orderItemConfiguration.Property(o => o.Id) .ForSqlServerUseSequenceHiLo("orderitemseq"); @@ -215,5 +234,12 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure .HasMaxLength(200) .IsRequired(); } + + public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + var result = await base.SaveChangesAsync(); + await _mediator.RaiseDomainEventsAsync(this); + return result; + } } } diff --git a/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs b/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs index ecdc705dd..fec918003 100644 --- a/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs +++ b/src/Services/Ordering/Ordering.Infrastructure/Repositories/OrderRepository.cs @@ -1,6 +1,8 @@ -using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; +using Microsoft.EntityFrameworkCore; +using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; using System; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositories { @@ -24,8 +26,18 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.Repositor public Order Add(Order order) { - return _context.Orders.Add(order) - .Entity; + return _context.Orders.Add(order).Entity; + + } + + public async Task GetAsync(int orderId) + { + return await _context.Orders.FindAsync(orderId); + } + + public void Update(Order order) + { + _context.Entry(order).State = EntityState.Modified; } } } diff --git a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs index a34e0d7c0..46b5cb2b0 100644 --- a/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs +++ b/test/Services/FunctionalTests/Services/Ordering/OrderingScenarios.cs @@ -75,7 +75,9 @@ country: "USA", state: "WA", street: "One way", - zipcode: "zipcode" + zipcode: "zipcode", + paymentId: 1, + buyerId: 3 ); order.AddOrderItem(new OrderItemDTO() @@ -101,7 +103,9 @@ country: "USA", state: "WA", street: "One way", - zipcode: "zipcode" + zipcode: "zipcode", + paymentId: 1, + buyerId: 3 ); return JsonConvert.SerializeObject(order); diff --git a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs index a8474427b..63e2e01e1 100644 --- a/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs +++ b/test/Services/UnitTest/Ordering/Application/IdentifierCommandHandlerTest.cs @@ -79,7 +79,9 @@ namespace UnitTest.Ordering.Application cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", - cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); + cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0, + paymentId: args != null && args.ContainsKey("paymentId") ? (int)args["paymentId"] : 0, + buyerId: args != null && args.ContainsKey("buyerId") ? (int)args["buyerId"] : 0); } } } diff --git a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs index a048dc975..fe39ffb6c 100644 --- a/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs +++ b/test/Services/UnitTest/Ordering/Application/NewOrderCommandHandlerTest.cs @@ -10,21 +10,22 @@ using System.Threading.Tasks; namespace UnitTest.Ordering.Application { + using MediatR; using System.Collections; using System.Collections.Generic; using Xunit; public class NewOrderRequestHandlerTest { - private readonly Mock> _buyerRepositoryMock; private readonly Mock> _orderRepositoryMock; private readonly Mock _identityServiceMock; + private readonly Mock _mediator; public NewOrderRequestHandlerTest() { - _buyerRepositoryMock = new Mock>(); _orderRepositoryMock = new Mock>(); _identityServiceMock = new Mock(); + _mediator = new Mock(); } [Fact] @@ -37,21 +38,15 @@ namespace UnitTest.Ordering.Application { ["cardExpiration"] = DateTime.Now.AddYears(1) }); // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())) - .Returns(FakeOrder()); - - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); var result = await handler.Handle(fakeOrderCmd); //Assert @@ -66,19 +61,16 @@ namespace UnitTest.Ordering.Application var fakeOrderCmd = FakeOrderRequestWithBuyer(new Dictionary { ["cardExpiration"] = DateTime.Now.AddYears(1) }); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())).Returns(FakeOrder()); - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(0)); _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); var result = await handler.Handle(fakeOrderCmd); //Assert @@ -95,21 +87,15 @@ namespace UnitTest.Ordering.Application { ["cardExpiration"] = DateTime.Now.AddYears(-1) }); // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())) - .Returns(FakeOrder()); - - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); //Assert await Assert.ThrowsAsync(async () => await handler.Handle(fakeOrderCmd)); @@ -128,21 +114,15 @@ namespace UnitTest.Ordering.Application }); // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); - - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())) - .Returns(FakeOrder()); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); //Assert await Assert.ThrowsAsync(async () => await handler.Handle(fakeOrderCmd)); @@ -161,21 +141,15 @@ namespace UnitTest.Ordering.Application }); // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())) - .Returns(FakeOrder()); - - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); //Assert await Assert.ThrowsAsync(async () => await handler.Handle(fakeOrderCmd)); @@ -185,7 +159,7 @@ namespace UnitTest.Ordering.Application public async Task Handle_throws_exception_when_no_cardNumber() { - var buyerId = "1234"; + var orderId = "1234"; var fakeOrderCmd = FakeOrderRequestWithBuyer(new Dictionary { @@ -194,21 +168,15 @@ namespace UnitTest.Ordering.Application }); // Arrange - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId)) - .Returns(Task.FromResult(FakeBuyer())); + _orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny())) + .Returns(Task.FromResult(FakeOrder())); - _buyerRepositoryMock.Setup(buyerRepo => buyerRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) + _orderRepositoryMock.Setup(orderRepo => orderRepo.UnitOfWork.SaveChangesAsync(default(CancellationToken))) .Returns(Task.FromResult(1)); - _orderRepositoryMock.Setup(or => or.Add(FakeOrder())) - .Returns(FakeOrder()); - - _orderRepositoryMock.Setup(or => or.UnitOfWork.SaveChangesAsync(default(CancellationToken))) - .Returns(Task.FromResult(1)); - - _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(buyerId); + _identityServiceMock.Setup(svc => svc.GetUserIdentity()).Returns(orderId); //Act - var handler = new CreateOrderCommandHandler(_buyerRepositoryMock.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); + var handler = new CreateOrderCommandHandler(_mediator.Object, _orderRepositoryMock.Object, _identityServiceMock.Object); //Assert await Assert.ThrowsAsync(async () => await handler.Handle(fakeOrderCmd)); @@ -228,7 +196,7 @@ namespace UnitTest.Ordering.Application private Order FakeOrder() { - return new Order(1, 1, new Address("street", "city", "state", "country", "zipcode")); + return new Order(new Address("street", "city", "state", "country", "zipcode"), 1, "12", "111", "fakeName", DateTime.Now.AddYears(1)); } private CreateOrderCommand FakeOrderRequestWithBuyer(Dictionary args = null) @@ -243,7 +211,9 @@ namespace UnitTest.Ordering.Application cardExpiration: args != null && args.ContainsKey("cardExpiration") ? (DateTime)args["cardExpiration"] : DateTime.MinValue, cardSecurityNumber: args != null && args.ContainsKey("cardSecurityNumber") ? (string)args["cardSecurityNumber"] : "123", cardHolderName: args != null && args.ContainsKey("cardHolderName") ? (string)args["cardHolderName"] : "XXX", - cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0); + cardTypeId: args != null && args.ContainsKey("cardTypeId") ? (int)args["cardTypeId"] : 0, + paymentId: args != null && args.ContainsKey("paymentId") ? (int)args["paymentId"] : 0, + buyerId: args != null && args.ContainsKey("buyerId") ? (int)args["buyerId"] : 0); } } } diff --git a/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs b/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs index 861ff378a..1ffb8f6ca 100644 --- a/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs +++ b/test/Services/UnitTest/Ordering/Domain/BuyerAggregateTest.cs @@ -40,11 +40,12 @@ public class BuyerAggregateTest var securityNumber = "1234"; var cardHolderName = "FakeHolderNAme"; var expiration = DateTime.Now.AddYears(1); + var orderId = 1; var identity = new Guid().ToString(); var fakeBuyerItem = new Buyer(identity); //Act - var result = fakeBuyerItem.AddPaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration); + var result = fakeBuyerItem.AddPaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration, orderId); //Assert Assert.NotNull(result);