Ben*_*min 8 orm domain-driven-design aggregate aggregateroot
我在SO上读了一个类似的问题:如何更新Aggregate中的实体,但我仍然不确定用户界面应该如何与聚合内的实体进行交互.
假设我有User一堆,有一堆Addresses.用户是聚合根,而地址仅存在于聚合中.
在Web界面上,用户可以编辑他的地址.基本上,会发生什么:
edit-address?user=1&address=2我决定绕过聚合根,这很简单:
Address它Id因为我们想用DDD方式做,所以我们有不同的解决方案:
我们要求用户通过Id获取此地址:
address = user.getAddress(id);
address.setPostCode("12345");
address.setCity("New York");
em.persist(user);
这种方法的问题是,IMO,聚合根仍然没有更多的控制地址的作用.它只返回对它的引用,因此与绕过聚合没有太大区别.
或者我们告诉聚合更新现有地址:
user.updateAddress(id, "12345", "New York");
em.persist(user);
现在,聚合控制了对此地址所做的操作,并且可以采取任何必要的操作来更新地址.
或者我们将地址视为值对象,我们不更新我们的Address,而是删除它并重新创建它:
user.removeAddress(id);
address = new Address();
address.setPostCode("12345");
address.setCity("New York");
user.addAddress(address);
em.persist(user);
最后一个解决方案看起来很优雅,但意味着地址不能是实体.然后,如果需要将其视为实体,例如因为聚合中的另一个业务对象具有对它的引用,该怎么办?
我很确定我在这里遗漏了一些东西,以正确理解聚合概念以及它如何在现实生活中使用,所以请不要犹豫,发表您的意见!
不,你不会错过任何东西-在大多数情况下,最好的办法是2号(虽然我会调用该方法changeAddress,然后updateAdress-更新显得那么不-DDD),这就是一个地址无论是实体或值对象.使用无处不在的语言,你宁愿说用户改变了他的地址,这正是你应该如何建模它的changeAddress方法- 它是决定更新属性(如果地址是实体)还是分配全新对象(当它是VO时)的方法.
以下示例代码假定最常见的方案 - Address as VO:
    public void ChangeAddress(AddressParams addressParams)
    {
        // here we might include some validation
        address = new Address(addressParams);
        // here we might include additional actions related with changing address
        // for example marking user as required to confirm address before
        // next billing
    }
在此示例中重要的是,一旦创建了Address,它就被认为是有效的 - 聚合中不能有无效的Address对象.但是,请注意,您是否应该遵循此示例取决于您的实际领域 - 没有一条路可以遵循.这是最常见的一个.
是的,你应该总是通过遍历聚合根来对你的实体进行操作 - 在SO的许多答案中给出了这个的原因(例如在这个 基本聚合问题中).
某事物是实体还是VO取决于要求和您的域名.大多数时间地址只是一个值对象,因为具有相同值的两个地址之间没有区别,并且地址在其生命周期中往往不会改变.但同样,这是大部分时间,取决于你正在建模的领域.
另一个例子 - 对于大多数域而言,a Money将是一个Value Object  -  10 $是10 $,除了amount之外它没有任何身份.但是,如果你建立一个在账单水平上处理货币的域,每个账单都有自己的身份(用某种特殊的数字表示),因此它就是一个实体.