我尝试为第一个DDD项目设计。我已经在BasketItem中创建了一个方法来更改购物篮中当前商品的数量,只有在仓库中有足够的商品时,才能更改数量。
我在应用程序服务中进行此检查,但我不喜欢,因为BasketItem.ChangeQuantity是公共方法,任何人都可以调用此方法而无需检查数量。
为了防止输入错误的数量,我想将控件移至ChangeQuantity。这个解决方案正确吗?考虑到SOLID,该方法将承担太多责任?
您推荐哪条路?
public class Basket : Entity, IAggregateRoot
{
public long BasketId { get; protected set; }
public long UserId { get; protected set; }
private List<BasketItem> _basketItems;
public virtual IReadOnlyCollection<BasketItem> BasketItems => basketItems?.ToList();
}
protected Basket() { }
public class BasketItem : Entity
{
public Guid Guid { get; protected set; }
public decimal Price { get; protected set; }
public int Quantity { get; protected set; }
public virtual Basket Basket { get; protected set; }
public virtual Product Product { get; protected set; }
protected BasketItem() { }
public IStatusGeneric ChangeQuantity(int quantity)
{
var errorStatus = new StatusGenericHandler();
//How to get avaiable quantity from WarehouseProduct?????????
//var avaiable = Basket.Warehouse.ProductQuantityAvaiable(Product);
if (avaiable < quantity)
{
var errorStatusMessage = $"There are only {avaiable} avaiable product of {Product.Code}.";
if (productQuantityInBasket > 0)
{
errorStatusMessage += $"Current basket have already {productQuantityInBasket}";
}
errorStatus.AddError(errorStatusMessage);
return errorStatus;
}
haveProduct.ChangeQuantity(quantity);
return errorStatus;
}
}
public class Warehouse : Entity, IAggregateRoot
{
public long WarehouseId { get; protected set; }
public string Name { get; protected set; }
private List<WarehouseProduct> _warehouseProducts;
public virtual IReadOnlyCollection<WarehouseProduct> WarehouseProducts => _warehouseProducts?.ToList();
}
public class WarehouseProduct : Entity
{
public Guid Guid { get; protected set; }
public int Quantity { get; protected set; }
public virtual Warehouse Warehouse { get; protected set; }
public virtual Product Product { get; protected set; }
private WarehouseProduct() { }
public void ChangeQuantity(int deltaQuantity)
{
Quantity += deltaQuantity;
}
}
第二个解决方案是将仓库带入篮球场。这样,我可以访问仓库的所有元素以查找可用数量。这也使我可以将篮球与特定的仓库关联,以便我可以管理多个仓库
public class Basket : Entity, IAggregateRoot
{
public long BasketId { get; protected set; }
public long UserId { get; protected set; }
public Warehouse Warehouse { get; protected set; }
private List<BasketItem> _basketItems;
public virtual IReadOnlyCollection<BasketItem> BasketItems => _basketItems?.ToList();
}
如何传递允许AR像basket.changeItemQuantity(itemId, quantity, inventoryService)
中那样查询给定项目的库存的服务?
有些人不喜欢这种风格,会更喜欢在应用程序服务中解析清单,然后将其提供给AR,例如basket.changeItemQuantity(itemId, qty, qtyOnHand)
。我更喜欢这里的第一种方法,尽管将稍微涉及单元测试。
第三种方法是实现域服务以协调逻辑。您将当前在应用程序服务中拥有的域逻辑提取到该域服务,并将用例处理从应用程序服务委托给域服务。仅凭该规则可能就算过高了,只会使您趋向贫血模型。
关于您的设计建议的几点评论:
您不应该分发非根实体引用。篮子应提供更改数量的界面。
从篮子式AR引用仓库AR的第二个建议也将违反AR设计规则:AR应仅按ID引用其他AR。这有助于使AR保持较小且集中的状态,并让开发人员查看每个AR真正拥有和保护的数据。您也将不太想在同一笔交易中更改两个AR。
从AR边界外部引用的数据被认为是陈旧的。您可能想探索将商品添加到购物篮中的情况,以便稍后发现库存已经耗尽。那你怎么办呢?考虑这些场景将指导您建立一个更加面向业务的模型,该模型可以处理这种复杂性,而不是试图使所有事物都成为一个高度一致的不变式,而最终会导致一个不切实际的模型与域需求不符。
好,关于DDD的问题。还有一些非常有趣的讨论。
我目前正在编写Shop应用程序,因此这对我和我的团队来说都是一个非常有用的问题。
我首先要说的是,您应该与仓库,销售和管理团队进行讨论,以更好地了解这些情况。如果有2个销售人员同时致电仓库,并询问当前的库存以及他们是否可以出售,这将对您的实现产生重大影响。
一般性讨论
这是我的技术观点(很乐意进一步讨论)...@plalx有一些很棒的主意,我认为它实际上介于两者之间。
您真的只想问一个问题-我可以将此数量的这个物品添加到这个购物篮中吗...
Basket basket = _basketStore.Get(1);
basket.ChangeQuantity(item, quantity, availability);
//定义
public interface IAvailabilityProcessor {
bool AvailableForBasket(item, quantity);
}
public class Basket {
public void ChangeQuantity (Item item, int quantity, IAvailabilityProcessor processor) {
var canBeAdded = processor.AvailableForBasket(item, quantity);
if (canBeAdded) {
this.Add(item, quantity);
}
}
[我通常可以这样做,花一些时间谈论这些事情的确切实现,但是我现在就这样说-请反馈或提出问题