DDD和值对象。可变值对象是否适合使用非Aggr。根实体?


9

这是一个小问题

有一个带有值对象的实体。没问题 我将值对象替换为新值对象,然后nhibernate插入新值并孤立旧值,然后将其删除。好,那是个问题。

被保险人是我在我域中的实体。他有一组地址(值对象)。地址之一是MailingAddress。当我们要更新邮寄地址时,假设邮编是错误的,按照埃文斯先生的学说,我们必须将旧对象替换为新对象,因为它是不可变的(值对象对吗?)。

但是我们不想删除您的行,因为该地址的PK是MailingHistory表中的FK。因此,按照埃文斯先生的学说,我们在这里几乎陷入了困境。除非我将地址设为实体,否则就不必“替换”它,只需像过去的美好时光那样更新其邮政编码成员即可。

在这种情况下,您会建议我什么?从我的角度来看,ValueObjects仅在您希望封装一组数据库表的列(nhibernate中的组件)时才有用。最好在数据库中具有持久性ID的所有对象都设置为实体(不一定是聚合根),这样您就可以在不重新创建整个对象图的情况下更新其成员,特别是如果这是一个深层嵌套的对象。

你同意吗?埃文斯先生是否允许使用可变的价值对象?还是可变值对象是实体的候选对象?

谢谢


2
有“可变值对象”这样的东西吗?我一直有印象值对象是不可变的。
herby 2012年

@herby我想您可能在代码中有一个可变对象,表示一个DDD值对象,但是您必须考虑到,一旦对该对象进行突变,它就不再引用相同的逻辑DDD值对象,而是一个新对象。这可能是合乎需要的,但这是导致混淆的秘诀-使值对象在代码中不可变是一种明智的约定。
MattDavey 2012年

Answers:


8

拥有身份的所有事物都应该是一个实体,而没有身份的所有事物都应该是一个简单的值,因此是一个价值对象。

引用马丁·福勒Martin Fowler)的话(反过来qoutes Eric Evans)

  • 实体:具有通过时间和不同表示形式运行的独特标识的对象。您还会听到这些称为“参考对象”的信息。
  • 价值对象:重要的对象仅具有其属性的组合。

将您的地址设为值对象的原因:

如果您的地址是可变的,那么最终您可能会弄糟您的邮件记录。例如,如果您要将商品运送给客户,则如果MailingHistory表所引用的地址已更改,则无法确定过去实际运送商品的地址。

MailingHistory条目我们将A764运送到地址657可能意味着我们昨天将A764运送到了波士顿,明天将A764运送到了纽约

如果必须更改邮寄地址,则无需删除旧地址。保留它,并将其标记为inactive,将新的标记为active


当然,您可以将您的地址视为实体,但是只有在更新地址时,它才不会更改该地址所指的实际位置,因此只能纠正拼写错误。

如果您确定可以确保,那么可以使用实体。


但是,恕我直言,最好的解决方案是不要在邮件历史记录中引用地址实体,而是直接在邮件历史记录表中保存特定地址(基本上是复制地址数据)。

这样,您就始终知道将您的东西(或您要邮寄的任何东西)运送到哪里,并且由于您将使用可变实体,因此地址表不会混乱。

我已经使用过或使用了多个ERP系统,几乎所有的系统都使用了这种方法。

您的数据库中会有一些冗余,但这是恕我直言的最实用方法。


这可能是最无头痛的解决方案。仅当您期望将来的通信渠道将需要额外的列并且您的数据库太大而无法ALTER使用时,才有必要在单独的表中使用实体。反过来,这就要求在您的查询中使用诸如“始终加入最新的地址/电话/电子邮件”之类的策略SELECT,这对于保持可维护性高效性具有挑战性。尽可能保持简单。
Timo

如果仅通过添加active-flag 稍微规范化数据,则@Timo“始终加入最新的地址/电话/电子邮件”并不难。当然,您必须确保始终and active = true在Joins中使用该标志,并使其保持最新状态,并在表中添加约束,以便例如每个客户只有一封电子邮件可以将此标志设置为true。
懒惰

这引入了停用前一个的问题。如果您已用代码替换了实例的“当前”地址对象,然后转到数据访问代码,则它将不知道(1)是否有一个新对象,或者(2)潜在的旧对象一个是。因此,每个保存操作都必须做一些复杂的事情,例如“先去激活数据库中所有相关的地址”,然后再用保存当前的地址active=true。这不是我所说的简单,这就是为什么我喜欢您的解决方案。
Timo

2

我看到两件事:

  1. 更改邮政编码可以影响历史记录是否可以?我认为历史记录指向旧的,未更改的地址是合乎逻辑的,因此您知道将其发送到错误的地址。

  2. MailingHistory在该地址上具有FK的那一刻,该地址就不再是值对象,而成为实体。值对象没有身份,允许其他实体引用此身份。您可以在一个表中有地址,并在其中指向其他表,但是唯一的效果就是节省了空间。从域的角度来看,如果两个实体引用了相同类型的值对象,则它们不会共享任何类型的信息。


2

IMO地址对象是您域中的实体。它由多个实体共享,具有自己的身份,并且在整个系统中是唯一的。

埃文斯说:

主要由其身份定义的对象称为实体。


据我所知,域身份与持久性身份无关。根据埃文先生的书。
Pepito Fernandez

你是对的。我编辑我的答案。我的意思是,对象Address在此特定域中确实很重要,这是唯一的。IMO外键和主键是一个标志,它实际上是整个域中的唯一对象,因此具有标识。
margabit

1
“地址对象……具有自己的身份” –地址的哪个属性可以唯一地标识它?地址的单个属性都不是唯一的,但是属性的组合可以用作标识。这就是值对象的
MattDavey

@MattDavey:这是一个很好的结论,但是当Tony说“我们不想删除该行,因为该地址的PK是MailingHistory表中的FK”时,我感到困惑。对我来说,这意味着Address对象在“被保险人”集合之外也具有含义。这说明“地址”对象不应是ValueObject。你怎么看?
margabit

我们是否可以说Value Objects一定是父公司的全资拥有组合(UML)?另外,如果没有父对象,值对象就毫无意义,并且不能在父对象之间共享?
Sudarshan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.