更新聚合内的实体

Ben*_*min 8 orm domain-driven-design aggregate aggregateroot

我在SO上读了一个类似的问题:如何更新Aggregate中的实体,但我仍然不确定用户界面应该如何与聚合内的实体进行交互.

假设我有User一堆,有一堆Addresses.用户是聚合根,而地址仅存在于聚合中.

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

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

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

  • 我们会直接加载AddressId
  • 我们会更新它,然后保存它

因为我们想用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);

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

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

kst*_*uch 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
    }
Run Code Online (Sandbox Code Playgroud)

在此示例中重要的是,一旦创建了Address,它就被认为是有效的 - 聚合中不能有无效的Address对象.但是,请注意,您是否应该遵循此示例取决于您的实际领域 - 没有一条路可以遵循.这是最常见的一个.

是的,你应该总是通过遍历聚合根来对你的实体进行操作 - 在SO的许多答案中给出了这个的原因(例如在这个 基本聚合问题中).

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

另一个例子 - 对于大多数域而言,a Money将是一个Value Object - 10 $是10 $,除了amount之外它没有任何身份.但是,如果你建立一个在账单水平上处理货币的域,每个账单都有自己的身份(用某种特殊的数字表示),因此它就是一个实体.