【问题标题】:Multiple repositories under the same transaction同一事务下的多个存储库
【发布时间】:2018-03-17 17:10:08
【问题描述】:

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

【问题讨论】:

  • 你认为他们应该知道吗?
  • 不,我认为他们不应该。
  • 这个问题是 CQRS 特有的还是一般 DDD 上下文中的问题?
  • 不,它一般适用于 DDD,NLayered 架构。但它也适用于 CQRS。
  • 在我看来,CQRS 是一个特例。在您的示例中,这两个操作实际上只是一个,应该明确说明。 save 操作具有这样的签名,很明显 Aggregates 状态与生成的事件非常耦合。换句话说,CQRS 存储库不是经典的 DDD 存储库。 CQRS 非常适合 DDD,但它的风格与经典风格截然不同。事件是一等公民,应该在整个代码库或框架中明确声明。

标签: architecture repository domain-driven-design cqrs


【解决方案1】:

存储库不知道它们在某个事务下运行的事实,还是应该知道?

这是 Evan 在Blue Book 中写的内容

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

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

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

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

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

在这里提供了一些提示。 从存储库中阅读很漂亮;您将实现的所有复杂性隐藏在一些与持久性无关的外观后面。编写可能会更棘手——如果您的域模型需要支持将多个聚合保存在一起(相同的事务),那么您的“存储库”接口应该明确说明。

这些天来,您可能会看到更多的支持,即对聚合的修改应始终在单独的事务中进行。这减轻了存储库协调的一些压力。

【讨论】:

    【解决方案2】:

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

    因此,我总是将 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();
    

    【讨论】:

    • 好的,但是工作对象单元内部是什么?我假设有一个打开的 IDbTransaction 对象。这意味着此 uof 仅适用于 .NET 支持的数据库。如果我将 Product Repository 实现为 HttpRepository,当 CustomerRepository 失败时,回滚的实现是什么?我应该自己实现某种回滚吗?
    • 您当然只能实现其背后技术支持的功能。如果你想创建一个 httprepository(这对我来说毫无意义),你不能实现回滚,因为 http 不支持。
    【解决方案3】:

    存储库和事务

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

    具有替代来源的存储库

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

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

    【讨论】:

      猜你喜欢
      • 2012-07-11
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      • 2017-12-27
      • 2020-03-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多