1

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).

  • Easy, just use persistence model in your data assembly and map them to domain objects later on... – kayess Oct 20 '17 at 11:56
  • @kayess, onion states that domain should not reference infrastructure. – w0051977 Oct 20 '17 at 12:36
  • Well, there must be something that needs to "see" the domain types, be it your mapper or an application service that does the job... – kayess Oct 20 '17 at 12:52
4

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.

  • Thanks for the comprehensive answer. +1. However, how is the session passed from the unit of work to the repository? The unit of work factory creates the unit of work, however unitOfWork.session is private. – w0051977 Oct 20 '17 at 17:05
  • @w0051977: I wouldn't pass the session from the unit of work to the repository. Some other thing needs to create the repository and unit of work so that other thing (the factory perhaps) has a reference to the NHibernate session. – Greg Burghardt Oct 20 '17 at 17:47
  • Thanks. They both have to use the same session. Don't they? – w0051977 Oct 20 '17 at 18:00
  • @w0051977: yes, they both need to use the same session. – Greg Burghardt Oct 20 '17 at 20:16
  • Thanks, so does unitOfWorkFactory have a method to create a repository (or multiple methods if there are multiple repositories) and a method to create a unit of work? – w0051977 Oct 21 '17 at 14:32
2

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.

  • Thanks, however BeginTransaction returns NHibernate.ITransaction, so a reference to NHibernate is still needed. Isn't it? – w0051977 Oct 20 '17 at 13:30
  • No, the methods I was referring to would return only types your domain layer should know about. – Paul Oct 20 '17 at 13:33
  • So what does BeginTransaction return? Thanks – w0051977 Oct 20 '17 at 13:47
  • Whatever you want it to return. Depends on how you're going to use it. The simplest thing would be to create an interface mirroring the calls you're going to make on the transaction object NHibernate exposes (not all it's public methods, just the ones you need), then implement your interface with a thin wrapper around the NHibernate one. Depending on your preferred, though, Begin transaction could return void and handle the details internally. – Paul Oct 20 '17 at 13:53
  • 2
    @w0051977 the UnitOfWork implementation could just store the ITransaction in a field. – Esben Skov Pedersen Oct 20 '17 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.