Unit Of Work和Repository模式的实际应用

21
我正在构建一个ORM,并试图弄清楚每个模式的确切责任是什么。假设我想在两个账户之间转账,使用工作单元来管理在单个数据库事务中的更新。
以下方法是否正确?
1. 从存储库获取它们 2. 将它们附加到我的工作单元 3. 进行业务交易并提交?
示例:
from = accountRepository.find(fromAccountId);
to = accountRepository.find(toAccountId);

unitOfWork.attach(from);
unitOfWork.attach(to);    

unitOfWork.begin();
from.withdraw(amount);
to.deposit(amount);
unitOfWork.commit();

应该像这个例子一样,独立使用工作单元和仓储,还是:
- 工作单元应该在内部使用仓储,并具有加载对象的能力吗? - ... 或者仓储应该在内部使用工作单元,并自动附加任何加载的实体吗?

3
为什么已经有几个好的ORM可用,还需要构建ORM呢? - Magnus Backeus
1
只是试图将ORM最佳实践应用于PHP。已经有一些了,但并不完全符合我的期望。 - BenMorel
谢谢我找到了这样的问题 +1 - Jeancarlo Fontalvo
3个回答

19
简而言之,仓储模式是一种创建类似于聚合根的集合抽象的方法,而工作单元旨在创建一种将一组与数据库相关的函数组合在一起以便执行为一个原子单元的方式。这两种模式处理的抽象级别非常不同,它们之间的关系取决于如何对聚合根进行建模。如果您的聚合/实体正在处理持久性,则将UoW包含在存储库中是合适的,否则,在存储库中包含UoW就没有意义了。如果您想创建面向公众的东西,超出组织范围,那么我建议您以一种方式创建您的存储库接口/基本实现,使它们可以根据ORM用户的需求直接与ORM交互或不交互。如果这是内部的,并且您正在Aggregates.Entities中进行持久化工作,则令Repository使用您的UoW是有意义的。对于通用的Repository,提供从Repository实现中访问UoW对象是有意义的,这样可以确保其被适当地初始化和处理。此外,有时您可能要在单个UoW boundary内利用多个Repositories,因此在这种情况下,您需要能够将已经初始化的UoW传递给Repository。

感谢您提供这个完整的答案。总的来说,在ORM的全局上下文中,没有“最佳”方法,这是一个选择问题。 - BenMorel

4

我建议您在存储库内部使用UoW的方法。这种方法有一些优点,特别适用于Web应用程序。

在Web应用程序中,使用UoW的推荐模式是每个HTTP请求使用一个Unit of Work(会话)。因此,如果您的存储库共享UoW,则可以为其他存储库请求的对象(例如被多个聚合引用的数据字典)使用第一级缓存(使用标识映射)。此外,您只需要提交一个事务而不是多个事务,因此从性能方面来看,它将更加有效。

您可以查看Java/.NET世界成熟的ORM Hibernate/NHibernate源代码。


2

好问题!

这取决于您的工作范围。如果它们将跨越多个存储库,则可能需要创建另一个抽象层以确保覆盖多个存储库。这就像在领域驱动设计中定义的小型“服务”层。

如果您的工作单元将基本上是每个存储库,那么我会选择第二个选项。

然而,我的问题是,在编写ORM时,您如何担心存储库?它们将由您的工作单元的使用者定义和使用,对吗?如果是这样,您只能提供一个工作单元,您的使用者将不得不向您的工作单元注册存储库,并且还将负责控制工作单元的边界。不是吗?


谢谢Nilesh。实际上我的计划是为所有这些模式定义一个清晰的接口,以及一个旨在非常通用并适应我们日常工作大多数情况的基本实现。我已经有了一个工作实现,包括模型和数据映射器以及映射器注册表,它提供了对所有映射器的访问,但我正在努力推动边界,以提供支持具有标识映射的存储库,支持条件搜索,并且支持工作单元来管理对数据库的更新并处理事务。 - BenMorel
抱歉我说话有点啰嗦,我需要更多的空间 :) 因此,我想找到一个通用的想法来将所有内容整合在一起!非常感谢您的帮助。 - BenMorel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接