在实施DDD时保护聚合根的不变量和子代

时间:2015-10-23 20:58:17

标签: domain-driven-design

在我开始学习DDD时,我开始使用一个简单的域模型,随着时间的推移,我会逐渐建立起来。我在这个例子中的域名是通常的顺序>订购商品是为了让事情变得简单,并且可以在以后添加发票等。这是我到目前为止所拥有的:

public class Order
{
    private readonly IList<OrderItem> _orderItems;

    public Guid Id { get; private set; }
    public bool Completed { get; private set; }
    public DateTime Created { get; private set; }
    public IEnumerable<OrderItem> OrderItems
    {
        get { return _orderItems; }
    }

    public Order()
    {
        Id = new Guid();
        Created = DateTime.UtcNow;
        _orderItems = new List<OrderItem>();
    }

    public void AddOrderItem(int quantity, int unitCost)
    {
        var orderItem = new OrderItem(quantity, unitCost);
        _orderItems.Add(orderItem);
    }

    public void CompleteOrder()
    {
        Completed = true;
    }
}

public class OrderItem
{
    public int Quantity { get; private set; }
    public int UnitCost { get; private set; }

    public OrderItem(int quantity, int unitCost)
    {
        Quantity = quantity;
        UnitCost = unitCost;
    }
}

我最终会将Quantity和UnitCost转换为值对象,但这不是重要的部分。正如DDD所说的那样,我们总是希望保护我们的不变量,但我对其中的一部分有点麻烦。从订单中,您可以通过调用AddOrderItem()方法并传递数量和单位成本来添加新的OrderItem。

我现在的问题是什么阻止另一个程序员用var orderItem = new OrderItem(1, 2)创建一个新的OrderItem? OrderItem构造函数可能应该有一个Order order参数,因为没有Order就不能存在OrderItem,但现在其他编码器只能调用新的OrderItem(new Order(),1,2)?

我错过了什么吗?或者只是接受了使用该模型的团队需要了解DDD的基本原理?

更新

感谢@theDmi,@ guillaume31,@ May,因为你们都提供了一些好处。我认为在这一点上非常清楚,存储库的界面应该足以清楚地表明你不能对自己创建的OrderItem做任何事情。将OrderItem的ctor设置为internal也有助于强制执行此限制,但可能不需要。我计划看看有没有内部ctor会发生什么。最终,我接受@ guillaume31的答案的原因是关于双向关系的评论。这很有道理,我过去曾经在EF中遇到过这个问题,所以我喜欢把它保持单边的想法。

2 个答案:

答案 0 :(得分:5)

使用DDD时,所有更改系统状态的尝试都会通过存储库运行,因为您需要首先检索要处理的聚合。 因此,即使某人创建的对象在某个实体之外没有任何意义,他们也无法对其进行任何有用的操作。

关于这个问题,DDD甚至优于基于CRUD的系统:它具有很高的可发现性。首先,存储库界面会告诉您可以加载的内容。然后你得到一个聚合,它反过来提供以有意义的方式修改聚合的操作。

答案 1 :(得分:5)

&#34; 如果没有OrderItem &#34; Order就不能存在实际上并不是一个不变的。好吧,至少它不是Order聚合中的不变量。根据定义,不变量只会查看内部一个聚合(或跨越多个聚合)的内容,而不是在聚合外部游荡的内容。

  

OrderItem构造函数可能应该有一个Order订单   参数,因为OrderItem在没有Order

的情况下不能存在

我不会那样建模,因为

  • 不建议实体之间的双向关系。它可能导致同步问题(A指向B但B指向其他东西),如果可以的话,最好有单向关系。

  • 通过这样做,你的最终目标是限制聚合之外发生的事情,这不是DDD的重点,而且正如其他答案所显示的那样,是可有可无的。 DDD系统中的所有更改都通过聚合和存储库。