make fields readonly

This commit is contained in:
Rafsanul Hasan 2019-04-04 10:21:04 +06:00
parent 4c530da377
commit 99d47401c0
No known key found for this signature in database
GPG Key ID: 4BBF45E04D0AD72B
3 changed files with 234 additions and 248 deletions

View File

@ -4,43 +4,41 @@ using System;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate
{ {
public class PaymentMethod public class PaymentMethod
: Entity : Entity
{ {
private string _alias; private readonly string _alias;
private string _cardNumber; private readonly string _cardNumber;
private string _securityNumber; private readonly string _securityNumber;
private string _cardHolderName; private readonly string _cardHolderName;
private DateTime _expiration; private readonly DateTime _expiration;
private int _cardTypeId; private readonly int _cardTypeId;
public CardType CardType { get; private set; } public CardType CardType { get; private set; }
protected PaymentMethod() { } protected PaymentMethod() { }
public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration) public PaymentMethod(int cardTypeId, string alias, string cardNumber, string securityNumber, string cardHolderName, DateTime expiration)
{ {
_cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber)); _cardNumber = !string.IsNullOrWhiteSpace(cardNumber) ? cardNumber : throw new OrderingDomainException(nameof(cardNumber));
_securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber)); _securityNumber = !string.IsNullOrWhiteSpace(securityNumber) ? securityNumber : throw new OrderingDomainException(nameof(securityNumber));
_cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName)); _cardHolderName = !string.IsNullOrWhiteSpace(cardHolderName) ? cardHolderName : throw new OrderingDomainException(nameof(cardHolderName));
if (expiration < DateTime.UtcNow) if (expiration < DateTime.UtcNow)
{ {
throw new OrderingDomainException(nameof(expiration)); throw new OrderingDomainException(nameof(expiration));
} }
_alias = alias; _alias = alias;
_expiration = expiration; _expiration = expiration;
_cardTypeId = cardTypeId; _cardTypeId = cardTypeId;
} }
public bool IsEqualTo(int cardTypeId, string cardNumber,DateTime expiration) public bool IsEqualTo(int cardTypeId, string cardNumber, DateTime expiration)
{ => _cardTypeId == cardTypeId
return _cardTypeId == cardTypeId && _cardNumber == cardNumber
&& _cardNumber == cardNumber && _expiration == expiration;
&& _expiration == expiration; }
}
}
} }

View File

@ -7,194 +7,189 @@ using System.Linq;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
{ {
public class Order public class Order
: Entity, IAggregateRoot : Entity, IAggregateRoot
{ {
// DDD Patterns comment // DDD Patterns comment
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation // 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) // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
private DateTime _orderDate; private readonly DateTime _orderDate;
// Address is a Value Object pattern example persisted as EF Core 2.0 owned entity // Address is a Value Object pattern example persisted as EF Core 2.0 owned entity
public Address Address { get; private set; } public Address Address { get; private set; }
public int? GetBuyerId => _buyerId; public int? GetBuyerId => _buyerId;
private int? _buyerId; private int? _buyerId;
public OrderStatus OrderStatus { get; private set; } public OrderStatus OrderStatus { get; private set; }
private int _orderStatusId; private int _orderStatusId;
private string _description; 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 // 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; private bool _isDraft;
// DDD Patterns comment // DDD Patterns comment
// Using a private collection field, better for DDD Aggregate's encapsulation // Using a private collection field, better for DDD Aggregate's encapsulation
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection, // so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
// but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour. // but only through the method OrderAggrergateRoot.AddOrderItem() which includes behaviour.
private readonly List<OrderItem> _orderItems; private readonly List<OrderItem> _orderItems;
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems; public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
private int? _paymentMethodId; private int? _paymentMethodId;
public static Order NewDraft() public static Order NewDraft()
{ {
var order = new Order(); var order = new Order
order._isDraft = true; {
return order; _isDraft = true
} };
return order;
}
protected Order() { protected Order()
_orderItems = new List<OrderItem>(); {
_isDraft = false; _orderItems = new List<OrderItem>();
} _isDraft = false;
}
public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, 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() string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
{ {
_buyerId = buyerId; _buyerId = buyerId;
_paymentMethodId = paymentMethodId; _paymentMethodId = paymentMethodId;
_orderStatusId = OrderStatus.Submitted.Id; _orderStatusId = OrderStatus.Submitted.Id;
_orderDate = DateTime.UtcNow; _orderDate = DateTime.UtcNow;
Address = address; Address = address;
// Add the OrderStarterDomainEvent to the domain events collection // Add the OrderStarterDomainEvent to the domain events collection
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ] // to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber, AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber,
cardSecurityNumber, cardHolderName, cardExpiration); cardSecurityNumber, cardHolderName, cardExpiration);
} }
// DDD Patterns comment // DDD Patterns comment
// This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order, // 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 // so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot
// in order to maintain consistency between the whole Aggregate. // 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) public void AddOrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
{ {
var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId) var existingOrderForProduct = _orderItems.Where(o => o.ProductId == productId)
.SingleOrDefault(); .SingleOrDefault();
if (existingOrderForProduct != null) if (existingOrderForProduct != null)
{ {
//if previous line exist modify it with higher discount and units.. //if previous line exist modify it with higher discount and units..
if (discount > existingOrderForProduct.GetCurrentDiscount()) if (discount > existingOrderForProduct.GetCurrentDiscount())
{ {
existingOrderForProduct.SetNewDiscount(discount); existingOrderForProduct.SetNewDiscount(discount);
} }
existingOrderForProduct.AddUnits(units); existingOrderForProduct.AddUnits(units);
} }
else else
{ {
//add validated new order item //add validated new order item
var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);
_orderItems.Add(orderItem); _orderItems.Add(orderItem);
} }
} }
public void SetPaymentId(int id) public void SetPaymentId(int id)
{ => _paymentMethodId = id;
_paymentMethodId = id;
}
public void SetBuyerId(int id) public void SetBuyerId(int id)
{ => _buyerId = id;
_buyerId = id;
}
public void SetAwaitingValidationStatus() public void SetAwaitingValidationStatus()
{ {
if (_orderStatusId == OrderStatus.Submitted.Id) if (_orderStatusId == OrderStatus.Submitted.Id)
{ {
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems)); AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id; _orderStatusId = OrderStatus.AwaitingValidation.Id;
} }
} }
public void SetStockConfirmedStatus() public void SetStockConfirmedStatus()
{ {
if (_orderStatusId == OrderStatus.AwaitingValidation.Id) if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{ {
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
_orderStatusId = OrderStatus.StockConfirmed.Id; _orderStatusId = OrderStatus.StockConfirmed.Id;
_description = "All the items were confirmed with available stock."; _description = "All the items were confirmed with available stock.";
} }
} }
public void SetPaidStatus() public void SetPaidStatus()
{ {
if (_orderStatusId == OrderStatus.StockConfirmed.Id) if (_orderStatusId == OrderStatus.StockConfirmed.Id)
{ {
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems)); AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
_orderStatusId = OrderStatus.Paid.Id; _orderStatusId = OrderStatus.Paid.Id;
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\""; _description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
} }
} }
public void SetShippedStatus() public void SetShippedStatus()
{ {
if (_orderStatusId != OrderStatus.Paid.Id) if (_orderStatusId != OrderStatus.Paid.Id)
{ {
StatusChangeException(OrderStatus.Shipped); StatusChangeException(OrderStatus.Shipped);
} }
_orderStatusId = OrderStatus.Shipped.Id; _orderStatusId = OrderStatus.Shipped.Id;
_description = "The order was shipped."; _description = "The order was shipped.";
AddDomainEvent(new OrderShippedDomainEvent(this)); AddDomainEvent(new OrderShippedDomainEvent(this));
} }
public void SetCancelledStatus() public void SetCancelledStatus()
{ {
if (_orderStatusId == OrderStatus.Paid.Id || if (_orderStatusId == OrderStatus.Paid.Id ||
_orderStatusId == OrderStatus.Shipped.Id) _orderStatusId == OrderStatus.Shipped.Id)
{ {
StatusChangeException(OrderStatus.Cancelled); StatusChangeException(OrderStatus.Cancelled);
} }
_orderStatusId = OrderStatus.Cancelled.Id; _orderStatusId = OrderStatus.Cancelled.Id;
_description = $"The order was cancelled."; _description = $"The order was cancelled.";
AddDomainEvent(new OrderCancelledDomainEvent(this)); AddDomainEvent(new OrderCancelledDomainEvent(this));
} }
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems) public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
{ {
if (_orderStatusId == OrderStatus.AwaitingValidation.Id) if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{ {
_orderStatusId = OrderStatus.Cancelled.Id; _orderStatusId = OrderStatus.Cancelled.Id;
var itemsStockRejectedProductNames = OrderItems var itemsStockRejectedProductNames = OrderItems
.Where(c => orderStockRejectedItems.Contains(c.ProductId)) .Where(c => orderStockRejectedItems.Contains(c.ProductId))
.Select(c => c.GetOrderItemProductName()); .Select(c => c.GetOrderItemProductName());
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames); var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
_description = $"The product items don't have stock: ({itemsStockRejectedDescription})."; _description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
} }
} }
private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber, private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber,
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) string cardSecurityNumber, string cardHolderName, DateTime cardExpiration)
{ {
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId, var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId,
cardNumber, cardSecurityNumber, cardNumber, cardSecurityNumber,
cardHolderName, cardExpiration); cardHolderName, cardExpiration);
this.AddDomainEvent(orderStartedDomainEvent); AddDomainEvent(orderStartedDomainEvent);
} }
private void StatusChangeException(OrderStatus orderStatusToChange) private void StatusChangeException(OrderStatus orderStatusToChange)
{ => throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
}
public decimal GetTotal() public decimal GetTotal()
{ => _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice()); }
}
}
} }

View File

@ -1,83 +1,76 @@
using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork; using Microsoft.eShopOnContainers.Services.Ordering.Domain.Seedwork;
using Ordering.Domain.Exceptions; using Ordering.Domain.Exceptions;
using System;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate
{ {
public class OrderItem public class OrderItem
: Entity : Entity
{ {
// DDD Patterns comment // DDD Patterns comment
// Using private fields, allowed since EF Core 1.1, is a much better encapsulation // 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) // aligned with DDD Aggregates and Domain Entities (Instead of properties and property collections)
private string _productName; private readonly string _productName;
private string _pictureUrl; private readonly string _pictureUrl;
private decimal _unitPrice; private readonly decimal _unitPrice;
private decimal _discount; private decimal _discount;
private int _units; private int _units;
public int ProductId { get; private set; } public int ProductId { get; private set; }
protected OrderItem() { } protected OrderItem() { }
public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string PictureUrl, int units = 1) 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"); throw new OrderingDomainException("Invalid number of units");
} }
if ((unitPrice * units) < discount) if ((unitPrice * units) < discount)
{ {
throw new OrderingDomainException("The total of order item is lower than applied discount"); throw new OrderingDomainException("The total of order item is lower than applied discount");
} }
ProductId = productId; ProductId = productId;
_productName = productName; _productName = productName;
_unitPrice = unitPrice; _unitPrice = unitPrice;
_discount = discount; _discount = discount;
_units = units; _units = units;
_pictureUrl = PictureUrl; _pictureUrl = PictureUrl;
} }
public string GetPictureUri() => _pictureUrl; public string GetPictureUri() => _pictureUrl;
public decimal GetCurrentDiscount() public decimal GetCurrentDiscount()
{ => _discount;
return _discount;
}
public int GetUnits() public int GetUnits()
{ => _units;
return _units;
}
public decimal GetUnitPrice() public decimal GetUnitPrice()
{ => _unitPrice;
return _unitPrice;
}
public string GetOrderItemProductName() => _productName; public string GetOrderItemProductName() => _productName;
public void SetNewDiscount(decimal discount) public void SetNewDiscount(decimal discount)
{ {
if (discount < 0) if (discount < 0)
{ {
throw new OrderingDomainException("Discount is not valid"); throw new OrderingDomainException("Discount is not valid");
} }
_discount = discount; _discount = discount;
} }
public void AddUnits(int units) public void AddUnits(int units)
{ {
if (units < 0) if (units < 0)
{ {
throw new OrderingDomainException("Invalid units"); throw new OrderingDomainException("Invalid units");
} }
_units += units; _units += units;
} }
} }
} }