Added domain events in Ordering Api

This commit is contained in:
Ramón Tomás 2017-03-14 18:02:28 +01:00
parent c38e078265
commit c8f0776f1f
25 changed files with 685 additions and 137 deletions

@ -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

@ -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<OrderItemDTO> 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; }

@ -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<CreateOrderCommand, bool>
{
private readonly IBuyerRepository<Buyer> _buyerRepository;
private readonly IOrderRepository<Order> _orderRepository;
private readonly IIdentityService _identityService;
private readonly IMediator _mediator;
// Using DI to inject infrastructure persistence Repositories
public CreateOrderCommandHandler(IBuyerRepository<Buyer> buyerRepository, IOrderRepository<Order> orderRepository, IIdentityService identityService)
public CreateOrderCommandHandler(IMediator mediator, IOrderRepository<Order> 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<bool> Handle(CreateOrderCommand message)
@ -42,35 +41,8 @@
// 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)
{
@ -80,7 +52,7 @@
_orderRepository.Add(order);
var result = await _orderRepository.UnitOfWork
.SaveChangesAsync();
.SaveEntitiesAsync();
return result > 0;

@ -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<OrderCreated>
{
private readonly ILoggerFactory _logger;
private readonly IBuyerRepository<Buyer> _buyerRepository;
private readonly IIdentityService _identityService;
public OrderCreatedEventHandler(ILoggerFactory logger, IBuyerRepository<Buyer> 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}.");
}
}
}

@ -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<PaymentMethodChecked>
{
private readonly IOrderRepository<Order> _orderRepository;
private readonly ILoggerFactory _logger;
public PaymentMethodCheckedEventHandler(IOrderRepository<Order> 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 }");
}
}
}

@ -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<IActionResult> CreateOrder([FromBody]CreateOrderCommand createOrderCommand, [FromHeader(Name = "x-requestid")] string requestId)
public async Task<IActionResult> 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, bool>(createOrderCommand, guid);
var requestCreateOrder = new IdentifiedCommand<CreateOrderCommand, bool>(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)

@ -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;
@ -21,6 +23,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API.Infrastructure.Autof
.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<SingleInstanceFactory>(context =>
{
var componentContext = context.Resolve<IComponentContext>();

@ -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<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "buyerseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("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<int>("Id")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.ToTable("cardtypes","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate.PaymentMethod", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "paymentseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<string>("Alias")
.IsRequired()
.HasMaxLength(200);
b.Property<int>("BuyerId");
b.Property<string>("CardHolderName")
.IsRequired()
.HasMaxLength(200);
b.Property<string>("CardNumber")
.IsRequired()
.HasMaxLength(25);
b.Property<int>("CardTypeId");
b.Property<DateTime>("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<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("City");
b.Property<string>("Country");
b.Property<string>("State");
b.Property<string>("Street");
b.Property<string>("ZipCode");
b.HasKey("Id");
b.ToTable("address","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.Order", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "orderseq")
.HasAnnotation("SqlServer:HiLoSequenceSchema", "ordering")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<int?>("AddressId");
b.Property<int?>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("OrderStatusId");
b.Property<int?>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:HiLoSequenceName", "orderitemseq")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo);
b.Property<decimal>("Discount");
b.Property<int>("OrderId");
b.Property<string>("PictureUrl");
b.Property<int>("ProductId");
b.Property<string>("ProductName")
.IsRequired();
b.Property<decimal>("UnitPrice");
b.Property<int>("Units");
b.HasKey("Id");
b.HasIndex("OrderId");
b.ToTable("orderItems","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate.OrderStatus", b =>
{
b.Property<int>("Id")
.HasDefaultValue(1);
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.HasKey("Id");
b.ToTable("orderstatus","ordering");
});
modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.ClientRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired();
b.Property<DateTime>("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);
});
}
}
}

@ -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<int>(
name: "PaymentMethodId",
schema: "ordering",
table: "orders",
nullable: true,
oldClrType: typeof(int));
migrationBuilder.AlterColumn<int>(
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<int>(
name: "PaymentMethodId",
schema: "ordering",
table: "orders",
nullable: false,
oldClrType: typeof(int),
oldNullable: true);
migrationBuilder.AlterColumn<int>(
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);
}
}
}

@ -119,13 +119,13 @@ namespace Ordering.API.Migrations
b.Property<int?>("AddressId");
b.Property<int>("BuyerId");
b.Property<int?>("BuyerId");
b.Property<DateTime>("OrderDate");
b.Property<int>("OrderStatusId");
b.Property<int>("PaymentMethodId");
b.Property<int?>("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<Guid>("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()

@ -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;
@ -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,7 +41,7 @@ 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;
}
}

@ -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<T> : IRepository<T> where T : IAggregateRoot
{
Order Add(Order order);
Task<Order> GetAsync(int orderId);
void Update(Order order);
}
}

@ -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<OrderItem>();
_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);
}
}
}

@ -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
{
/// <summary>
/// Event used when an order is created
/// </summary>
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;
}
}
}

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

@ -11,4 +11,9 @@
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="2.1.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="1.1.0" />
</ItemGroup>
</Project>

@ -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<IAsyncNotification> _events;
public virtual int Id
{
get
@ -21,6 +24,19 @@
}
}
public List<IAsyncNotification> Events => _events;
public void AddEvent(IAsyncNotification eventItem)
{
_events = _events ?? new List<IAsyncNotification>();
_events.Add(eventItem);
}
public void RemoveEvent(IAsyncNotification eventItem)
{
if (_events is null) return;
_events.Remove(eventItem);
}
public bool IsTransient()
{
return this.Id == default(Int32);

@ -7,5 +7,6 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork
public interface IUnitOfWork : IDisposable
{
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
Task<int> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken));
}
}

@ -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<Entity>().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);
}
}
}

@ -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> 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)
{
@ -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<DateTime>("OrderDate").IsRequired();
orderConfiguration.Property<int>("BuyerId").IsRequired();
orderConfiguration.Property<int?>("BuyerId").IsRequired(false);
orderConfiguration.Property<int>("OrderStatusId").IsRequired();
orderConfiguration.Property<int>("PaymentMethodId").IsRequired();
orderConfiguration.Property<int?>("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<int> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
var result = await base.SaveChangesAsync();
await _mediator.RaiseDomainEventsAsync(this);
return result;
}
}
}

@ -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<Order> GetAsync(int orderId)
{
return await _context.Orders.FindAsync(orderId);
}
public void Update(Order order)
{
_context.Entry(order).State = EntityState.Modified;
}
}
}

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

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

@ -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<IBuyerRepository<Buyer>> _buyerRepositoryMock;
private readonly Mock<IOrderRepository<Order>> _orderRepositoryMock;
private readonly Mock<IIdentityService> _identityServiceMock;
private readonly Mock<IMediator> _mediator;
public NewOrderRequestHandlerTest()
{
_buyerRepositoryMock = new Mock<IBuyerRepository<Buyer>>();
_orderRepositoryMock = new Mock<IOrderRepository<Order>>();
_identityServiceMock = new Mock<IIdentityService>();
_mediator = new Mock<IMediator>();
}
[Fact]
@ -37,21 +38,15 @@ namespace UnitTest.Ordering.Application
{ ["cardExpiration"] = DateTime.Now.AddYears(1) });
// Arrange
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<string, object>
{ ["cardExpiration"] = DateTime.Now.AddYears(1) });
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<ArgumentException>(async () => await handler.Handle(fakeOrderCmd));
@ -128,21 +114,15 @@ namespace UnitTest.Ordering.Application
});
// Arrange
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<ArgumentException>(async () => await handler.Handle(fakeOrderCmd));
@ -161,21 +141,15 @@ namespace UnitTest.Ordering.Application
});
// Arrange
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<ArgumentException>(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<string, object>
{
@ -194,21 +168,15 @@ namespace UnitTest.Ordering.Application
});
// Arrange
_buyerRepositoryMock.Setup(buyerRepo => buyerRepo.FindAsync(buyerId))
.Returns(Task.FromResult<Buyer>(FakeBuyer()));
_orderRepositoryMock.Setup(orderRepo => orderRepo.GetAsync(It.IsAny<int>()))
.Returns(Task.FromResult<Order>(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<ArgumentException>(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<string, object> 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);
}
}
}

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