同一事务下的多个存储库

Multiple repositories under the same transaction

想象这样一种情况,应用层新开了一个事务,你想在这个事务下进行2次repository操作。假设我想使用 IUserRepository 添加一个新用户,然后保存在用户域模型状态更改期间生成的所有事件。我在 ApplicationLayer (TransactionScope) 中打开一个新事务,然后执行这两个操作。但是,如果明天我想将存储库实现更改为 FileBasedRepository 或 HttpCloudRepository 怎么办?存储库不知道它们在某些事务下 运行 的事实,或者它们应该知道吗? 主要问题是关于抽象。我们都努力编写基于抽象的代码。我看到的是,如果我实现了一个工作单元并使用应用层中的 2 个存储库更新了 2 个聚合(存储库使用 MSSql),明天我无法将其中一个存储库实现更改为基于 http 或基于内存。问题是关于如何编写这样的代码。似乎我必须创建一个事务抽象并通过我自己的 HttpUnitOfWork 实现回滚。感觉我的初始工作单元与 db 紧密耦合,应该重命名为 DbUnitOfWork。但这又是错误的,因为没有 sql 数据库根本不支持事务。

存储库和事务

对我来说,存储库不应该知道环境事务 - 应该有一个 Work-like 容器单元来跟踪更新的实体并在您声明工作单元完成时提交事务。

具有替代来源的存储库

在同一工作单元中混合来自不同来源的存储库的数据应该不是问题。 理想情况下,UoW 会知道哪种持久化机制适用于哪种类型的实体,并最终将其全部分类。

出于实际原因,您可能希望将 UoW 限制为单个持久存储(通常是通过 ORM 的数据库)并添加到具有不同 transaction-illiterate 数据源方法的存储库,例如 Update(entity) 调用时直接保存到源。

Repositories don't know about the fact that they are running under some transaction, or should they?

这是 Evan 在 Blue Book

中写的内容

The REPOSITORY concept is adaptable to many situations. The possibilities of implementation are so diverse that I can only list some concerns to keep in mind.... Leave transaction control to the client. Although the REPOSITORY will insert and delete from the database, it will ordinarily not commit anything.... Transaction management will be simpler if the REPOSITORY keeps its hands off.

老实说,我发现这个概念很难与同一章中的其他作品相协调。

For each type of object that needs global access, create ( a REPOSITORY ) that can provide the illusion of an in-memory collection of all objects of that set.

在我通常使用的语言中,in-memory 集合通常管理它们自己的状态。这是否意味着事务控制应该在集合接口之后,而不是留给客户端?

另外一点是我们的持久性解决方案变得更加复杂;对于 Evans,域模型仅管理对单个数据库的写入,该数据库支持可以跨越多个聚合(即 RDBMS)的事务。但是如果你改变那个假设,那么很多事情就会开始变得更加复杂。

在这里提供了一些提示。 从知识库中阅读 很美妙;您承担了实现的所有复杂性,并将其隐藏在一些与持久性无关的外观之后。编写可能更棘手——如果您的域模型需要支持将多个聚合保存在一起(同一事务),那么您的 "repository" 接口应该明确说明。

最近,您可能会看到更多人支持这样的想法,即聚合的修改应该始终发生在单独的事务中。这减轻了存储库协调的一些压力。

存储库不知道它的使用环境。因此,存储库不应负责启动、提交或回滚事务。

因此,我总是将 UnitOfWork / DbSession / 任何东西传递到我的存储库。存储库使用该 UnitOfWork,事务在存储库外提交。

在伪代码中,这看起来像这样:

var uow = DatabaseContext.CreateUnitOfWork();

uow.StartTransaction();

var productRepository = new ProductRepository(uow);
var customerRepository = new CustomerRepository(uow);

// ... do some operations on the repositories.

uow.Commit();