【问题标题】:Few confusing things about globally accessible Value Objects关于全局可访问值对象的一些令人困惑的事情
【发布时间】:2013-01-23 05:19:36
【问题描述】:

引用来自DDD: Tackling Complexity in the Heart of Software(第 150 页)

一)

对 VALUE 的全局搜索访问通常是没有意义的,因为找到一个 VALUE 的属性相当于创建一个新实例 与那些属性。也有例外。例如,当我在 在线计划旅行,我有时会保存一些预期的行程 稍后返回以选择一个进行预订。这些行程是价值 (如果有两个由相同的航班组成,我不在乎哪个 是哪个),但它们已与我的用户名相关联,并且 完好无损地为我找回。

我不明白作者的推理,为什么让Itinierary 值对象全局可访问而不是客户端必须全局搜索Customer root entity 然后从它遍历到这个Itinierary 对象更合适?

b)

持久对象的一个​​子集必须可以通过一个全局访问 基于对象属性的搜索......它们通常是实体, 有时是具有复杂内部结构的 VALUE OBJECTS ...

为什么具有复杂内部结构的 Values 对象 更常见于全局可访问,而不是更简单的 Value 对象

c) 无论如何,是否有一些关于如何确定特定值对象是否应可全局访问的一般准则?

更新:

一)

没有域理由使行程可以遍历 客户实体。如果不需要,为什么要加载客户实体 对于任何行为?查询通常最好在没有的情况下处理 使行为领域复杂化。

我可能错了,但是当用户(即客户根实体)登录时,域模型检索用户的Customer Aggregate 不是很常见吗?

如果用户可以选择预订航班,那么他们也会不时查看Itineraries(尽管英语不是我的第一语言,所以Itinerary 这个词实际上可能意味着什么与我认为的意思有点不同)他们已经选择或预订了。

既然Customer Aggregate 已经从数据库中检索到了,为什么还要在Itinerary 已经与Customer Aggregate 一起检索到它时再次对Itinerary 进行全局搜索(可能会在数据库中搜索它)?

c)

IMO 规则非常简单——如果有需要的话。它没有 取决于 VO 本身的结构,但取决于 用例需要特定的 VO。

但是这个 VO 实例必须与某个实体相关(即 Itinerary 与特定的 Customer 相关),否则正如作者指出的那样,我们可以简单地创建一个新的而不是通过其属性搜索 VO具有这些属性的 VO 实例?

第二次更新:

a) 来自您的link

另一种表达关系的方法是使用存储库。

当通过 repository 表达关系时,您是否实现 SalesOrder.LineItems 属性(我对此表示怀疑,因为您建议不要直接调用存储库的实体),而后者又调用 repository,或者你是否实现了SalesOrder.MyLineItems(IOrderRepository repo) 之类的东西?如果是后者,那么我认为不需要SalesOrder.LineItems 属性?

b)

要记住的重要一点是聚合并不意味着 用于显示数据。

领域模型确实不关心上层将如何处理数据,但如果不使用 ApplicationUI 之间的 DTO层,那么我假设 UI 将从 聚合 中提取要显示的数据(假设我们将整个聚合发送到 UI 而不仅仅是驻留在其中的某个实体)?

谢谢

【问题讨论】:

    标签: domain-driven-design


    【解决方案1】:

    a) 没有域理由使行程可以通过客户实体。如果任何行为都不需要客户实体,为什么还要加载它?通常最好在不使行为域复杂化的情况下处理查询。

    b) 我假设他的推理是复杂的值对象是您想要查询的对象,因为您无法轻松地重新创建它们。此问题和所有与查询相关的问题都可以通过read-model pattern 解决。

    c) IMO 的规则非常简单——如果需要的话。它不取决于 VO 本身的结构,而是取决于用例是否需要特定 VO 的实例。

    更新

    a) 客户集合不太可能引用客户的行程。原因是我看不到行程将如何与客户集合中存在的行为相关联。如果只需要显示一些数据,则根本不需要加载客户聚合。但是,如果您确实加载了聚合并且它确实包含您需要的参考数据,那么您也可以显示它。需要记住的重要一点是,聚合并不是用来显示数据的。

    c) 客户和行程之间的关系可以通过一个共享 ID 来表示 - 每个行程都有一个 customerId。这将允许根据需要进行查找。但是,仅仅因为这两件事是相关的,并不意味着您需要遍历客户才能到达相关实体或值对象以进行查看。更一般地,关联可以实现为direct references or via repository search。无论哪种方式都需要权衡取舍。

    更新 2

    a) 如果使用存储库实现,则没有 LineItems 属性 - 没有直接引用。相反,要获取行项目列表,需要调用存储库。

    b) 或者您可以创建一个类似 DTO 的对象,一个读取模型,它将直接从存储库返回。存储库可以依次执行简单的 SQL 查询来获取所有必需的数据。这使您可以获取不属于聚合但相关的数据。如果聚合确实具有视图所需的所有数据,则使用该聚合。但是,一旦您需要更多与聚合无关的数据,请切换到读取模型。

    【讨论】:

    • 为什么需要一个 MyLineItems 方法? SalesOrder 上是否存在需要订单项的行为?
    • 我认为 SalesOrder 是 SaleOrder 聚合的根。客户是另一个集合体。
    • 您可以要求客户提供销售订单,但您只需要一个 ID,而不是对整个聚合对象的引用。换句话说,您可以拥有交叉聚合引用,但最好只使用 ID。
    • 在某些情况下,实际上可以接受没有等待填充客户数据的客户的销售订单,但这是更复杂场景的一部分。看看this article
    • 值对象也是如此——它们在实体之外没有意义。仅仅因为一个实体 A 依赖于另一个实体 B 的存在,并不意味着 A 不能是聚合根。事实上,通常建议考虑创建聚合的上下文 - Don't Create Aggregate Roots
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-04
    • 2014-11-02
    • 1970-01-01
    • 2022-08-14
    相关资源
    最近更新 更多