195 lines
7.4 KiB
C#
Raw Normal View History

2017-05-11 16:04:35 +02:00
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
2017-03-14 18:02:28 +01:00
using Ordering.Domain.Events;
using Ordering.Domain.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
{
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; }
2016-11-22 18:40:47 +01:00
2017-03-14 18:02:28 +01:00
private int? _buyerId;
public OrderStatus OrderStatus { get; private set; }
private int _orderStatusId;
2017-05-11 16:04:35 +02:00
private string _description;
2017-03-03 12:03:31 +01:00
// 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;
2016-11-22 18:40:47 +01:00
2017-03-14 18:02:28 +01:00
private int? _paymentMethodId;
2016-11-22 18:40:47 +01:00
protected Order() { _orderItems = new List<OrderItem>(); }
public Order(string userId, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
2017-03-14 18:02:28 +01:00
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null)
{
_orderItems = new List<OrderItem>();
_buyerId = buyerId;
_paymentMethodId = paymentMethodId;
2017-05-20 12:35:16 -07:00
_orderStatusId = OrderStatus.Submitted.Id;
_orderDate = DateTime.UtcNow;
Address = address;
2017-03-17 18:36:34 -07:00
// Add the OrderStarterDomainEvent to the domain events collection
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
AddOrderStartedDomainEvent(userId, cardTypeId, cardNumber,
2017-03-17 18:36:34 -07:00
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 previous line exist modify it with higher discount and units..
if (discount > existingOrderForProduct.GetCurrentDiscount())
{
existingOrderForProduct.SetNewDiscount(discount);
}
2017-08-22 12:04:40 +02:00
existingOrderForProduct.AddUnits(units);
}
else
{
//add validated new order item
var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);
_orderItems.Add(orderItem);
}
2016-12-14 18:23:57 +01:00
}
2017-03-14 18:02:28 +01:00
public void SetPaymentId(int id)
{
_paymentMethodId = id;
}
public void SetBuyerId(int id)
{
_buyerId = id;
}
public void SetAwaitingValidationStatus()
{
if (_orderStatusId == OrderStatus.Cancelled.Id ||
2017-05-20 12:35:16 -07:00
_orderStatusId != OrderStatus.Submitted.Id)
{
StatusChangeException(OrderStatus.AwaitingValidation);
}
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id;
}
public void SetStockConfirmedStatus()
{
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
{
StatusChangeException(OrderStatus.StockConfirmed);
}
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
2017-05-11 16:04:35 +02:00
_orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock.";
}
public void SetPaidStatus()
{
if (_orderStatusId != OrderStatus.StockConfirmed.Id)
{
StatusChangeException(OrderStatus.Paid);
}
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
_orderStatusId = OrderStatus.Paid.Id;
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
}
public void SetShippedStatus()
{
if (_orderStatusId != OrderStatus.Paid.Id)
{
StatusChangeException(OrderStatus.Shipped);
}
_orderStatusId = OrderStatus.Shipped.Id;
_description = "The order was shipped.";
}
2017-05-16 10:19:35 +02:00
public void SetCancelledStatus()
{
if (_orderStatusId == OrderStatus.Paid.Id ||
_orderStatusId == OrderStatus.Shipped.Id)
{
StatusChangeException(OrderStatus.Cancelled);
}
_orderStatusId = OrderStatus.Cancelled.Id;
_description = $"The order was cancelled.";
}
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
{
if (_orderStatusId != OrderStatus.AwaitingValidation.Id)
{
StatusChangeException(OrderStatus.Cancelled);
2017-05-16 10:19:35 +02:00
}
_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, int cardTypeId, string cardNumber,
2017-03-14 18:02:28 +01:00
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
{
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, cardTypeId,
cardNumber, cardSecurityNumber,
cardHolderName, cardExpiration);
2017-03-14 18:02:28 +01:00
2017-03-17 18:36:34 -07:00
this.AddDomainEvent(orderStartedDomainEvent);
2017-03-14 18:02:28 +01:00
}
private void StatusChangeException(OrderStatus orderStatusToChange)
{
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
}
2017-08-22 12:04:40 +02:00
public decimal GetTotal()
{
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
}
}
}
2017-05-11 16:04:35 +02:00