存储库模式和 TransactionScope

问题描述

考虑具有 OrderItem 集合的 Order(aggregate root) 实体。

class Order
{
  public string Number;
  public List<OrderItem> OrderItems;
}
class OrderItem
{
  public string Name;
  public float Price;
}

然后像这样的 OrderRepository:

class OrderRepository
{
  IDbConnection connection;
  void SaveOrder(Order o)
  {
    connection.Execute("INSERT INTO Order ...");
    
    foreach(var oi in o.OrderItems)
    {
      connection.Execute("INSERT INTO OrderItem ...");
    }
  }
}

问题是 TransactionScope 是否应该放在 OrderRepository 中。像这样:

class OrderRepository
{
  IDbConnection connection;
  void SaveOrder(Order o)
  {
    using(var scope = new TransactionScope())
    {
      connection.Execute("INSERT INTO Order ...");
    
      foreach(var oi in o.OrderItems)
      {
        connection.Execute("INSERT INTO OrderItem ...");
      }
      scope.Complete();
    }
  }
}

我对此的考虑: 调用Save后需要保证数据一致。这意味着如果没有任何 TransactionScope,在存储库内部或外部,这可能会导致某些 OrderItems 已提交,而有些则没有(异常)。然后我们可以有:

  1. TransactionScope 仅在存储库外部。包装多个存储库调用
using(var scope = new TransactionScope())
{
  orderRepository.Save(order);
  warehouseRepository.Save(...);
}
  1. TransactionScope 位于 1) 外部和存储库内部(见上文)。

我在 1) 中看到的问题是 OrderRepository 需要假设调用代码将提供 TransactionScope。否则无法保证 Order 聚合的一致持久性。

解决方法

存储库可以假设自己在更高级别发起的事务(即用例)的上下文中运行。此外,它根本不应该关心它可能运行或不运行的上下文。

因此,不建议在存储库内启动事务。

您可能希望为组成单个聚合的多个实体提供多个存储库对象,这意味着它们都必须在单个事务下运行。事务本质上是一个工作单元,而工作单元是一个应该存在于应用层中的抽象。