DDD Repository:用DAO做分离?

DDD Repository: Use DAO for separation?

关于 Repositories、它们在 Whosebug 和整个网络上的使用和布局的讨论存在争议。我对如何有效地实现存储库背后的数据访问抽象(例如数据库)感到困惑。

我没有使用 ORM tool/framework,因为我想自己查看详细信息。目前我正在使用 DAO 对象访问 (mysql) 数据库并提供业务对象(域对象)。数据库表中外键给出的关联在相应对象的 DAO 中解析和加载(目前没有延迟加载)。因为我不想在业务逻辑中直接使用我的数据库 DAO,所以我认为存储库是一个很好的进一步抽象。在 Repository 中实现诸如 GetEmployeesByShopAndPosition() 之类的复杂查询时,我遇到了困难:我看到了两种实现方式:

  1. Brute Force:使用 Employee DAO 并将 all 员工作为业务对象(包括关联的 shops/positions)从数据库加载到 Repository 的集合中。遍历集合和 return 在给定商店和职位工作的员工。
  2. 高效:实施连接相关表的数据库查询,returns 只需要 EmployeeDAO 中的 where 子句。

第一种方法使用了 Repository 应有的集合性质,但似乎效率很低。第二种方法生成 bloated DAO 但效率更高。

我的问题:

  1. 这里有什么偏好或者在实践中是如何完成的?
  2. 我是不是错了,Repository 不应该和 DAO 一起使用,数据库相关的代码可以直接放在 Repository 中吗?
  3. 作为存储库处理 聚合 ,它实际上应该 assemble 关联的外键来构建(完整的)业务对象而不是我当前使用的 DAO 吗?

我知道这个主题不是 black/white 因为所涉及的设计模式也可以用不同的方式实现,但我想有一些指导方针不应该被打破或混淆以获得关注点分离和持久性无知 (PI).

您实际上在这里问了很多问题,所以我会尽量让答案尽可能简洁:)

存储库 return 一个 Aggregate Root 或一个 Entity。有些人非常坚定地认为存储库只有 return AR,这很好并且总是足够的。

有两种类型的存储库(正如 Vaughn Vernon 在他的 实施 Domain-Driven 设计 书中所描述的那样):

  • collection-oriented
  • persistence-oriented

您可能会更频繁地遇到并使用 persistence-oriented。这可能是混乱的来源 w.r.t。道。当然,一个 DAO 可能 return 一个企业 object 但它可能会 return 不止于此。

您的 query 示例是 DAO 可能更合适的地方。因此,在 domain-driven 设计领域,您会经常遇到 CQRS(command/query 职责分离)。归结为 不是 查询您的域。

你应该有一个薄的、专用的、查询 层,returns 产生最合适的格式(但不是实体)。在 C# 中,我使用 DataTableDataRowstring 之类的东西,如果需要,有时还会使用复杂的 DTO。

存储库只与 AR 相关,例如:

  • 得到
  • 添加
  • 移除

存储库基本上使用各种逻辑 DAO(ADO.NET,ORM --- 我尽量避免使用 ORM)。

关于使用关联的外键检索 AR 的第二点:AR 应该从不包含对另一个 AR 的引用。实体和 Value Objects 没问题。对于关联的 AR,使用 ID 或值 Object 来表示外部 AR。 AR 本身可能包含一个复杂的结构,但不要混淆所有权和包含。 OrderLine 包含在 Order 中。 Customer 拥有 Order。所以 Order 将有一个 OrderLine collection 但不是对 Customer object 的引用(而是 ID/VO)。

Order/OrderLine 示例说明了我们不查询域的原因。当我们想要给定开始日期和结束日期之间的订单列表时,我们可能对所有订单数据都不感兴趣,当然也不会对订单行感兴趣。所以加载这些聚合没有意义。这是在查询域时,像 lazy-loading 这样令人讨厌的东西悄悄进入的地方。恕我直言,Lazy-loading 不应该存在 :) --- 一个简单的查询层就足够了。

希望对您有所帮助。

  1. 尽可能利用数据库的力量。这就是 ORM 会尝试做的事情——在 SQL 查询中尽可能多地进行过滤、排序等。

  2. 我认为同时拥有存储库和 DAO 的价值不大。它们都抽象出持久存储。如果你不想使用 ORM,你通常会在具体的 Repository 实现中处理数据库查询生成部分。但在协作应用程序中,这也留下了其他复杂的事情需要实现,例如更改跟踪、事务管理等

  3. 完整是一个整体的意思,是的。但作为建模的良好实践,尽量不要在聚合中存储(并因此重新水合)对其他聚合根的引用。

这是从 "classical" DDD 的角度来看的,但是如果您想走 CQRS 路线(有些人会说这是唯一明智的路线;),一定要遵循 @EbenRoux 的建议。