Geeks With Blogs
It should just work...

Whether we like it or not, unlike conventional engineering structures like bridges and buildings, software will always be engineered and built to be changed at some future point. It is the nature of the business requirements that we are there to serve. Everything changes. Constantly.

Software Engineering is the science we busy ourselves with in order to cope with these changes, and a lot of patterns and practises have evolved as the artifacts of our constant quest to accommodate changes in our programs.

I've recently been assigned the task of extending some legacy C# back-end server code. The code used typed datasets and stored procedures for data access. Over time, with lots of hasty maintenance being performed, the line between business logic and data access have blurred to the extent that the code has become both extremely hard to understand and prone to regression bugs. Everyone knows that such code doesn't lend itself to being extended nor re-used.

I would like to present an application design pattern here which proved to produce an elegant implementation with the following benefits:
  1. Seperation of complex business logic and data access concerns.
  2. Generic business object design that supports re-usability.
  3. Lends itself to dependency injection.
  4. Unit-testability through the use of RhinoMocks.
From the bottom-up: The data access layer.

There are many arguments these days against stored procedures. It is a common conclusion amongst most application designers that the benefits of using an ORM are far superior to those of stored procedures. There are, however, certain production environments where stored procedures still have a place, and the reasons for this are many, sound for the most part, and not the primary focus of this article. I will rather be addressing the question of how to structure your application so that your business object layer is data access-agnostic. Whether you are stuck with using datasets, or are in a position to use an object-relational mapping(ORM) technology like NHibernate or Entity Framework, I believe the pattern I present here will prove equally beneficial to your project.

Also, the poison here is the easily learned traditional view of the domain object, which in many peoples minds "knows how to persist itself". This little phrase captures the essence which i'm trying to combat in this article. I believe it is this view which often destroys the seperation I'm advocating here, without which, you are left with, more often than not, an unmaintainable mess.

How to prevent writing data access logic into your Business/Domain object layer

Rule #1: Wrap your Data Access objects

I've seen some sources on the subject on the web playing down the importance of these very simple wrapper classes citing that it is a lot of work for not much benefit, however, this is the very LEAST of what you must do in order to bring about this most important of seperation between concerns. Whether you are using datasets or mapped NHibernate entity objects, you MUST resist the temptation to reference these entity/dataset objects directly in business logic code. The way to achieve this is to write a wrapper class around these often volatile database objects, and reference the wrapped object instead. I say it is the least of what you must do because I now arrive at the point for motivating my rule #2:

Rule #2: Program all your business logic against an interface of the domain object, not the domain object itself

Why an interface?
  1. It allows you to re-use business logic between implementations with different data requirements.
  2. Those familiar with RhinoMocks should be smiling right now...(for the rest: in general RhinoMocks only allows you to mock interfaces)
Re-use of business logic? Suppose you have an object that needs to be manipulated by a processing pipeline, for instance: an accounting application has to make an Order book entry. If my domain process class is OrderWriter, and I make it operate on an IOrder instead of a concrete Order implementation, do you see the opportunity to potentially use OrderWriter to be able to populate two different kinds of orders, both implementing IOrder? As you can start to visualize, my data access object wrapper object implements IOrder(amongst any others I may choose), an instance of which gets passed to OrderWriter at run-time. When OrderWriter calls IOrder.Save(), the business logic is completely agnostic to how this happens - I have my all-important seperation.

Caveat 1: How do I construct something which is an interface?
Suppose my OrderWriter only uses IOrder's and needs to create a new one? Use the well-known Factory Class pattern. In OrderWriter, in order to construct a new order, I call IOrderFactory.CreateInstance(). In my client code of OrderWriter, I create a concrete implementation MyOrderFactory that implements IOrderFactory, that knows how to construct a new concrete ClientOrder, which implements IOrder. So OrderWriter now looks like this:

class OrderWriter
     public OrderWriter(IOrderFactory orderFactory)
          mOrderFactory = orderFactory;

      void WriteOrder(IOrder order)
           IOrder oppositeLeg = mOrderFactory.CreateInstance();

     IOrderFactory mOrderFactory;

As you can see, our domain class now becomes a dependency injected, unit testable class, where the data access part is easily mocked out by testing the method with a mocked-out IOrder.

var order = MockRepository.GenerateMock<IOrder>();
var orderFactory = MockRepository.GenerateStub<IOrderFactory>();
orderFactory.Stub(x => x.CreateInstance().Return(MockRepository.GenerateMock<IOrder>());

order.AssertWasCalled(x => x.Save());

and so on.

In my own application, the data access object wrapper class(the one actually implementing IOrder) ended up delegating storage to datarow fields from each of the implemented IOrder property setters, and returning the datarow value in return in the property getter. This way the database access code is nicely isolated from and invisible to the concerns of the business layer.

Posted on Tuesday, April 17, 2012 4:52 PM | Back to top

Comments on this post: A simple pattern to separate Business Logic from Data Access.

No comments posted yet.
Your comment:
 (will show your gravatar)

Copyright © cdpcodingblog | Powered by: