Unit of Work Factory

by Scott MIllett 5. February 2011 11:35

I was answering a post over at the ASP.NET forums on the unit of work pattern which I thought I would follow up on here.

For those of you that don't know the Unit of Work pattern, here's how Martin Fowler defines it...

"[The Unit of Work] Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems."

By the way I also cover the unit of work pattern in my ASP.NET Design Patterns book Wink.

Now that we know what the pattern is how can it help us out? Let's use the domain of borrowing a book from the library. The code below shows a simple command handler that handles requests for members borrowing a book.

public class BorrowABookHandler : ICommandHandler<BorrowABookRequest>
    {
        private readonly IBookRepository _book_repository;
        private readonly IMemberRepository _member_repository;

        public BorrowABookHandler(IBookRepository book_Repository, 
                                  IMemberRepository member_repository)
        {
            _book_repository = book_Repository;
            _member_repository = member_repository;
        }

        public void Handle(BorrowABookRequest borrow_a_book_request)
        {
            var book_to_loan = _book_repository.find_by(borrow_a_book_request.book_id);
            var member = _member_repository.find_by(borrow_a_book_request.member_id);

            member.borrow(book_to_loan);

            _book_repository.save(book_to_loan);
            _book_repository.save(member);
        }
    }

The issue with the code however is that if there is an exception when a member is saved (line 21) then the model is left in an invalid state due to the fact that the book_to_loan has already been saved and is marked as being loaned out. This business case of loaning out a book involves updating the member and the book being loaned, this process needs to happen as one atomic action, i.e. if there is a problem when saving the member then we should roll back the changes to the book so its not marked as loaned out. This is where the unit of work pattern comes in.

Consider the revised code below.

public class BorrowABookHandler : ICommandHandler<BorrowABookRequest>
    {
        private readonly IBookRepository _book_repository;
        private readonly IMemberRepository _member_repository;
        private readonly IUnitOfWorkFactory _unit_of_work_factory;

        public BorrowABookHandler(IBookRepository book_Repository, 
                                  IMemberRepository member_repository,
                                  IUnitOfWorkFactory unit_of_work_factory)
        {
            _book_repository = book_Repository;
            _member_repository = member_repository;
            _unit_of_work_factory = unit_of_work_factory;
        }

        public void Handle(BorrowABookRequest borrow_a_book_request)
        {
            var book_to_loan = _book_repository.find_by(borrow_a_book_request.book_id);
            var member = _member_repository.find_by(borrow_a_book_request.member_id);

            member.borrow(book_to_loan);

            using (var unit_of_work = _unit_of_work_factory.create())
            {
                _book_repository.save(book_to_loan);
                _book_repository.save(member);
            }
        }
    }

    public interface UnitOfWorkFactory
    {
        UnitOfWork create();
    }

    public interface UnitOfWork : IDisposable
    {

    }

In this example a UnitOfWorkFactory is injected into the BorrowABookHandler class and is used to wrap the calls which persist the member and book.

The following code shows an example of the UnitOfWork implementation using NHibernate.

public class NHUnitOfWork : UnitOfWork
    {
        public void Dispose()
        {
            using (ITransaction transaction = SessionFactory.GetCurrentSession().BeginTransaction())
            {
                try
                { transaction.Commit(); }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    throw;
                }
            }

        }
    }

There are lots of other ways to implement the unit of work pattern such as hooking onto the begin request/end request of a custom httpmodule, or creating a transactional command handler to wrap the BorrowABookHandler and any others that are involved in a business operation but I will cover that and other methods to achieve the same result in a following post. However I like the way the code reads and the concern that it is demonstrating.

Take care of yourselves, and each other.
Scott

Tags:

Design Patterns

Pingbacks and trackbacks (1)+

Comments are closed

About Me

Hello my name is Scott Millett, this is my blog designed to capture my thoughts on development. You can find out more info on me by checking out my linked in profile below.

View Scott Millett's profile on LinkedIn

Coming soon...

Awards

MVP Logo

ASP.NET MVP
2010-2011

Month List

Powered by BlogEngine.NET 2.0.0.36 - Eco Theme by n3o Web Designers