首页 > 解决方案 > C# DDD:更改篮子项目数量(如果可用)

问题描述

我尝试按照第一个 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();
}

标签: domain-driven-designsingle-responsibility-principle

解决方案


传递一个允许 AR 查询给定项目库存的服务怎么样basket.changeItemQuantity(itemId, quantity, inventoryService)

有些人不喜欢这种风格,他们更喜欢在应用程序服务中解析库存,然后将其提供给 AR,例如basket.changeItemQuantity(itemId, qty, qtyOnHand). 我更喜欢这里的第一种方法,尽管单元测试会稍微多一些。

第三种方法是实现域服务来编排逻辑。您可以将您当前在应用程序服务中的域逻辑提取到该域服务,并将用例处理从应用程序服务委托给域之一。对于这条规则来说,这可能是矫枉过正,只会把你推向一个贫血的模型。

关于您的设计建议的一些评论:

  • 您不应该分发非根实体引用。篮子应该提供改变数量的接口。

  • 从 Basket AR 中引用仓库 AR 的第二个提议也违反了 AR 设计规则:AR 只能通过 id 引用其他 AR。这有助于让 AR 保持小而专注,并让开发人员看到每个 AR 真正拥有和保护哪些数据。您也不太愿意在同一事务中更改两个 AR。

  • 从 AR 边界之外引用的数据将被视为陈旧。您可能想探索将商品添加到购物篮的场景,但片刻之后才发现库存已经用完。那你怎么办呢?考虑这些场景将引导您走向一个更加面向业务的模型,该模型可以处理这些错综复杂的问题,而不是试图让一切都成为一个强一致的不变量,最终导致一个不切实际的模型与领域需求不一致。


推荐阅读