Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit of Work + Repository Pattern: The Fall of the Business Transaction Concept

Combining Unit of Work and Repository Pattern is something used fairly widely nowadays. As Martin Fowler says a purpose of using UoW is to form a Business Transaction while being ignorant of how repositories actually work (being persistent ignorant). I've reviewed many implementations; and ignoring specific details (concrete/abstract class, interface,...) they are more or less similar to what follows:

public class RepositoryBase<T> {     private UoW _uow;     public RepositoryBase(UoW uow) // injecting UoW instance via constructor     {        _uow = uow;     }     public void Add(T entity)     {        // Add logic here     }     // +other CRUD methods }  public class UoW {     // Holding one repository per domain entity      public RepositoryBase<Order> OrderRep { get; set; }     public RepositoryBase<Customer> CustomerRep { get; set; }     // +other repositories      public void Commit()     {        // Psedudo code:         For all the contained repositories do:            store repository changes.     } } 

Now my problem:

UoW exposes public method Commit to store the changes. Also, because each repository has a shared instance of UoW, each Repository can access method Commit on UoW. Calling it by one repository makes all other repositories store their changes too; hence the result the whole concept of transaction collapses:

class Repository<T> : RepositoryBase<T> {     private UoW _uow;     public void SomeMethod()     {         // some processing or data manipulations here         _uow.Commit(); // makes other repositories also save their changes     } } 

I think this must be not allowed. Considering the purpose of the UoW (business transaction), the method Commit should be exposed only to the one who started a Business Transaction for example Business Layer. What surprised me is that I couldn't find any article addressing this issue. In all of them Commit can be called by any repo being injected.

PS: I know I can tell my developers not to call Commit in a Repository but a trusted Architecture is more reliable than trusted developers!

like image 868
Alireza Avatar asked Oct 23 '13 17:10

Alireza


People also ask

What is Repository pattern used for?

The Repository pattern. Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.

What is repository in design pattern?

By definition, the Repository Design Pattern in C# mediates between the domain and the data mapping layers using a collection-like interface for accessing the domain objects. Repository Design Pattern separates the data access logic and maps it to the entities in the business logic.

What is unit of work in oops?

Unit of Work is the concept related to the effective implementation of the repository pattern. non-generic repository pattern, generic repository pattern. Unit of Work is referred to as a single transaction that involves multiple operations of insert/update/delete and so on.

What is unit of work in Java?

Unit of Work merges many small database updates in a single batch to optimize the number of round-trips. MartinFowler.com says. Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. Programmatic Example.


2 Answers

I do agree with your concerns. I prefer to have an ambient unit of work, where the outermost function opening a unit of work is the one that decides whether to commit or abort. Functions called can open a unit of work scope which automatically enlists in the ambient UoW if there is one, or creates a new one if there is none.

The implementation of the UnitOfWorkScope that I used is heavily inspired by how TransactionScope works. Using an ambient/scoped approach also removes the need for dependency injection.

A method that performs a query looks like this:

public static Entities.Car GetCar(int id) {     using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Reading))     {         return uow.DbContext.Cars.Single(c => c.CarId == id);     } } 

A method that writes looks like this:

using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing)) {     Car c = SharedQueries.GetCar(carId);     c.Color = "White";     uow.SaveChanges(); } 

Note that the uow.SaveChanges() call will only do an actual save to the database if this is the root (otermost) scope. Otherwise it is interpreted as an "okay vote" that the root scope will be allowed to save the changes.

The entire implementation of the UnitOfWorkScope is available at: http://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/

like image 63
Anders Abel Avatar answered Oct 12 '22 15:10

Anders Abel


Make your repositories members of your UoW. Don't let your repositories 'see' your UoW. Let UoW handle the transaction.

like image 42
Chalky Avatar answered Oct 12 '22 14:10

Chalky