Moved namespaces to ordering.domain project
This commit is contained in:
parent
46dad7ac75
commit
4758530f1f
@ -1,55 +1,48 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using Ordering.Domain.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate
|
||||
public class Buyer
|
||||
: Entity, IAggregateRoot
|
||||
{
|
||||
public class Buyer
|
||||
: Entity, IAggregateRoot
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
private List<PaymentMethod> _paymentMethods;
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
|
||||
protected Buyer()
|
||||
{
|
||||
public string IdentityGuid { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
_paymentMethods = new List<PaymentMethod>();
|
||||
}
|
||||
|
||||
private List<PaymentMethod> _paymentMethods;
|
||||
public Buyer(string identity, string name) : this()
|
||||
{
|
||||
IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity));
|
||||
Name = !string.IsNullOrWhiteSpace(name) ? name : throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly();
|
||||
public PaymentMethod VerifyOrAddPaymentMethod(
|
||||
int cardTypeId, string alias, string cardNumber,
|
||||
string securityNumber, string cardHolderName, DateTime expiration, int orderId)
|
||||
{
|
||||
var existingPayment = _paymentMethods
|
||||
.SingleOrDefault(p => p.IsEqualTo(cardTypeId, cardNumber, expiration));
|
||||
|
||||
protected Buyer()
|
||||
if (existingPayment != null)
|
||||
{
|
||||
AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, existingPayment, orderId));
|
||||
|
||||
_paymentMethods = new List<PaymentMethod>();
|
||||
return existingPayment;
|
||||
}
|
||||
|
||||
public Buyer(string identity, string name) : this()
|
||||
{
|
||||
IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity));
|
||||
Name = !string.IsNullOrWhiteSpace(name) ? name : throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration);
|
||||
|
||||
public PaymentMethod VerifyOrAddPaymentMethod(
|
||||
int cardTypeId, string alias, string cardNumber,
|
||||
string securityNumber, string cardHolderName, DateTime expiration, int orderId)
|
||||
{
|
||||
var existingPayment = _paymentMethods
|
||||
.SingleOrDefault(p => p.IsEqualTo(cardTypeId, cardNumber, expiration));
|
||||
_paymentMethods.Add(payment);
|
||||
|
||||
if (existingPayment != null)
|
||||
{
|
||||
AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, existingPayment, orderId));
|
||||
AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId));
|
||||
|
||||
return existingPayment;
|
||||
}
|
||||
|
||||
var payment = new PaymentMethod(cardTypeId, alias, cardNumber, securityNumber, cardHolderName, expiration);
|
||||
|
||||
_paymentMethods.Add(payment);
|
||||
|
||||
AddDomainEvent(new BuyerAndPaymentMethodVerifiedDomainEvent(this, payment, orderId));
|
||||
|
||||
return payment;
|
||||
}
|
||||
return payment;
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,20 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate
|
||||
{
|
||||
/// <remarks>
|
||||
/// Card type class should be marked as abstract with protected constructor to encapsulate known enum types
|
||||
/// this is currently not possible as OrderingContextSeed uses this constructor to load cardTypes from csv file
|
||||
/// </remarks>
|
||||
public class CardType
|
||||
: Enumeration
|
||||
{
|
||||
public static CardType Amex = new(1, nameof(Amex));
|
||||
public static CardType Visa = new(2, nameof(Visa));
|
||||
public static CardType MasterCard = new(3, nameof(MasterCard));
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
||||
|
||||
public CardType(int id, string name)
|
||||
: base(id, name)
|
||||
{
|
||||
}
|
||||
/// <remarks>
|
||||
/// Card type class should be marked as abstract with protected constructor to encapsulate known enum types
|
||||
/// this is currently not possible as OrderingContextSeed uses this constructor to load cardTypes from csv file
|
||||
/// </remarks>
|
||||
public class CardType
|
||||
: Enumeration
|
||||
{
|
||||
public static CardType Amex = new(1, nameof(Amex));
|
||||
public static CardType Visa = new(2, nameof(Visa));
|
||||
public static CardType MasterCard = new(3, nameof(MasterCard));
|
||||
|
||||
public CardType(int id, string name)
|
||||
: base(id, name)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,13 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate
|
||||
//This is just the RepositoryContracts or Interface defined at the Domain Layer
|
||||
//as requisite for the Buyer Aggregate
|
||||
|
||||
public interface IBuyerRepository : IRepository<Buyer>
|
||||
{
|
||||
//This is just the RepositoryContracts or Interface defined at the Domain Layer
|
||||
//as requisite for the Buyer Aggregate
|
||||
|
||||
public interface IBuyerRepository : IRepository<Buyer>
|
||||
{
|
||||
Buyer Add(Buyer buyer);
|
||||
Buyer Update(Buyer buyer);
|
||||
Task<Buyer> FindAsync(string BuyerIdentityGuid);
|
||||
Task<Buyer> FindByIdAsync(string id);
|
||||
}
|
||||
Buyer Add(Buyer buyer);
|
||||
Buyer Update(Buyer buyer);
|
||||
Task<Buyer> FindAsync(string BuyerIdentityGuid);
|
||||
Task<Buyer> FindByIdAsync(string id);
|
||||
}
|
||||
|
||||
|
@ -1,46 +1,41 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using Ordering.Domain.Exceptions;
|
||||
using System;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate
|
||||
public class PaymentMethod
|
||||
: Entity
|
||||
{
|
||||
public class PaymentMethod
|
||||
: Entity
|
||||
private string _alias;
|
||||
private string _cardNumber;
|
||||
private string _securityNumber;
|
||||
private string _cardHolderName;
|
||||
private DateTime _expiration;
|
||||
|
||||
private int _cardTypeId;
|
||||
public CardType CardType { get; private set; }
|
||||
|
||||
|
||||
protected PaymentMethod() { }
|
||||
|
||||
public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration)
|
||||
{
|
||||
private string _alias;
|
||||
private string _cardNumber;
|
||||
private string _securityNumber;
|
||||
private string _cardHolderName;
|
||||
private DateTime _expiration;
|
||||
|
||||
private int _cardTypeId;
|
||||
public CardType CardType { get; private set; }
|
||||
_cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber));
|
||||
_securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber));
|
||||
_cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName));
|
||||
|
||||
|
||||
protected PaymentMethod() { }
|
||||
|
||||
public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration)
|
||||
if (expiration < DateTime.UtcNow)
|
||||
{
|
||||
|
||||
_cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber));
|
||||
_securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber));
|
||||
_cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName));
|
||||
|
||||
if (expiration < DateTime.UtcNow)
|
||||
{
|
||||
throw new OrderingDomainException(nameof(expiration));
|
||||
}
|
||||
|
||||
_alias = alias;
|
||||
_expiration = expiration;
|
||||
_cardTypeId = cardTypeId;
|
||||
throw new OrderingDomainException(nameof(expiration));
|
||||
}
|
||||
|
||||
public bool IsEqualTo(int cardTypeId, string cardNumber, DateTime expiration)
|
||||
{
|
||||
return _cardTypeId == cardTypeId
|
||||
&& _cardNumber == cardNumber
|
||||
&& _expiration == expiration;
|
||||
}
|
||||
_alias = alias;
|
||||
_expiration = expiration;
|
||||
_cardTypeId = cardTypeId;
|
||||
}
|
||||
|
||||
public bool IsEqualTo(int cardTypeId, string cardNumber, DateTime expiration)
|
||||
{
|
||||
return _cardTypeId == cardTypeId
|
||||
&& _cardNumber == cardNumber
|
||||
&& _expiration == expiration;
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,33 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
public class Address : ValueObject
|
||||
{
|
||||
public class Address : ValueObject
|
||||
public String Street { get; private set; }
|
||||
public String City { get; private set; }
|
||||
public String State { get; private set; }
|
||||
public String Country { get; private set; }
|
||||
public String ZipCode { get; private set; }
|
||||
|
||||
public Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
public String Street { get; private set; }
|
||||
public String City { get; private set; }
|
||||
public String State { get; private set; }
|
||||
public String Country { get; private set; }
|
||||
public String ZipCode { get; private set; }
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
|
||||
public Address() { }
|
||||
|
||||
public Address(string street, string city, string state, string country, string zipcode)
|
||||
{
|
||||
Street = street;
|
||||
City = city;
|
||||
State = state;
|
||||
Country = country;
|
||||
ZipCode = zipcode;
|
||||
}
|
||||
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
// Using a yield return statement to return each element one at a time
|
||||
yield return Street;
|
||||
yield return City;
|
||||
yield return State;
|
||||
yield return Country;
|
||||
yield return ZipCode;
|
||||
}
|
||||
protected override IEnumerable<object> GetEqualityComponents()
|
||||
{
|
||||
// Using a yield return statement to return each element one at a time
|
||||
yield return Street;
|
||||
yield return City;
|
||||
yield return State;
|
||||
yield return Country;
|
||||
yield return ZipCode;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,13 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
//This is just the RepositoryContracts or Interface defined at the Domain Layer
|
||||
//as requisite for the Order Aggregate
|
||||
|
||||
public interface IOrderRepository : IRepository<Order>
|
||||
{
|
||||
//This is just the RepositoryContracts or Interface defined at the Domain Layer
|
||||
//as requisite for the Order Aggregate
|
||||
Order Add(Order order);
|
||||
|
||||
public interface IOrderRepository : IRepository<Order>
|
||||
{
|
||||
Order Add(Order order);
|
||||
void Update(Order order);
|
||||
|
||||
void Update(Order order);
|
||||
|
||||
Task<Order> GetAsync(int orderId);
|
||||
}
|
||||
Task<Order> GetAsync(int orderId);
|
||||
}
|
||||
|
@ -1,202 +1,195 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using Ordering.Domain.Events;
|
||||
using Ordering.Domain.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Ordering.Domain.Events;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
public class Order
|
||||
: Entity, IAggregateRoot
|
||||
{
|
||||
public class Order
|
||||
: Entity, IAggregateRoot
|
||||
// DDD Patterns comment
|
||||
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation
|
||||
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
||||
private DateTime _orderDate;
|
||||
|
||||
// Address is a Value Object pattern example persisted as EF Core 2.0 owned entity
|
||||
public Address Address { get; private set; }
|
||||
|
||||
public int? GetBuyerId => _buyerId;
|
||||
private int? _buyerId;
|
||||
|
||||
public OrderStatus OrderStatus { get; private set; }
|
||||
private int _orderStatusId;
|
||||
|
||||
private string _description;
|
||||
|
||||
|
||||
|
||||
// Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed
|
||||
private bool _isDraft;
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
|
||||
private readonly List<OrderItem> _orderItems;
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
|
||||
|
||||
private int? _paymentMethodId;
|
||||
|
||||
public static Order NewDraft()
|
||||
{
|
||||
// DDD Patterns comment
|
||||
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation
|
||||
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
||||
private DateTime _orderDate;
|
||||
var order = new Order();
|
||||
order._isDraft = true;
|
||||
return order;
|
||||
}
|
||||
|
||||
// Address is a Value Object pattern example persisted as EF Core 2.0 owned entity
|
||||
public Address Address { get; private set; }
|
||||
protected Order()
|
||||
{
|
||||
_orderItems = new List<OrderItem>();
|
||||
_isDraft = false;
|
||||
}
|
||||
|
||||
public int? GetBuyerId => _buyerId;
|
||||
private int? _buyerId;
|
||||
public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
|
||||
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
|
||||
{
|
||||
_buyerId = buyerId;
|
||||
_paymentMethodId = paymentMethodId;
|
||||
_orderStatusId = OrderStatus.Submitted.Id;
|
||||
_orderDate = DateTime.UtcNow;
|
||||
Address = address;
|
||||
|
||||
public OrderStatus OrderStatus { get; private set; }
|
||||
private int _orderStatusId;
|
||||
// Add the OrderStarterDomainEvent to the domain events collection
|
||||
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
|
||||
AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber,
|
||||
cardSecurityNumber, cardHolderName, cardExpiration);
|
||||
}
|
||||
|
||||
private string _description;
|
||||
// DDD Patterns comment
|
||||
// This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order,
|
||||
// so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot
|
||||
// in order to maintain consistency between the whole Aggregate.
|
||||
public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
|
||||
{
|
||||
var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId)
|
||||
.SingleOrDefault();
|
||||
|
||||
|
||||
|
||||
// Draft orders have this set to true. Currently we don't check anywhere the draft status of an Order, but we could do it if needed
|
||||
private bool _isDraft;
|
||||
|
||||
// DDD Patterns comment
|
||||
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
|
||||
private readonly List<OrderItem> _orderItems;
|
||||
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
|
||||
|
||||
private int? _paymentMethodId;
|
||||
|
||||
public static Order NewDraft()
|
||||
if (existingOrderForProduct != null)
|
||||
{
|
||||
var order = new Order();
|
||||
order._isDraft = true;
|
||||
return order;
|
||||
}
|
||||
//if previous line exist modify it with higher discount and units..
|
||||
|
||||
protected Order()
|
||||
{
|
||||
_orderItems = new List<OrderItem>();
|
||||
_isDraft = false;
|
||||
}
|
||||
|
||||
public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
|
||||
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
|
||||
{
|
||||
_buyerId = buyerId;
|
||||
_paymentMethodId = paymentMethodId;
|
||||
_orderStatusId = OrderStatus.Submitted.Id;
|
||||
_orderDate = DateTime.UtcNow;
|
||||
Address = address;
|
||||
|
||||
// Add the OrderStarterDomainEvent to the domain events collection
|
||||
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
|
||||
AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber,
|
||||
cardSecurityNumber, cardHolderName, cardExpiration);
|
||||
}
|
||||
|
||||
// DDD Patterns comment
|
||||
// This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order,
|
||||
// so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot
|
||||
// in order to maintain consistency between the whole Aggregate.
|
||||
public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
|
||||
{
|
||||
var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId)
|
||||
.SingleOrDefault();
|
||||
|
||||
if (existingOrderForProduct != null)
|
||||
if (discount > existingOrderForProduct.GetCurrentDiscount())
|
||||
{
|
||||
//if previous line exist modify it with higher discount and units..
|
||||
|
||||
if (discount > existingOrderForProduct.GetCurrentDiscount())
|
||||
{
|
||||
existingOrderForProduct.SetNewDiscount(discount);
|
||||
}
|
||||
|
||||
existingOrderForProduct.AddUnits(units);
|
||||
}
|
||||
else
|
||||
{
|
||||
//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;
|
||||
}
|
||||
|
||||
public void SetAwaitingValidationStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Submitted.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
|
||||
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStockConfirmedStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
|
||||
|
||||
_orderStatusId = OrderStatus.StockConfirmed.Id;
|
||||
_description = "All the items were confirmed with available stock.";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPaidStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.StockConfirmed.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
|
||||
|
||||
_orderStatusId = OrderStatus.Paid.Id;
|
||||
_description = "The payment was performed at a simulated \"American Bank checking bank account ending on XX35071\"";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShippedStatus()
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.Paid.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Shipped);
|
||||
existingOrderForProduct.SetNewDiscount(discount);
|
||||
}
|
||||
|
||||
_orderStatusId = OrderStatus.Shipped.Id;
|
||||
_description = "The order was shipped.";
|
||||
AddDomainEvent(new OrderShippedDomainEvent(this));
|
||||
existingOrderForProduct.AddUnits(units);
|
||||
}
|
||||
|
||||
public void SetCancelledStatus()
|
||||
else
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Paid.Id ||
|
||||
_orderStatusId == OrderStatus.Shipped.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Cancelled);
|
||||
}
|
||||
//add validated new order item
|
||||
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
_description = $"The order was cancelled.";
|
||||
AddDomainEvent(new OrderCancelledDomainEvent(this));
|
||||
}
|
||||
|
||||
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
|
||||
var itemsStockRejectedProductNames = OrderItems
|
||||
.Where(c => orderStockRejectedItems.Contains(c.ProductId))
|
||||
.Select(c => c.GetOrderItemProductName());
|
||||
|
||||
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
|
||||
_description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber,
|
||||
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
|
||||
{
|
||||
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId,
|
||||
cardNumber, cardSecurityNumber,
|
||||
cardHolderName, cardExpiration);
|
||||
|
||||
this.AddDomainEvent(orderStartedDomainEvent);
|
||||
}
|
||||
|
||||
private void StatusChangeException(OrderStatus orderStatusToChange)
|
||||
{
|
||||
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
|
||||
}
|
||||
|
||||
public decimal GetTotal()
|
||||
{
|
||||
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
|
||||
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;
|
||||
}
|
||||
|
||||
public void SetAwaitingValidationStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Submitted.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
|
||||
_orderStatusId = OrderStatus.AwaitingValidation.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetStockConfirmedStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
|
||||
|
||||
_orderStatusId = OrderStatus.StockConfirmed.Id;
|
||||
_description = "All the items were confirmed with available stock.";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPaidStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.StockConfirmed.Id)
|
||||
{
|
||||
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
|
||||
|
||||
_orderStatusId = OrderStatus.Paid.Id;
|
||||
_description = "The payment was performed at a simulated \"American Bank checking bank account ending on XX35071\"";
|
||||
}
|
||||
}
|
||||
|
||||
public void SetShippedStatus()
|
||||
{
|
||||
if (_orderStatusId != OrderStatus.Paid.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Shipped);
|
||||
}
|
||||
|
||||
_orderStatusId = OrderStatus.Shipped.Id;
|
||||
_description = "The order was shipped.";
|
||||
AddDomainEvent(new OrderShippedDomainEvent(this));
|
||||
}
|
||||
|
||||
public void SetCancelledStatus()
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.Paid.Id ||
|
||||
_orderStatusId == OrderStatus.Shipped.Id)
|
||||
{
|
||||
StatusChangeException(OrderStatus.Cancelled);
|
||||
}
|
||||
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
_description = $"The order was cancelled.";
|
||||
AddDomainEvent(new OrderCancelledDomainEvent(this));
|
||||
}
|
||||
|
||||
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
|
||||
{
|
||||
if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
|
||||
{
|
||||
_orderStatusId = OrderStatus.Cancelled.Id;
|
||||
|
||||
var itemsStockRejectedProductNames = OrderItems
|
||||
.Where(c => orderStockRejectedItems.Contains(c.ProductId))
|
||||
.Select(c => c.GetOrderItemProductName());
|
||||
|
||||
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
|
||||
_description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
|
||||
}
|
||||
}
|
||||
|
||||
private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber,
|
||||
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
|
||||
{
|
||||
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId,
|
||||
cardNumber, cardSecurityNumber,
|
||||
cardHolderName, cardExpiration);
|
||||
|
||||
this.AddDomainEvent(orderStartedDomainEvent);
|
||||
}
|
||||
|
||||
private void StatusChangeException(OrderStatus orderStatusToChange)
|
||||
{
|
||||
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
|
||||
}
|
||||
|
||||
public decimal GetTotal()
|
||||
{
|
||||
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
|
||||
}
|
||||
}
|
||||
|
@ -1,82 +1,78 @@
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
using Ordering.Domain.Exceptions;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
public class OrderItem
|
||||
: Entity
|
||||
{
|
||||
public class OrderItem
|
||||
: Entity
|
||||
// DDD Patterns comment
|
||||
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation
|
||||
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
||||
private string _productName;
|
||||
private string _pictureUrl;
|
||||
private decimal _unitPrice;
|
||||
private decimal _discount;
|
||||
private int _units;
|
||||
|
||||
public int ProductId { get; private set; }
|
||||
|
||||
protected OrderItem() { }
|
||||
|
||||
public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1)
|
||||
{
|
||||
// DDD Patterns comment
|
||||
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation
|
||||
// aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
|
||||
private string _productName;
|
||||
private string _pictureUrl;
|
||||
private decimal _unitPrice;
|
||||
private decimal _discount;
|
||||
private int _units;
|
||||
|
||||
public int ProductId { get; private set; }
|
||||
|
||||
protected OrderItem() { }
|
||||
|
||||
public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1)
|
||||
if (units <= 0)
|
||||
{
|
||||
if (units <= 0)
|
||||
{
|
||||
throw new OrderingDomainException("Invalid number of units");
|
||||
}
|
||||
|
||||
if ((unitPrice * units) < discount)
|
||||
{
|
||||
throw new OrderingDomainException("The total of order item is lower than applied discount");
|
||||
}
|
||||
|
||||
ProductId = productId;
|
||||
|
||||
_productName = productName;
|
||||
_unitPrice = unitPrice;
|
||||
_discount = discount;
|
||||
_units = units;
|
||||
_pictureUrl = PictureUrl;
|
||||
throw new OrderingDomainException("Invalid number of units");
|
||||
}
|
||||
|
||||
public string GetPictureUri() => _pictureUrl;
|
||||
|
||||
public decimal GetCurrentDiscount()
|
||||
if ((unitPrice * units) < discount)
|
||||
{
|
||||
return _discount;
|
||||
throw new OrderingDomainException("The total of order item is lower than applied discount");
|
||||
}
|
||||
|
||||
public int GetUnits()
|
||||
ProductId = productId;
|
||||
|
||||
_productName = productName;
|
||||
_unitPrice = unitPrice;
|
||||
_discount = discount;
|
||||
_units = units;
|
||||
_pictureUrl = PictureUrl;
|
||||
}
|
||||
|
||||
public string GetPictureUri() => _pictureUrl;
|
||||
|
||||
public decimal GetCurrentDiscount()
|
||||
{
|
||||
return _discount;
|
||||
}
|
||||
|
||||
public int GetUnits()
|
||||
{
|
||||
return _units;
|
||||
}
|
||||
|
||||
public decimal GetUnitPrice()
|
||||
{
|
||||
return _unitPrice;
|
||||
}
|
||||
|
||||
public string GetOrderItemProductName() => _productName;
|
||||
|
||||
public void SetNewDiscount(decimal discount)
|
||||
{
|
||||
if (discount < 0)
|
||||
{
|
||||
return _units;
|
||||
throw new OrderingDomainException("Discount is not valid");
|
||||
}
|
||||
|
||||
public decimal GetUnitPrice()
|
||||
_discount = discount;
|
||||
}
|
||||
|
||||
public void AddUnits(int units)
|
||||
{
|
||||
if (units < 0)
|
||||
{
|
||||
return _unitPrice;
|
||||
throw new OrderingDomainException("Invalid units");
|
||||
}
|
||||
|
||||
public string GetOrderItemProductName() => _productName;
|
||||
|
||||
public void SetNewDiscount(decimal discount)
|
||||
{
|
||||
if (discount < 0)
|
||||
{
|
||||
throw new OrderingDomainException("Discount is not valid");
|
||||
}
|
||||
|
||||
_discount = discount;
|
||||
}
|
||||
|
||||
public void AddUnits(int units)
|
||||
{
|
||||
if (units < 0)
|
||||
{
|
||||
throw new OrderingDomainException("Invalid units");
|
||||
}
|
||||
|
||||
_units += units;
|
||||
}
|
||||
_units += units;
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,47 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
|
||||
public class OrderStatus
|
||||
: Enumeration
|
||||
{
|
||||
using global::Ordering.Domain.Exceptions;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
|
||||
public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
|
||||
public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
|
||||
public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
|
||||
public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
|
||||
public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
|
||||
|
||||
public class OrderStatus
|
||||
: Enumeration
|
||||
public OrderStatus(int id, string name)
|
||||
: base(id, name)
|
||||
{
|
||||
public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
|
||||
public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
|
||||
public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
|
||||
public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
|
||||
public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
|
||||
public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
|
||||
}
|
||||
|
||||
public OrderStatus(int id, string name)
|
||||
: base(id, name)
|
||||
public static IEnumerable<OrderStatus> List() =>
|
||||
new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled };
|
||||
|
||||
public static OrderStatus FromName(string name)
|
||||
{
|
||||
var state = List()
|
||||
.SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}");
|
||||
}
|
||||
|
||||
public static IEnumerable<OrderStatus> List() =>
|
||||
new[] { Submitted, AwaitingValidation, StockConfirmed, Paid, Shipped, Cancelled };
|
||||
return state;
|
||||
}
|
||||
|
||||
public static OrderStatus FromName(string name)
|
||||
public static OrderStatus From(int id)
|
||||
{
|
||||
var state = List().SingleOrDefault(s => s.Id == id);
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
var state = List()
|
||||
.SingleOrDefault(s => String.Equals(s.Name, name, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}");
|
||||
}
|
||||
|
||||
return state;
|
||||
throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}");
|
||||
}
|
||||
|
||||
public static OrderStatus From(int id)
|
||||
{
|
||||
var state = List().SingleOrDefault(s => s.Id == id);
|
||||
|
||||
if (state == null)
|
||||
{
|
||||
throw new OrderingDomainException($"Possible values for OrderStatus: {String.Join(",", List().Select(s => s.Name))}");
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,16 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
|
||||
namespace Ordering.Domain.Events
|
||||
public class BuyerAndPaymentMethodVerifiedDomainEvent
|
||||
: INotification
|
||||
{
|
||||
public class BuyerAndPaymentMethodVerifiedDomainEvent
|
||||
: INotification
|
||||
{
|
||||
public Buyer Buyer { get; private set; }
|
||||
public PaymentMethod Payment { get; private set; }
|
||||
public int OrderId { get; private set; }
|
||||
public Buyer Buyer { get; private set; }
|
||||
public PaymentMethod Payment { get; private set; }
|
||||
public int OrderId { get; private set; }
|
||||
|
||||
public BuyerAndPaymentMethodVerifiedDomainEvent(Buyer buyer, PaymentMethod payment, int orderId)
|
||||
{
|
||||
Buyer = buyer;
|
||||
Payment = payment;
|
||||
OrderId = orderId;
|
||||
}
|
||||
public BuyerAndPaymentMethodVerifiedDomainEvent(Buyer buyer, PaymentMethod payment, int orderId)
|
||||
{
|
||||
Buyer = buyer;
|
||||
Payment = payment;
|
||||
OrderId = orderId;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,12 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Ordering.Domain.Events;
|
||||
|
||||
namespace Ordering.Domain.Events
|
||||
public class OrderCancelledDomainEvent : INotification
|
||||
{
|
||||
public class OrderCancelledDomainEvent : INotification
|
||||
{
|
||||
public Order Order { get; }
|
||||
public Order Order { get; }
|
||||
|
||||
public OrderCancelledDomainEvent(Order order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
public OrderCancelledDomainEvent(Order order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,11 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
|
||||
namespace Ordering.Domain.Events
|
||||
public class OrderShippedDomainEvent : INotification
|
||||
{
|
||||
public class OrderShippedDomainEvent : INotification
|
||||
{
|
||||
public Order Order { get; }
|
||||
public Order Order { get; }
|
||||
|
||||
public OrderShippedDomainEvent(Order order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
public OrderShippedDomainEvent(Order order)
|
||||
{
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using System;
|
||||
|
||||
namespace Ordering.Domain.Events
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Event used when an order is created
|
||||
|
@ -1,23 +1,18 @@
|
||||
namespace Ordering.Domain.Events
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Event used when the grace period order is confirmed
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToAwaitingValidationDomainEvent
|
||||
: INotification
|
||||
{
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using System.Collections.Generic;
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderItem> OrderItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event used when the grace period order is confirmed
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToAwaitingValidationDomainEvent
|
||||
: INotification
|
||||
public OrderStatusChangedToAwaitingValidationDomainEvent(int orderId,
|
||||
IEnumerable<OrderItem> orderItems)
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderItem> OrderItems { get; }
|
||||
|
||||
public OrderStatusChangedToAwaitingValidationDomainEvent(int orderId,
|
||||
IEnumerable<OrderItem> orderItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderItems = orderItems;
|
||||
}
|
||||
OrderId = orderId;
|
||||
OrderItems = orderItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,17 @@
|
||||
namespace Ordering.Domain.Events
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
/// <summary>
|
||||
/// Event used when the order is paid
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToPaidDomainEvent
|
||||
: INotification
|
||||
{
|
||||
using MediatR;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate;
|
||||
using System.Collections.Generic;
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderItem> OrderItems { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event used when the order is paid
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToPaidDomainEvent
|
||||
: INotification
|
||||
public OrderStatusChangedToPaidDomainEvent(int orderId,
|
||||
IEnumerable<OrderItem> orderItems)
|
||||
{
|
||||
public int OrderId { get; }
|
||||
public IEnumerable<OrderItem> OrderItems { get; }
|
||||
|
||||
public OrderStatusChangedToPaidDomainEvent(int orderId,
|
||||
IEnumerable<OrderItem> orderItems)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderItems = orderItems;
|
||||
}
|
||||
OrderId = orderId;
|
||||
OrderItems = orderItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,13 @@
|
||||
namespace Ordering.Domain.Events
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Event used when the order stock items are confirmed
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToStockConfirmedDomainEvent
|
||||
: INotification
|
||||
{
|
||||
using MediatR;
|
||||
public int OrderId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event used when the order stock items are confirmed
|
||||
/// </summary>
|
||||
public class OrderStatusChangedToStockConfirmedDomainEvent
|
||||
: INotification
|
||||
{
|
||||
public int OrderId { get; }
|
||||
|
||||
public OrderStatusChangedToStockConfirmedDomainEvent(int orderId)
|
||||
=> OrderId = orderId;
|
||||
}
|
||||
}
|
||||
public OrderStatusChangedToStockConfirmedDomainEvent(int orderId)
|
||||
=> OrderId = orderId;
|
||||
}
|
||||
|
@ -1,21 +1,18 @@
|
||||
using System;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Exceptions;
|
||||
|
||||
namespace Ordering.Domain.Exceptions
|
||||
/// <summary>
|
||||
/// Exception type for domain exceptions
|
||||
/// </summary>
|
||||
public class OrderingDomainException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception type for domain exceptions
|
||||
/// </summary>
|
||||
public class OrderingDomainException : Exception
|
||||
{
|
||||
public OrderingDomainException()
|
||||
{ }
|
||||
public OrderingDomainException()
|
||||
{ }
|
||||
|
||||
public OrderingDomainException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
public OrderingDomainException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
public OrderingDomainException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
}
|
||||
public OrderingDomainException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
}
|
||||
|
@ -1,92 +1,87 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
|
||||
public abstract class Entity
|
||||
{
|
||||
using MediatR;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public abstract class Entity
|
||||
int? _requestedHashCode;
|
||||
int _Id;
|
||||
public virtual int Id
|
||||
{
|
||||
int? _requestedHashCode;
|
||||
int _Id;
|
||||
public virtual int Id
|
||||
get
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Id;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_Id = value;
|
||||
}
|
||||
return _Id;
|
||||
}
|
||||
|
||||
private List<INotification> _domainEvents;
|
||||
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
|
||||
|
||||
public void AddDomainEvent(INotification eventItem)
|
||||
protected set
|
||||
{
|
||||
_domainEvents = _domainEvents ?? new List<INotification>();
|
||||
_domainEvents.Add(eventItem);
|
||||
}
|
||||
|
||||
public void RemoveDomainEvent(INotification eventItem)
|
||||
{
|
||||
_domainEvents?.Remove(eventItem);
|
||||
}
|
||||
|
||||
public void ClearDomainEvents()
|
||||
{
|
||||
_domainEvents?.Clear();
|
||||
}
|
||||
|
||||
public bool IsTransient()
|
||||
{
|
||||
return this.Id == default(Int32);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || !(obj is Entity))
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
if (this.GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
Entity item = (Entity)obj;
|
||||
|
||||
if (item.IsTransient() || this.IsTransient())
|
||||
return false;
|
||||
else
|
||||
return item.Id == this.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (!IsTransient())
|
||||
{
|
||||
if (!_requestedHashCode.HasValue)
|
||||
_requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
|
||||
|
||||
return _requestedHashCode.Value;
|
||||
}
|
||||
else
|
||||
return base.GetHashCode();
|
||||
|
||||
}
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
if (Object.Equals(left, null))
|
||||
return (Object.Equals(right, null)) ? true : false;
|
||||
else
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Entity left, Entity right)
|
||||
{
|
||||
return !(left == right);
|
||||
_Id = value;
|
||||
}
|
||||
}
|
||||
|
||||
private List<INotification> _domainEvents;
|
||||
public IReadOnlyCollection<INotification> DomainEvents => _domainEvents?.AsReadOnly();
|
||||
|
||||
public void AddDomainEvent(INotification eventItem)
|
||||
{
|
||||
_domainEvents = _domainEvents ?? new List<INotification>();
|
||||
_domainEvents.Add(eventItem);
|
||||
}
|
||||
|
||||
public void RemoveDomainEvent(INotification eventItem)
|
||||
{
|
||||
_domainEvents?.Remove(eventItem);
|
||||
}
|
||||
|
||||
public void ClearDomainEvents()
|
||||
{
|
||||
_domainEvents?.Clear();
|
||||
}
|
||||
|
||||
public bool IsTransient()
|
||||
{
|
||||
return this.Id == default(Int32);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || !(obj is Entity))
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
if (this.GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
Entity item = (Entity)obj;
|
||||
|
||||
if (item.IsTransient() || this.IsTransient())
|
||||
return false;
|
||||
else
|
||||
return item.Id == this.Id;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (!IsTransient())
|
||||
{
|
||||
if (!_requestedHashCode.HasValue)
|
||||
_requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
|
||||
|
||||
return _requestedHashCode.Value;
|
||||
}
|
||||
else
|
||||
return base.GetHashCode();
|
||||
|
||||
}
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
if (Object.Equals(left, null))
|
||||
return (Object.Equals(right, null)) ? true : false;
|
||||
else
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Entity left, Entity right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
public abstract class Enumeration : IComparable
|
||||
{
|
||||
public abstract class Enumeration : IComparable
|
||||
public string Name { get; private set; }
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
protected Enumeration(int id, string name) => (Id, Name) = (id, name);
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
|
||||
typeof(T).GetFields(BindingFlags.Public |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.DeclaredOnly)
|
||||
.Select(f => f.GetValue(null))
|
||||
.Cast<T>();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
protected Enumeration(int id, string name) => (Id, Name) = (id, name);
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
|
||||
typeof(T).GetFields(BindingFlags.Public |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.DeclaredOnly)
|
||||
.Select(f => f.GetValue(null))
|
||||
.Cast<T>();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
if (obj is not Enumeration otherValue)
|
||||
{
|
||||
if (obj is not Enumeration otherValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var typeMatches = GetType().Equals(obj.GetType());
|
||||
var valueMatches = Id.Equals(otherValue.Id);
|
||||
|
||||
return typeMatches && valueMatches;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
var typeMatches = GetType().Equals(obj.GetType());
|
||||
var valueMatches = Id.Equals(otherValue.Id);
|
||||
|
||||
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
|
||||
{
|
||||
var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id);
|
||||
return absoluteDifference;
|
||||
}
|
||||
|
||||
public static T FromValue<T>(int value) where T : Enumeration
|
||||
{
|
||||
var matchingItem = Parse<T, int>(value, "value", item => item.Id == value);
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
public static T FromDisplayName<T>(string displayName) where T : Enumeration
|
||||
{
|
||||
var matchingItem = Parse<T, string>(displayName, "display name", item => item.Name == displayName);
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
private static T Parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration
|
||||
{
|
||||
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
|
||||
|
||||
if (matchingItem == null)
|
||||
throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");
|
||||
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);
|
||||
return typeMatches && valueMatches;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
|
||||
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
|
||||
{
|
||||
var absoluteDifference = Math.Abs(firstValue.Id - secondValue.Id);
|
||||
return absoluteDifference;
|
||||
}
|
||||
|
||||
public static T FromValue<T>(int value) where T : Enumeration
|
||||
{
|
||||
var matchingItem = Parse<T, int>(value, "value", item => item.Id == value);
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
public static T FromDisplayName<T>(string displayName) where T : Enumeration
|
||||
{
|
||||
var matchingItem = Parse<T, string>(displayName, "display name", item => item.Name == displayName);
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
private static T Parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration
|
||||
{
|
||||
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
|
||||
|
||||
if (matchingItem == null)
|
||||
throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");
|
||||
|
||||
return matchingItem;
|
||||
}
|
||||
|
||||
public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork
|
||||
{
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
|
||||
public interface IAggregateRoot { }
|
||||
|
||||
public interface IAggregateRoot { }
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
|
||||
public interface IRepository<T> where T : IAggregateRoot
|
||||
{
|
||||
public interface IRepository<T> where T : IAggregateRoot
|
||||
{
|
||||
IUnitOfWork UnitOfWork { get; }
|
||||
}
|
||||
IUnitOfWork UnitOfWork { get; }
|
||||
}
|
||||
|
@ -1,12 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
public interface IUnitOfWork : IDisposable
|
||||
{
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
|
@ -1,48 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
public abstract class ValueObject
|
||||
{
|
||||
public abstract class ValueObject
|
||||
protected static bool EqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
protected static bool EqualOperator(ValueObject left, ValueObject right)
|
||||
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
|
||||
{
|
||||
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ReferenceEquals(left, null) || left.Equals(right);
|
||||
return false;
|
||||
}
|
||||
return ReferenceEquals(left, null) || left.Equals(right);
|
||||
}
|
||||
|
||||
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
return !(EqualOperator(left, right));
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<object> GetEqualityComponents();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
|
||||
{
|
||||
return !(EqualOperator(left, right));
|
||||
}
|
||||
var other = (ValueObject)obj;
|
||||
|
||||
protected abstract IEnumerable<object> GetEqualityComponents();
|
||||
return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetEqualityComponents()
|
||||
.Select(x => x != null ? x.GetHashCode() : 0)
|
||||
.Aggregate((x, y) => x ^ y);
|
||||
}
|
||||
|
||||
var other = (ValueObject)obj;
|
||||
|
||||
return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetEqualityComponents()
|
||||
.Select(x => x != null ? x.GetHashCode() : 0)
|
||||
.Aggregate((x, y) => x ^ y);
|
||||
}
|
||||
|
||||
public ValueObject GetCopy()
|
||||
{
|
||||
return this.MemberwiseClone() as ValueObject;
|
||||
}
|
||||
public ValueObject GetCopy()
|
||||
{
|
||||
return this.MemberwiseClone() as ValueObject;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user