mardi 6 juillet 2021

Repository pattern and TransactionScope

Consider Order(aggregate root) entity having a collection of OrderItem.

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

then OrderRepository like this:

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

The question is whether TransactionScope should be placed in OrderRepository. Something like this:

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

My considerations on this: We need to ensure that data is consistent after calling Save. This means that without any TransactionScope, inside repository or outside, this may result in some OrderItems committed and some not (exception). Then we can have:

  1. TransactionScope only outside repository. Wrapping multiple repository calls.
using(var scope = new TransactionScope())
{
  orderRepository.Save(order);
  warehouseRepository.Save(...);
}
  1. TransactionScope both outside 1) and inside repository (see above).

The issue I see with 1) is that OrderRepository needs to make an assumption that the calling code will provide TransactionScope. Otherwise it can't guarantee a consistent persistence for Order aggregate.

Aucun commentaire:

Enregistrer un commentaire