using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; 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; public Address Address { get; private set; } public Buyer Buyer { get; private set; } private int _buyerId; public OrderStatus OrderStatus { get; private set; } private int _orderStatusId; // 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 _orderItems; public IEnumerable OrderItems => _orderItems.AsReadOnly(); // Using List<>.AsReadOnly() // This will create a read only wrapper around the private list so is protected against "external updates". // It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance) //https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx public PaymentMethod PaymentMethod { get; private set; } private int _paymentMethodId; protected Order() { } public Order(int buyerId, int paymentMethodId, Address address) { _orderItems = new List(); _buyerId = buyerId; _paymentMethodId = paymentMethodId; _orderStatusId = OrderStatus.InProcess.Id; _orderDate = DateTime.UtcNow; Address = address; } // 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); existingOrderForProduct.AddUnits(units); } } else { //add validated new order item var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); _orderItems.Add(orderItem); } } } }