3

背景

Udi Dahan 建议将获取策略作为用于数据访问的有用模式。我同意。

这个概念是使角色明确。例如,我有一个聚合根 - 客户。我希望客户在我的应用程序的几个部分中 - 可供选择的客户列表,客户详细信息的视图,并且我想要一个按钮来停用客户。

似乎 Udi 会为这些角色中的每一个建议一个界面。所以我有ICustomerInList非常基本的细节,ICustomerDetail其中包括最近购买的 10 种产品,并且IDeactivateCustomer有一种方法可以停用客户。每个接口都暴露了我的客户聚合根,以便在每种情况下完成工作。我的客户聚合根实现了所有这些接口。

现在我想为每个角色实现一个获取策略。每种策略都可以将不同数量的数据加载到我的聚合根中,因为它将位于只公开所需信息位的接口后面。

实现这部分的一般方法是询问服务定位器或其他样式的依赖注入。例如,此代码将获取您想要的接口ICustomerInList,并找到一个获取策略来加载它(IStrategyForFetching<ICustomerInList>)。这个策略是由一个类实现的,该类知道只加载具有 ICustomerInList 接口所需的信息位的客户。

到目前为止,一切都很好。

问题

您传递给服务定位器或IStrategyForFetching<ICustomerInList>. 我看到的所有示例都仅通过已知 ID 选择一个对象。这种情况很简单,调用代码通过这个id,会得到具体的接口。

如果我想搜索怎么办?或者我想要客户列表的第 2 页?现在我想传递更多获取策略所需的术语。

可能的解决方案

我见过的一些示例使用谓词 - 如果特定聚合根应该是结果集的一部分,则返回 true 或 false 的表达式。这在条件下工作得很好,但是让前 n 个客户回来而不是更多呢?或者获取搜索结果的第 2 页?或者结果如何排序?

我的第一反应是开始向我的IStrategyForFetching<ICustomerInList>It now添加泛型参数IStrategyForFetching<TAggregateRoot, TStrategyForSelecting, TStrategyForOrdering>。这很快变得复杂而丑陋。不同的存储库使情况更加复杂。一些存储库仅在使用特定策略进行选择时提供数据,有些仅提供某些类型的排序。我希望能够灵活地实现可以采用排序功能的通用存储库以及仅返回以特定方式排序的聚合根的专用存储库。

听起来我应该应用开始时使用的相同模式 - 如何明确角色?我是否应该使用有效负载 Y(搜索/排序参数)实施获取 X(聚合根)的策略?

编辑 (2012-03-05)

如果我不是每次都返回聚合根,这一切仍然有效。如果每个接口由不同的 DTO 实现,我仍然可以使用 IStrategyForFetching。这就是这种模式强大的原因 - 获取和返回的内容不必以任何方式映射到聚合根。

我最终使用了IStrategyForFetching<TEntity, TSpecification>. TEntity 是我想要得到的东西,TSpecification 是我想要得到它的方式。

4

2 回答 2

3

你遇到过CQRS吗?Udi 是它的大力支持者,它的目的就是解决这个确切的问题。

最基本形式的概念是将域模型与查询分开。这意味着域模型仅在您想要执行命令/提交事务时发挥作用。您不会使用聚合和实体中的数据在屏幕上显示信息。相反,您创建一个单独的数据访问服务(或一组),其中包含提供每个屏幕所需的确切数据的方法。这些方法可以接受标准对象作为参数,因此可以使用您想要的任何标准进行搜索。

这是如何工作的快速序列:

  • 屏幕显示上周已下订单的客户列表。
  • UI 调用 CustomerQueryService 传递一个日期作为条件。
  • CustomerQueryService 执行一个查询,该查询仅返回此屏幕所需的字段,包括每个客户的聚合 ID。
  • 用户在列表中选择一个客户,并选择执行“Make Important Customer”操作/命令。
  • UI 向包含客户 ID 的命令服务(或 DDD 术语中的应用程序服务)发送 MakeImportantCommand。
  • 命令服务使用命令中传递的 ID 从存储库中获取客户聚合,调用必要的方法并更新数据库。

使用 CQRS 架构构建您的应用程序为您提供了许多关于性能和可扩展性的可能性。您可以通过创建包含每个视图、最终一致性和事件溯源的非规范化表的单独查询数据库来进一步采用这个简单的示例。有很多关于 CQRS 的视频/示例/博客,我认为你会很感兴趣。

我知道你的问题是关于“获取策略”的,但我注意到他在 2007 年写了这篇文章,他很可能认为 CQRS 是它的继承者。

总结一下我的回答:

  1. 不要尝试从您的域聚合中减少 DTO。相反,只需创建单独的查询服务,为您提供定制的查询以满足您的需求。
  2. 阅读 CQRS(如果您还没有的话)。
于 2012-03-02T14:02:08.417 回答
1

补充大卫马斯特斯的回应,我认为所有的获取策略接口都在增加不必要的复杂性。让客户 AR 实现在 UI 之后建模的各种接口是对 AR 类的不必要约束,您将花费大量精力来尝试强制执行它。此外,它是一个脆弱的解决方案。如果视图需要与客户相关但不属于客户类的数据怎么办?然后是否强制客户类和相应的 ORM 映射包含该数据?为什么不只使用一组单独的类用于查询目的并使用它呢?这使您可以在它们所属的位置处理获取策略 - 在存储库中。此外,获取策略接口抽象真正增加了什么价值?它可能是应用程序中正在发生的事情的适当模型,它不是

于 2012-03-03T02:13:39.423 回答