CQRS中的值对象 - 在哪里使用

时间:2011-02-02 00:14:14

标签: domain-driven-design dto cqrs value-objects

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

  1. 命令
  2. 活动
  3. DTO的
  4. 我还没有看到任何在上述组件中使用Value Objects(VO)的示例。相反,使用原始类型。也许这只是简单的例子。毕竟,我对DDD中使用VO的理解是它们可以作为整个应用程序的粘合剂。

    我的动机:

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

    域名活动。
    域模型已经在值对象方面运行,因此通过使用VO发布事件而不是将它们转换为基本类型,我们可以避免使用某些映射代码。我很确定在这种情况下使用VO是可以的。

    的DTO。
    如果我们的查询端DTO可以包含值对象,则可以提供更多灵活性。例如,如果我们有Money对象,我们可以选择是以EUR还是USD显示,不需要更改Read Model。

5 个答案:

答案 0 :(得分:25)

好的,我改变了主意。我最近一直试图与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所需的必要属性。

原文(后代):

如果你问的是域模型是否可以将值对象传递给事件或者通过命令传入,我实际上并没有看到前者的巨大问题,尽管后者可能违反聚合的某些规则root是价值的“所有者”。

表示值对象表示诸如颜色之类的概念。您 绿色, 绿色或不绿色。通过这个命令告诉你你是绿色的命令似乎没有什么本质上的错误。

从聚合根模式的DDD中读取章节很好地解释了实体和值对象,值得阅读几次。

答案 1 :(得分:4)

我说这是个坏主意。

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

想象一下,您创建的活动包含一个VO。您域中的更改要求您更改该VO。你现在已经把自己装进了一个角落,你的事件也被强制改变了,同样适用于任何命令或DTO,它都是它的一部分。

这是应该避免的。

使用DTO和/或基元。映射它们(AutoMapper使它成为一线交易)。

答案 2 :(得分:3)

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

答案 3 :(得分:0)

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

我也不想从架构的角度将值对象放到DTO中。值对象在域模型内部,而您提到的DTO定义了模型的接口。我们通常会构建一个界面来将外部世界与某些东西分开。因此,在目前的情况下,我们添加了DTO以将外部世界与值对象(以及其他模型相关的东西)分离。之后,向界面添加值对象是疯狂的。

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

答案 4 :(得分:0)

值对象是或至少应该是不可变的。一旦用一个值实例化,该值将在对象的整个生命周期中永远不变。因此,将VO作为数据传递到DTO(例如“事件”)应该不是问题,因为您所能做的就是获得它们的价值。最多它们的值以不同的表示形式(例如toString())而不是原始的getValue(),后者可能返回整数或任何值。