George Polevoy
George Polevoy

Reputation: 7681

Abstract transactions with service/repository/unitofwork

I have a ton of transactional db access code intermixed with business logic in legacy code. I want to separate it to services and repositories, with transaction control outside services and abstract away transactions for testability.

The problem is, i need to have IDbConnection parameter, enlisted in transaction, for every method signature both in service and repository layers, and that is what i certainly don't like:

void Process(Stuff s, IDbConnection connection);

I want to figure out a way to have something like this in a top level method:

void Process(Stuff stuff, User u)
{
 using(var UoW = ???) // Start transaction
 {
  // various operations in the same transaction
  if( AuthorizationService.AuthorizesUserForStuff(u,stuff) )
  {
   StuffService.Process(stuff);
   AuditService.Success(u, stuff);
  }
  else
   AuditService.Failure(u,stuff);
  UoW.Commit();
 }
}

Repository:

void Process(Stuff s)
{
 IDbCommand command = ??? //aware of current UnitOfWork
 ...
 command.ExecuteNonQuery();
}

The legacy code does not use any kind of ORM, it's a lot of stored procedure and custom sql code, also some operations require roundtrips to database, so i can't just register the objects with unitofwork, and carry out operations in commit method.

Please suggest what pattern can be used in this situation for some persistence ignorance and will allow to control transactions at the top level.

Upvotes: 0

Views: 339

Answers (2)

MBoros
MBoros

Reputation: 1140

The only way i see your repository without the actual unit of work, is through the Static gateway pattern. I understand your frustration, but i dont think doing it this way is much better. Also unit testing will be harder, and code readability as well.

I would be very intested in any other method.

Upvotes: 1

Mahol25
Mahol25

Reputation: 3211

We use OR-mapping for this, so our solution for this looks different. But one way to do accomplish the same thing could be to use an abstracted persistance context which deals with concrete database stuff, inject this into all the service classes that use it and create your unit of work from this context at the 'process' level, and then allow the UoW instance to manage transaction state and connections by collaborating with the context instance.

Something like

        using (var unitOfWork = Context.CreateUnitOfWork())
        {
            // access injected services and repositories to modify stuff

            unitOfWork.Complete();
        }

When actually doing DB-stuff, in your situation you would aquire a DB connection from your (injected) context instance, something like this

        using (var conn = Context.AquireDbConnection())
        using (var command = conn.CreateDbCommand())
        {
            // do db stuff

            command.ExecuteNonQuery();
        }

your concrete unit of work/context implementation should be smart enough here to recognize if the connection returned should be a new or ambient connection, based on if there is an active ambient business transaction there or not.

Obviously, you would need per-request isolation for this mechanism and life time of context instance would be per request.

Upvotes: 0

Related Questions