Designing a Domain-Driven Data Access Layer

Published 23 April 07 07:35 PM

(originally posted to Absolute Opinion on June 4, 2006)

I had started writing a post about best practices for using the different NHibernate collection mapping types when it dawned on me that I may be assuming too much in thinking that we all look at data access layers in the same way. In fact, I think that it is fair to say that most folks who came through the ADO school of data access were trained to think of the data access layer as little more than a set of helper classes used to connect “business objects” to SQL Server stored procedures.

Now, for those of you who still view the data access layer in the way that I described above, this article is not to convince you that you are wrong (even though you are). However, I think it may be fair to say that you may have trouble fitting what I’m going to describe into your way of thinking.

So let’s start with a few very basic goals for a data access layer (DAL).

  • The DAL should completely hide the underlying data access technology, whether it be an ORM tool like NHibernate, hand-generated inline SQL, calls to stored procedures, or anything else.
  • The DAL should not place any significant constraints on the design of the domain model.
  • The entire layer should be replaceable with minimal impact (or minimal risk for you PM folks).

Now, all of the aforementioned goals can be summarized with one word: decoupling. This is a great word – it’s a word that we keep hearing over and over again with every architecture talk and every article. In fact, didn’t the introduction of component programming give us decoupling without any effort on our parts?

In case you were wondering about that last one, the short answer is – no.

So what should a decoupled DAL look like? In .NET programming terms, the answer lies in the references. Given our stated DAL goals, we need to be able to pull out our entire DAL and replace it with another (say one that persists our objects to and from the file system rather than a RDBMS) without causing any impact on the rest of the system. We therefore can’t have our business layer (henceforth called domain model) having any knowledge that a DAL even exists. This is due to the fact that our domain model is designed to be the very core of our system. The objects contained within the domain model form the basic language that our software system uses to describe a real world problem. Put another way, our domain model is (or, should be) a neutral layer in terms of technology concerns. The last thing that we should want to do is create a dependency on a layer whose sole focus is a technology-specific concern (persistence).

So, getting back to observing decoupling through references, what can we deduce from my tangential diatribe above? Firstly, the DAL assembly should hold a reference to the domain model assembly – the domain model assembly should not reference the DAL in any way. Secondly, all other assemblies that need to interact with a persistent store to get and set domain objects will hold a reference to both the domain model assembly and to the DAL assembly. To pull from the SARK Classic project, such a relationship looks as follows:

Now that we have an architectural view of what the DAL should look like, what should it look like at the code level? Firstly, DAL classes should be defined not in terms of “1 DAL class per 1 domain model class” but rather in terms of domain model aggregates. The term aggregate was defined by Eric Evans in his book Domain Driven Design as follows.

“An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each AGGREGATE has a root and a boundary. The boundary defines what is inside the AGGREGATE. The root is a single, specific ENTITY contained in the AGGREGATE.”

So you may be wondering why the terms “aggregate” and “entity” are typed in caps – well, me too – that’s just the way the book was written.

Let’s apply that definition to the SARK Classic sample project. A golf course has a specified number of holes – and each hole has a par value. However, the unit of change from the data perspective is always the course – i.e. – there is never a time when you would access a specific hole directly – you would always access a hole through the course. Therefore, we can say that the Course Aggregate consists of 2 classes: Course and Hole. That is the aggregate boundary. Additionally, Course is the aggregate root. In support of the aggregate boundary, DAL objects should be defined for aggregates – not specifically for domain objects.

Once you have defined aggregate boundaries, you will hopefully find that persistence concerns can be dealt with at a more coarsely granular scope. Dealing with aggregates rather than objects may also help reduce decoupling even within the domain model. I’ve seen that many domain models have unnecessary associations between objects simply for the purpose of pulling down the entire object graph during persistence. In thinking of persistence in terms of aggregates rather than domain objects, you may also see opportunities to get rid of some of these associations and instead simply create additional query methods on the DAL object for the associated aggregate. This idea is also discussed in Eric Evans’ book, so for additional research on the topic of aggregates, it is a must.

Now, when talking about DALs that are written in terms of a domain model rather than a technology like DataSets, critics typically raise a very good point: Why is this design technique worth losing all of the wonderful support that Visual Studio provides for RAD development? In short, it’s not. A colleague once said something very profound – it was something I was already doing, but I like the way that he said it, so I’m going to attempt to quote him. He said, “Render unto the DAL that which is the DAL’s – render unto the DataGrid that which is the DataGrid’s”. Ok, he was talking about NHibernate – and I’m generalizing a bit more – I’m certain that he will forgive me. If you’re building an ASP.NET application using a GridView, you should use the data source providers in the .NET framework to populate that GridView. Your DAL should not be concerned with dealing with tabular data (especially now that the data source providers are in the .NET Framework). Your DAL is concerned with getting and setting domain objects. See? You can have your cake and eat it too!

Finally, let’s spend a few minutes dealing with DAL design as it relates to NHibernate. Again, I want to quickly reiterate that the DAL design style that we’ve been discussing in no way relies on NHibernate. However, there are some major benefits to using NHibernate inside of your DAL.

  • Reflection based mapping allows complete encapsulation of object state, as it can reflect over private members.
  • NHibernate keeps track of the mapped id of an object and can determine whether it is a new object (known as a transient entity) or whether it is an existing persistent object. The NHibernate API then allows you to simply call a method “SaveOrUpdate(object)” – and NHibernate will determine the type of query that needs to be run.
  • NHibernate keeps track of the dirty state of object members.
  • For object graphs where there are several associations, NHibernate can be optimized to use outer joining so that only one query needs to be run rather than running a query for the aggregate root’s data, then running subsequent queries for the other members of the aggregate.
  • Caching anyone?
  • NHibernate supports multiple RDBMSs right out of the box (or zip file)

These are just some of the many benefits to using NHibernate as your data access technology. Over the next couple weeks we will dive into some of the specifics of using NHibernate. However, like I said at the beginning, I want to make sure that we’re all on the same page architecturally before getting into the details.

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Interesting finding - 04/27/2007 « Another .NET Blog said on April 27, 2007 9:25 PM:

PingBack from http://liangwu.wordpress.com/2007/04/27/interesting-finding-04272007/

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About hdierking

I am currently the Editor-in-Chief for MSDN Magazine. I joined Microsoft in 2006 as a product planner with the certification team at Microsoft Learning. Prior to that, I spent my career as a developer and later as an architect. My main technology passions include pretty much anything on language theory, agile development, and service-oriented architecture.
Page view tracker