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
private bool _isDraft;
// DDD Patterns comment // 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
// Using a private collection field, better for DDD Aggregate's encapsulation private bool _isDraft;
// 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; // 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;
public static Order NewDraft() private int? _paymentMethodId;
{
var order = new Order();
order._isDraft = true;
return order;
}
protected Order() { public static Order NewDraft()
_orderItems = new List<OrderItem>(); {
_isDraft = false; var order = new Order
} {
_isDraft = true
};
return order;
}
public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber, protected Order()
string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this() {
{ _orderItems = new List<OrderItem>();
_buyerId = buyerId; _isDraft = false;
_paymentMethodId = paymentMethodId; }
_orderStatusId = OrderStatus.Submitted.Id;
_orderDate = DateTime.UtcNow;
Address = address;
// Add the OrderStarterDomainEvent to the domain events collection public Order(string userId, string userName, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
// to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ] string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null) : this()
AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber, {
cardSecurityNumber, cardHolderName, cardExpiration); _buyerId = buyerId;
} _paymentMethodId = paymentMethodId;
_orderStatusId = OrderStatus.Submitted.Id;
_orderDate = DateTime.UtcNow;
Address = address;
// DDD Patterns comment // Add the OrderStarterDomainEvent to the domain events collection
// This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order, // to be raised/dispatched when comitting changes into the Database [ After DbContext.SaveChanges() ]
// so any behavior (discounts, etc.) and validations are controlled by the AggregateRoot AddOrderStartedDomainEvent(userId, userName, cardTypeId, cardNumber,
// in order to maintain consistency between the whole Aggregate. cardSecurityNumber, cardHolderName, cardExpiration);
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) // DDD Patterns comment
{ // This Order AggregateRoot's method "AddOrderitem()" should be the only way to add Items to the Order,
//if previous line exist modify it with higher discount and units.. // 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 (discount > existingOrderForProduct.GetCurrentDiscount()) if (existingOrderForProduct != null)
{ {
existingOrderForProduct.SetNewDiscount(discount); //if previous line exist modify it with higher discount and units..
}
existingOrderForProduct.AddUnits(units); if (discount > existingOrderForProduct.GetCurrentDiscount())
} {
else existingOrderForProduct.SetNewDiscount(discount);
{ }
//add validated new order item
var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units); existingOrderForProduct.AddUnits(units);
_orderItems.Add(orderItem); }
} else
} {
//add validated new order item
public void SetPaymentId(int id) var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);
{ _orderItems.Add(orderItem);
_paymentMethodId = id; }
} }
public void SetBuyerId(int id) public void SetPaymentId(int id)
{ => _paymentMethodId = id;
_buyerId = id;
}
public void SetAwaitingValidationStatus() public void SetBuyerId(int id)
{ => _buyerId = id;
if (_orderStatusId == OrderStatus.Submitted.Id)
{
AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id;
}
}
public void SetStockConfirmedStatus() public void SetAwaitingValidationStatus()
{ {
if (_orderStatusId == OrderStatus.AwaitingValidation.Id) if (_orderStatusId == OrderStatus.Submitted.Id)
{ {
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id)); AddDomainEvent(new OrderStatusChangedToAwaitingValidationDomainEvent(Id, _orderItems));
_orderStatusId = OrderStatus.AwaitingValidation.Id;
}
}
_orderStatusId = OrderStatus.StockConfirmed.Id; public void SetStockConfirmedStatus()
_description = "All the items were confirmed with available stock."; {
} if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
} {
AddDomainEvent(new OrderStatusChangedToStockConfirmedDomainEvent(Id));
public void SetPaidStatus() _orderStatusId = OrderStatus.StockConfirmed.Id;
{ _description = "All the items were confirmed with available stock.";
if (_orderStatusId == OrderStatus.StockConfirmed.Id) }
{ }
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
_orderStatusId = OrderStatus.Paid.Id; public void SetPaidStatus()
_description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\""; {
} if (_orderStatusId == OrderStatus.StockConfirmed.Id)
} {
AddDomainEvent(new OrderStatusChangedToPaidDomainEvent(Id, OrderItems));
public void SetShippedStatus() _orderStatusId = OrderStatus.Paid.Id;
{ _description = "The payment was performed at a simulated \"American Bank checking bank account endinf on XX35071\"";
if (_orderStatusId != OrderStatus.Paid.Id) }
{ }
StatusChangeException(OrderStatus.Shipped);
}
_orderStatusId = OrderStatus.Shipped.Id; public void SetShippedStatus()
_description = "The order was shipped."; {
AddDomainEvent(new OrderShippedDomainEvent(this)); if (_orderStatusId != OrderStatus.Paid.Id)
} {
StatusChangeException(OrderStatus.Shipped);
}
public void SetCancelledStatus() _orderStatusId = OrderStatus.Shipped.Id;
{ _description = "The order was shipped.";
if (_orderStatusId == OrderStatus.Paid.Id || AddDomainEvent(new OrderShippedDomainEvent(this));
_orderStatusId == OrderStatus.Shipped.Id) }
{
StatusChangeException(OrderStatus.Cancelled);
}
_orderStatusId = OrderStatus.Cancelled.Id; public void SetCancelledStatus()
_description = $"The order was cancelled."; {
AddDomainEvent(new OrderCancelledDomainEvent(this)); if (_orderStatusId == OrderStatus.Paid.Id ||
} _orderStatusId == OrderStatus.Shipped.Id)
{
StatusChangeException(OrderStatus.Cancelled);
}
public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems) _orderStatusId = OrderStatus.Cancelled.Id;
{ _description = $"The order was cancelled.";
if (_orderStatusId == OrderStatus.AwaitingValidation.Id) AddDomainEvent(new OrderCancelledDomainEvent(this));
{ }
_orderStatusId = OrderStatus.Cancelled.Id;
var itemsStockRejectedProductNames = OrderItems public void SetCancelledStatusWhenStockIsRejected(IEnumerable<int> orderStockRejectedItems)
.Where(c => orderStockRejectedItems.Contains(c.ProductId)) {
.Select(c => c.GetOrderItemProductName()); if (_orderStatusId == OrderStatus.AwaitingValidation.Id)
{
_orderStatusId = OrderStatus.Cancelled.Id;
var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames); var itemsStockRejectedProductNames = OrderItems
_description = $"The product items don't have stock: ({itemsStockRejectedDescription})."; .Where(c => orderStockRejectedItems.Contains(c.ProductId))
} .Select(c => c.GetOrderItemProductName());
}
private void AddOrderStartedDomainEvent(string userId, string userName, int cardTypeId, string cardNumber, var itemsStockRejectedDescription = string.Join(", ", itemsStockRejectedProductNames);
string cardSecurityNumber, string cardHolderName, DateTime cardExpiration) _description = $"The product items don't have stock: ({itemsStockRejectedDescription}).";
{ }
var orderStartedDomainEvent = new OrderStartedDomainEvent(this, userId, userName, cardTypeId, }
cardNumber, cardSecurityNumber,
cardHolderName, cardExpiration);
this.AddDomainEvent(orderStartedDomainEvent); 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);
private void StatusChangeException(OrderStatus orderStatusToChange) AddDomainEvent(orderStartedDomainEvent);
{ }
throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
}
public decimal GetTotal() private void StatusChangeException(OrderStatus orderStatusToChange)
{ => throw new OrderingDomainException($"Is not possible to change the order status from {OrderStatus.Name} to {orderStatusToChange.Name}.");
return _orderItems.Sum(o => o.GetUnits() * o.GetUnitPrice());
} public decimal GetTotal()
} => _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;
} }
} }
} }