Architecture 同一事务下的多个存储库

Architecture 同一事务下的多个存储库,architecture,repository,domain-driven-design,cqrs,Architecture,Repository,Domain Driven Design,Cqrs,想象一下,当应用程序层打开一个新事务时,您希望在此事务下执行2个存储库操作。假设我想使用IUserRepository添加一个新用户,然后保存用户域模型状态更改期间生成的所有事件。我在ApplicationLayer(TransactionScope)中打开一个新事务,然后执行这两个操作。但是如果tommorow我想将存储库实现更改为FileBasedRepository或HttpCloudRepository呢。存储库不知道它们正在某个事务下运行,或者应该这样做? 主要的问题是关于抽象。我们都

想象一下,当应用程序层打开一个新事务时,您希望在此事务下执行2个存储库操作。假设我想使用IUserRepository添加一个新用户,然后保存用户域模型状态更改期间生成的所有事件。我在ApplicationLayer(TransactionScope)中打开一个新事务,然后执行这两个操作。但是如果tommorow我想将存储库实现更改为FileBasedRepository或HttpCloudRepository呢。存储库不知道它们正在某个事务下运行,或者应该这样做?
主要的问题是关于抽象。我们都努力编写基于抽象的代码。我看到的是,如果我实现了一个工作单元,并在应用层使用2个存储库更新了2个聚合(存储库与MSSql一起工作),tommorow我无法将其中一个存储库实现更改为基于httpbased或基于内存的。问题是如何编写这样的代码。似乎我必须在事务上创建一个抽象,并为HttpUnitOfWork实现自己的回滚。感觉我最初的工作单元与db紧密耦合,应该重命名为DbUnitOfWork。但这又错了,因为根本不存在不支持事务的sql数据库

存储库和事务

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

具有替代源的存储库

在同一工作单元中混合来自不同来源的存储库的数据应该不会有问题。理想情况下,UoW将知道哪种持久性机制与哪种类型的实体相匹配,并最终将其分类

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

存储库不知道它们正在某个事务下运行,或者应该这样做

这是埃文写的,在

存储库概念适用于许多情况。实施的可能性如此之多,我只能列出一些需要牢记的问题。。。。 将事务控制权留给客户机。虽然存储库将从数据库中插入和删除,但通常不会提交任何内容。。。。如果存储库不插手,事务管理将更简单

说实话,我发现这个概念很难与同一章的其他著作相一致

对于需要全局访问的每种类型的对象,创建(一个存储库),该存储库可以提供该集合中所有对象的内存集合的幻觉

在我通常使用的语言中,内存中的集合通常管理自己的状态。这是否意味着事务控制应该在集合接口后面,而不是留给客户机

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

这里提供一些提示。从知识库中阅读是美丽的;您承担了实现的所有复杂性,并将其隐藏在一些持久性不可知的外观后面。编写可能更为棘手——如果您的域模型需要支持将多个聚合保存在一起(同一事务),那么您的“存储库”接口应该使其显式化


现在,您可能会看到更多人支持这样一种观点,即聚合的修改应该总是在单独的事务中进行。这减轻了存储库协调的一些压力。

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

因此,我总是将UnitOfWork/DbSession/whatever传递给我的存储库。存储库使用该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();

你认为他们应该知道吗?不,我认为他们不应该。这个问题是针对CQR的,还是在一般DDD上下文中?不,它是DDD的一般,NLayered架构。但它也适用于CQR。在我看来,CQR是一个特例。在您的示例中,这两个操作实际上只有一个,这应该是明确的。
save
操作具有这样一个特征,即聚合状态与生成的事件非常耦合。换句话说,CQRS存储库不是clasic DDD存储库。CQRS是DDD的完美选择,但它的风格与古典风格截然不同。事件是第一类公民,应该在整个代码库或框架中明确声明。好的,但工作单元对象内部是什么?我假设有一个打开的IDbTransaction对象。这意味着此uof仅适用于.NET支持的数据库。如果我将产品存储库实现为HttpRepository,那么当CustomerRepository失败时,回滚的实现是什么?我应该自己实现某种回滚吗?当然,您只能实现它背后的技术支持的功能。如果您想创建一个httprepository(这对我来说毫无意义),就不能实现回滚,因为http不支持回滚。