首页 > 解决方案 > 存储库模式和 TransactionScope

问题描述

考虑具有 OrderItem 集合的 Order(聚合根)实体。

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();
    }
  }
}

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

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

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

标签: c#sqldesign-patternsrepository-patternddd-repositories

解决方案


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

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

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


推荐阅读