Unit of Work

Maintains a list of operations affected by a business transaction and coordinates their writing.

Context

A business operation modifies multiple aggregates (Order, Inventory, Audit). You want to commit ALL or nothing.

Problem

Without coordination, you might save the order but fail to decrement stock.

Solution

A UnitOfWork groups operations and commits them with SaveChanges() within a single transaction.

#Example in C#

public interface IUnitOfWork : IAsyncDisposable
{
    IOrderRepository Orders { get; }
    IInventoryRepository Inventory { get; }
    Task<int> SaveChangesAsync(CancellationToken ct = default);
}

public class EfUnitOfWork : IUnitOfWork
{
    private readonly AppDbContext _db;
    public EfUnitOfWork(AppDbContext db, IOrderRepository o, IInventoryRepository i)
    { _db = db; Orders = o; Inventory = i; }

    public IOrderRepository Orders { get; }
    public IInventoryRepository Inventory { get; }

    public Task<int> SaveChangesAsync(CancellationToken ct = default) => _db.SaveChangesAsync(ct);
    public ValueTask DisposeAsync() => _db.DisposeAsync();
}

// Usage
await using var uow = factory.Create();
uow.Orders.Add(order);
uow.Inventory.Reserve(order.Items);
await uow.SaveChangesAsync();

In EF Core, DbContext is already a Unit of Work. Creating another one on top is usually redundant.

When NOT to use it
  • If your ORM already implements Unit of Work (EF, NHibernate).
  • If operations span processes (in which case you need Saga / Outbox).

#extra #persistence #transaction