我正在SO上阅读类似的问题:How update an entity inside Aggregate,但我仍然不确定用户界面应该如何与聚合内的实体进行交互。
假设我有一个User
,有一堆Address
个es。用户是聚合根,而地址仅存在于聚合中。
在网络界面上,用户可以编辑他的地址。基本上,会发生什么:
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);
这最后一个解决方案看起来很优雅,但意味着地址不能是实体。然后,如果需要将视为实体,例如因为聚合中的另一个业务对象具有对它的引用,该怎么办?
我很确定我在这里遗漏了一些东西,无法正确理解聚合概念以及如何在现实生活中使用它,所以请不要犹豫,发表您的意见!
答案 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 $,除了金额之外它没有身份。但是,如果你建立一个在账单水平上处理货币的域名,那么每个账单都有自己的身份(用某种独特的数字表示),因此它就是一个实体。