更新聚合内的实体

时间:2011-08-25 20:33:47

标签: orm domain-driven-design aggregate aggregateroot

我正在SO上阅读类似的问题:How update an entity inside Aggregate,但我仍然不确定用户界面应该如何与聚合内的实体进行交互。

假设我有一个User,有一堆Address个es。用户是聚合根,而地址仅存在于聚合中。

在网络界面上,用户可以编辑他的地址。基本上,会发生什么:

  • 用户在其网络界面上看到地址列表
  • 他点击了一个地址,并被重定向到此页面:edit-address?user=1&address=2
  • 在这个页面上,他得到一个表格,他可以修改这个地址。

我决定绕过聚合根,这很简单:

  • 我们会直接加载Address及其Id
  • 我们会更新它,然后保存它

因为我们想用DDD方式做,所以我们有不同的解决方案:

  1. 我们要求用户通过ID 获取此地址:

    address = user.getAddress(id);
    address.setPostCode("12345");
    address.setCity("New York");
    em.persist(user);

    这种方法的问题是,IMO,聚合根仍然没有更多的控制地址的作用。它只返回对它的引用,因此与绕过聚合没有太大区别。

  2. 或者我们告诉汇总更新现有地址

    user.updateAddress(id, "12345", "New York");
    em.persist(user);

    现在,汇总可以控制使用此地址完成的操作,并可以采取任何必要的操作来更新地址。

  3. 或者我们将地址视为值对象,我们不会更新我们的Address,而是删除它并重新创建

    user.removeAddress(id);
    address = new Address();
    address.setPostCode("12345");
    address.setCity("New York");
    user.addAddress(address);
    em.persist(user);

    这最后一个解决方案看起来很优雅,但意味着地址不能是实体。然后,如果需要将视为实体,例如因为聚合中的另一个业务对象具有对它的引用,该怎么办?

  4. 我很确定我在这里遗漏了一些东西,无法正确理解聚合概念以及如何在现实生活中使用它,所以请不要犹豫,发表您的意见!

1 个答案:

答案 0 :(得分:6)

不,你没有遗漏任何东西 - 在大多数情况下,最好的选择是数字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的许多答案中给出了这个的原因(例如在这个Basic Aggregate Question中)。

某些内容是实体还是VO取决于要求和您的域。大多数时间地址只是一个值对象,因为具有相同值的两个地址之间没有区别,并且地址在其生命周期中往往不会改变。但同样,这是大部分时间,取决于你正在建模的领域。

另一个例子 - 对于大多数域,Money将是一个值对象 - 10 $是10 $,除了金额之外它没有身份。但是,如果你建立一个在账单水平上处理货币的域名,那么每个账单都有自己的身份(用某种独特的数字表示),因此它就是一个实体。