40

假设我们有受 CQRS 启发的架构,其中包含命令、领域模型、领域事件、读取模型 DTO 等组件。
当然,我们可以在我们的领域模型中使用值对象。我的问题是,它们是否也可以用于:

  1. 命令
  2. 活动
  3. DTO

我还没有看到在上述组件中使用值对象 (VO) 的任何示例。相反,使用原始类型。也许这只是简单的例子。毕竟,我对在 DDD 中使用 VO 的理解是它们充当了整个应用程序的粘合剂。

我的动机:

命令。
假设用户提交了一个包含地址字段的表单。我们有地址值对象来表示这个概念。在客户端构建命令时,无论如何我们都应该验证用户输入,当它格式正确时,我们可以在那里创建地址对象并用它初始化命令。我认为无需将 Address 对象的创建委托给命令处理程序。

领域事件。
领域模型已经根据值对象进行操作,因此通过使用 VO 发布事件而不是将它们转换为原始类型,我们可以避免一些映射代码。我很确定在这种情况下使用 VO 是可以的。

DTO。
如果我们的查询端 DTO 可以包含值对象,则可以提供更多的灵活性。例如,如果我们有 Money 对象,我们可以选择以 EUR 或 USD 显示它,无需更改读取模型。

4

5 回答 5

28

好吧,我改变主意了。我最近一直在尝试处理 VO,在观看了这个http://www.infoq.com/presentations/Value-Objects-Dan-Bergh-Johnsson之后,它为我澄清了一些事情。

命令和事件是消息(而不是对象,对象是数据+行为),在某些方面很像 DTO,它们传达有关事件的数据,它们本身不封装任何行为。

值对象根本不像 DTO。它们是域表示,一般来说,它们像所有其他域表示一样具有丰富的行为。

命令和事件分别将信息传入和传出域,但它们本身并不封装任何行为。从这个角度来看,在其中传递 VO 似乎是错误的,并且可能违反了上下文边界。

套用 Oren(尽管他指的是 nHibernate 和 WCF)“不要通过网络发送您的域”。 http://ayende.com/Blog/archive/2009/05/14/the-stripper-pattern.aspx

如果你想传达一个值对象,那么我建议传递必要的属性来在它们中重建 VO。

原文(供后人):

如果您询问值对象是否可以通过域模型传递给事件或通过命令传递,我真的不认为前者有什么大问题,尽管后者可能违反聚合根的某些规则价值观的“拥有者”。

也就是说,值对象表示诸如颜色之类的概念。你没有绿色,你是否绿色的。一个命令告诉你通过这个命令你是绿色的,这似乎没有本质上的错误。

阅读 DDD 关于聚合根模式的章节很好地解释了实体和值对象,值得多读几遍。

于 2011-02-07T17:07:49.553 回答
5

我说这是个坏主意。

我们不对实体做同样的事情是有原因的——以避免将系统的其他部分耦合到域(在错误的地方)。值对象也是如此,值对象和实体之间的唯一区别是生命周期和所有权——这些区别不会影响我们应该如何耦合它们,也不应该如何耦合它们。

想象一下,你让一个事件包含一个 VO。您的域中的更改需要您更改该 VO。你现在已经把自己限制在一个角落里,你的事件也被迫改变,对于它所属的任何命令或 DTO 也是如此。

这是要避免的。

使用 DTO 和/或原语。映射它们(AutoMapper 使其成为单行交易)。

于 2011-02-02T00:35:55.037 回答
3

与其他答案类似,在 SOA 中,这会破坏服务的封装,因为域现在正在泄漏。

于 2011-02-17T03:33:24.507 回答
0

根据Clean Code ,您的 DTO 是数据结构(只是添加另一个术语),而值对象是对象。对象可以有行为的区别。将数据结构与对象混合通常是一个非常糟糕的主意,因为很难维护你得到的混合。

从架构的角度来看,我也觉得将值对象放入 DTO 是不正确的。值对象位于域模型内部,而您提到的 DTO 正在定义模型的接口。我们通常构建一个接口来将外部世界与内部世界解耦。因此,在当前情况下,我们添加了 DTO 以将外部世界与值对象(以及其他与模型相关的东西)分离。之后向接口添加值对象是疯狂的。

所以你还没有遇到这个解决方案,因为它是一种反模式。

于 2014-09-25T05:33:48.687 回答
0

值对象是或至少应该是不可变的。一旦用一个值实例化,该值将在对象的整个生命周期内永远不会改变。因此,将 VO 作为数据传递给 DTO(例如事件)应该不是问题,因为您可以对它们做的就是获取它们的值。最多以不同的表示形式表示它们的值,例如与可能返回整数或任何值toString()的原始值相反。getValue()

于 2019-12-13T12:47:46.307 回答