Question

Please see the question here, which I asked yesterday: Domain Driven Design in an Onion Architecture

As stated I understand that DDD is an approach that targets the domain layer only and Onion is an architecture for the entire system i.e. core, infrastructure etc.

Onion suggests that all interfaces in the application should be contained in the domain layer as described here: https://stackoverflow.com/questions/16861127/onion-architecture-business-services-interfaces-and-implementation and also described in my question from yesterday.

Now please see the Unit of Work interface here: https://codereview.stackexchange.com/questions/55127/unit-of-work-repository-nhibernate. Here is an excerpt of the code:

 public ISession Session { get; private set; }

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        Session = _sessionFactory.OpenSession();
    }

Therefore I have to create a UOW interface that has a reference to Session and a reference to SessionFactory. Therefore I have to install NHibernate in the domain layer, which seems to be a big no no.

What can I do in this scenario? Please don't advise me not to put the interface in the domain layer as the domain layer contains a domain service similar to this one: http://www.zankavtaskin.com/2013/11/applied-domain-driven-design-ddd-part-4.html (Domain Services can access the database via a repository in a Domain Service).

Was it helpful?

Solution

The Unit of Work is the transaction. The challenge is making sure the NHibernate interfaces and classes are not referenced outside of your data access layer. The unit of work interface should not have any NHibernate references, something like:

public interface IUnitOfWork : IDisposable
{
    void Begin();
    void Commit();
    void Rollback();
}

The concrete implementation of this interface could require something from NHibernate, for example:

public class UnitOfWork : IUnitOfWork
{
    private ISession session;
    private ITransaction transaction;

    public UnitOfWork(ISession session)
    {
        this.session = session ?? throw new ArgumentNullException(nameof(session));
    }

    public void Begin()
    {
        if (transaction != null)
            return;

        transaction = session.BeginTransaction();
    }

    public void Dispose()
    {
        Rollback();
    }

    public void Commit()
    {
        if (transaction == null)
            return;

        transaction.SaveChanges();
        transaction.Dispose();
        transaction = null;
    }

    public void Rollback()
    {
        if (transaction == null)
            return;

        transaction.DiscardChanges();
        transaction.Dispose();
        transaction = null;
    }
}

The only references to NHibernate are in the constructor and private fields. A factory object will need to give you the unit of work, but your outside code should not need any references to NHibernate.

using (IUnitOfWork uow = unitOfWorkFactory.Create())
{
    uow.Begin();
    // do stuff
    uow.Commit();
}

In the code above, nothing is referencing NHibernate.

The Unit of Work pattern gives you transaction control while decoupling the implementation of those transactions from the rest of the application.

OTHER TIPS

Even though you said not to, I’m going to tell you that your use of ISession is an implementation detail, and should not be exposed to the domain layer.

The domain should be agnostic of how (or even if) your repository is physically persisting entities. ISession, as you say, is an NHibernate specific implement detail.

So how to solve? Well, your UoW interface should just expose just the methods it needs to do to accomplish its job, things like Commit() and BeginTransaction(), depending on what mode you prefer. How it goes about committing might be implemented by an ISession under the hood, but not exposed in the interface.

Licensed under: CC-BY-SA with attribution
scroll top