Architectural principles we will follow

Building an extensible, testable, and maintainable data access layer is a topic very close to my heart. I cannot claim to be an expert on the topic but I have had a fair amount of success with implementing clean data access layer code using NHibernate on different projects. What has helped me get better over time is few architectural or design principles that I have discovered during this journey. I would like to introduce these principles to you briefly as I am going to base most conversations in this chapter and next on these principles. At first, some of the principles sound unnecessarily complex but my experience with these is that in the long run these are always beneficial.

No anaemic domain model

An anaemic domain model does not have much in terms of behavior. You have got classes with relationships between them which correctly represent the business domain. But inside these classes you have got bunch of getter/setter properties and nothing more. Then you have got another set of classes called domain services or manager classes which contain all the domain logic. This not only defeats the purpose of object-oriented programming but also leaves your domain entities as pure data containers and nothing more. We would rather add any business behavior related to an entity on that entity directly. That does not mean that we want to cram every aspect of a business operation into one method on a single class. We may still have classes that handle different aspects of validations, classes that interact with external services, and so on. But core domain logic for any business operation will sit within the relevant domain entity.

We will still have an application service layer in various different forms whose job is to expose the domain logic to consumers. But this layer is thin and only delegates the task to domain layer.

The onion architecture

Most software developers of my age or older have worked on one or more project that embraced layered architecture. Following diagram shows what I mean by layered architecture:

The onion architecture

To summarize layered architecture:

  • Every major concern of the application such as data access, business logic, application service, and UI is modeled as a layer.
  • Layers have compile time and runtime dependency from top to bottom. UI layer depends on application service layer which in turn depends on business logic layer, and so on.
  • Any layer directly depends on the layer right below it and indirectly depends on all the layers below it. For instance, UI layer indirectly depends on data access layer.

Thousands of software projects are executed following this architecture but lately the development community has started realizing the problems that this style introduces in long run. I would like to mention the following two problems with this architectural style which are important from data access point of view:

  • Tight coupling: Every layer is tightly coupled to the layer directly below it. Tight coupling in any form is bad. It limits your opportunities to update the software by making small progressive changes. Such a situation makes the software get outdated too quickly. Tight coupling also comes in the way of unit testing. If you are not doing unit testing then you are missing one of the most important skills required to deliver quality software with confidence. If you are following TDD then you know what I mean.
  • Domain is not at the heart of the application: In layered architecture, domain layer or business layer is not independent. It depends on and is tightly coupled to data access layer which is tightly coupled to a particular database schema. Any changes in database schema or data access layer may have an impact on business logic. Core of your application is business logic and that should be free of any external elements such as database or UI.

Let me introduce onion architecture to you which solves both of these problems. The term onion architecture was first coined by Jeffrey Palermo in 2008 on his blog at http://jeffreypalermo.com/blog/the-onion-architecture-part-1/. Following figure depicts onion architecture in a nutshell:

The onion architecture

To summarize onion architecture:

As against layers, each major component is modeled around concentric circles:

  • Compile time dependencies go from the outermost circle to the innermost circle
  • Domain or business logic is at the centre and hence not dependent on any other components
  • Infrastructure components such as UI and data access sit on outermost circle and no other component depends on them

If you come from layered architecture background then you may find this little hard to digest at first go. But have patience to go through this chapter with me and I hope to convince you why this is better architecture than layered. Two important concepts that help in working with onion architecture style are Inversion of Control (IoC) and externalization of infrastructure. Next section talks in detail about IoC. Externalization of infrastructure lets you identify and remove dependency on pieces of code that are external to our domain model. An example of this is database and data access layer. From onion architecture perspective, database is a place where we store our entities. It is just one step in overall domain logic. Domain logic does not care where and how the entities are stored as long as it can retrieve them back or persist them. Business operations depend on application's ability to retrieve and persist data but do not need to know how it is done or even be dependent on the components that actually persist or retrieve data.

Note

In the preceding figure, there are two layers - application service and domain service. It is important to understand the precise difference between the two. A domain service is where we can add any business logic that cannot be fitted into domain layer. CRUD operations are not usually business logic and they should not be part of domain services. An application service is what external clients/consumers use to talk to your system. Think of WCF or web API-based service layer when you think of application services.

Dependency inversion

I would like to quote very nice description of dependency inversion principle that appears in Bob Martin's article on object oriented design available at http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod. Dependency inversion can be summarized in two simple sentences:

  • High-level modules should not depend upon low-level modules. Both should depend upon abstraction.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

In the previous definition, a high-level module refers to the module that contains the business logic. High-level module is identity of your software. Low-level module is things such as UI, file operations, database access, and so on. Dependency inversion principle states that business logic should not depend on details such as file operations, database access, and so on. This is in line with what onion architecture gave us. But there is one more thing that dependency inversion says – both high-level modules and low-level modules should depend on abstraction. What does that mean?

Suppose you have got a Vehicle class that depends on an Engine class besides some other classes. The Vehicle class has a method Start that encapsulates the logic for starting the vehicle. Starting the vehicle involves performing some checks on different parts of the vehicle and if everything is fine, then calling the Fire method on the Engine class. Vehicle is a high-level module. It contains the logic for starting the vehicle. Engine is a low-level module which provides the torque needed to push the vehicle forward. We may choose to swap one engine for another if we need to. That is the nature of low-level module. They provide a capability that can be replaced if needed.

Following is how this implementation would look like in a world without dependency inversion:

public class Engine
{
  public void Start()
  {
    //Start the engine here
  }
}

public class Vehicle
{
  private readonly Engine engine;
  public Vehicle()
  {
    engine = new Engine();
  }

  public void Start()
  {
    //Perform other checks
    engine.Start();
  }
}

We have got the Vehicle class, a high-level module, dependent on the Engine class, a low-level module. Dependency inversion principle says that this is not right. There are several reasons for why this is not right. This book is not about design principles and hence I will not go into details of those. But there is one reason that I will talk about here. Because of the way our dependencies are structured, it is not possible to swap out one engine for another without modifying the high-level module Vehicle. This is because the Vehicle module is dependent on a concrete Engine class. The Vehicle class is also controlling and instantiating the Engine class, the dependency.

If we introduce an abstraction that represents an engine, then things start looking a bit better. Suppose we introduce the following interface:

public interface IEngine
{
  void Start();
}

With the preceding abstraction in place, we could change our original code to the following:

public class Engine : IEngine
{
  public void Start()
  {
    //Start the engine here
  }
}

public class Vehicle
{
  private readonly IEngine engine;
  public Vehicle()
  {
    engine = new Engine();
  }

  public void Start()
  {
    //Perform other checks
    engine.Start();
  }
}

We still have got one tiny problem in the preceding code. Though the Vehicle class now depends on IEngine, it is instantiating an instance of the Engine class in its constructor. That keeps the dependency of the Vehicle class on the Engine class as is. Ideally, we should be passing an instance of IEngine to the constructor of the Vehicle class, as shown next:

private readonly IEngine engine;
public Vehicle(IEngine engine)
{
  this.engine = engine;
}

The Vehicle class now depends on IEngine. We have completely removed dependency of Vehicle on Engine. We now also get flexibility to choose which implementation of IEngine we want to pass into the Vehicle class. Now we can inject an instance of engine into the Vehicle class and Vehicle is no more dependent on Engine. We have inverted the dependencies.

Note

Dynamically injecting the dependencies through constructor is also called dependency injection. There are tools called IoC containers (Inversion of Control, another word for dependency inversion) that let you configure which dependencies should be injected where and then handle the actual dependency injection at runtime automatically for you. There are several such tools available and Autofac is one of my favorites.

The second statement in dependency inversion principle is not easy to understand at first. What it means is, the abstraction that you use to make the modules depend on, should not know anything about modules that depend on it. Let's elaborate that by extending the previous example. Suppose we have two engine implementations, PetrolEngine and DieselEngine. A petrol engine needs a spark plug but a diesel engine does not. If we add a property on the IEngine abstraction to hold spark plug then that would be a violation of dependency inversion principle. Spark plug is a detail that one of the implementations of IEngine needs. IEngine itself does not need spark plug and hence we should not add it to the IEngine interface.

Next principle is not a standard per say. This is something I have been following and seeing good results when used in incremental development environments or TDD settings.

Explicitly declaring the capabilities required

"Explicitly declaring the capabilities required" is more of a coding style that one can follow. Following this style brings about multiple advantages listed as follows:

  • In the context of designing a class, lets you concentrate on what the class should be doing and abstracting the details that do not belong to the class.
  • With experience, above leads to single responsibility principle.
  • Offers a different way of looking at and thinking about dependencies. This helps in guiding you through your design incrementally instead of doing full design in one big sweep.
  • Lets you concentrate on domain logic at hand than how multiple classes and dependencies would play out at runtime.
  • Helps in identifying and pushing out concepts that do not belong to domain layer.

Let me reuse the vehicle example from the previous section to expand on this principle. Suppose you are asked to build a vehicle. You are working on "start vehicle" functionality and on outset you know roughly what is needed in order to implement the functionality. Now instead of thinking more about all those semi-complete details, you concentrate on domain logic of starting a vehicle and begin coding it in the Vehicle class. The first stab of this could be something as follows which is nothing but comments that guide us through implementation:

public class Vehicle
{
  public void Start()
  {
    //Check all parts are functioning ok
    //If all parts are ok start the engine else return
    //confirm that vehicle has started
  }
}

This is a good start. We have got a class that represents a vehicle and a method on it which when called will start the vehicle. As for starting of the vehicle goes, we now know that it is a three-step process. Let's look at the first step – check all parts are functioning ok. Is the Vehicle class capable of confirming that all parts are functioning ok? Maybe it is. But is Vehicle the right class to make that check? I think not. This is not a responsibility of the Vehicle class. This is a capability that needs to be offered to the Vehicle class in order for it to successfully start the vehicle. So the Vehicle class explicitly declares that it needs this capability. This explicit declaration is through a dependency injected via constructor. Moreover, the Vehicle class does not care how the actual check is performed. All it cares for is that the check is performed and it is supplied with the results in a format that it can understand. So the required capability needs to be declared in the form of an abstraction. Let's say we declare this capability using an interface IConfirmVehiclePartsFunctioningOk:

public interface IConfirmVehiclePartsFunctioningOk
{
  bool AreAllPartsOk();
}

The beauty of the preceding code is that it is such a simple piece of code to understand. We do not know and care (at this point of time), how many parts are involved in starting a vehicle and how they are checked. All we do is call the AreAllPartsOk method on the injected dependency and use the Boolean value returned to determine what to do next. With the previous dependency injected, following is how the Vehicle class would look like:

public class Vehicle
{
  private readonly IConfirmVehiclePartsFunctioningOk partsConfirmer;

  public Vehicle(IConfirmVehiclePartsFunctioningOk partsConfirmer)
  {
    this.partsConfirmer = partsConfirmer;
  }

  public void Start()
  {
    If(partsConfirmer.AreAllPartsOk())
    {
      //Start the engine
      //confirm that vehicle has started
    }
  }
}

Same goes for the other steps - starting the engine and confirming if the vehicle has started. You need to ask yourself whether this logic belongs to the Vehicle class. If yes, go ahead and code it; if not, then explicitly declare this as a capability that the Vehicle class needs. Next time someone looks at the Vehicle class, they would know what external components the Vehicle class needs in order to function correctly. We also get all the advantages of dependency inversion principle that we just discussed. Following is the complete Vehicle class code with all capabilities declared:

public class Vehicle
{
  private readonly IConfirmVehiclePartsFunctioningOk partsConfirmer;
  private readonly IEngine engine;


  public Vehicle(IConfirmVehiclePartsFunctioningOk partsConfirmer, IEngine engine)
  {
    this.partsConfirmer = partsConfirmer;
    this.engine = engine;
  }

  public void Start()
  {
    if(partsConfirmer.AreAllPartsOk())
    {
      engine.Start();
      ConfirmVehicleStarted();
    }
  }

  public void ConfirmVehicleStarted()
  {
    //confirm that vehicle has started
  }
}

Confirming that vehicle has started is not pushed out as a capability required. This shows that though everything can be pushed out as a capability required, not everything needs to be pushed out. Always ask yourself, whether that particular piece of logic belongs to the class you are building. This will also help maintain single responsibility that the class delivers.

Unit of work

Unit of work is a programming concept that enables grouping database operations and executing them together as one unit. A unit of work implementation is expected to offer a guarantee that all database operations specified in the unit finish successfully. If any operation does not finish for any reason then unit of work should provide a way to roll back the changes made to the database state. Concept of unit of work is very important from business point of view. It is not desirable to leave the database in an inconsistent state when a business operation cannot be finished for any reason.

At relational database level, unit of work is equivalent of a transaction. You put all your SQL statements inside of the BEGIN TRANSACTION and END TRANSACTION statements. Database server then guarantees that either all SQL statements are executed or none are executed, leaving the database state intact. While this is good from theoretical point of view, this is no good when it comes to practical use. The main issue with this pattern is that it forces you to execute all state affecting operations together into a transaction. In code we do not have a notion of SQL statements or transaction. All we do in code is create new entities or update/delete exiting entities. NHibernate tracks the changes we are making to entities and issues appropriate SQL statements. Moreover, we do not make changes to entities in one place. During execution of a particular business operation, there may be several updates to state of different entities and these updates may be coded in multiple difference places. How can we make sure that these changes are treated as a unit of work? NHibernate's ISession and ITransaction interfaces are to the rescue here.

ITransaction implementation of NHibernate makes use of transaction capability of database to provide a better implementation of unit of work. And ISession intelligently tracks changes being made to entities and puts all the resulting SQL commands inside of unit of work provided by ITransaction. Following code snippet shows a very simple version of ITransaction and ISession working together to provide a unit of work pattern:

var session = //acquire session instance
try
{
  session.BeginTransaction();

  //different business operations here

  session.Transaction.Commit();
}
catch (Exception)
{
  if(session.Transaction.IsActive)
      session.Transaction.Rollback();
}

We begin a new transaction by calling the BeginTransaction method on ISession. This internally signals NHibernate that any database updates that result out of entity state changes from this point onwards need to be considered as part of a unit of work. The transaction created is held in an instance of ITransaction. This is returned to our code and internal copy is saved by ISession and made available to use via the Transaction property. We choose to discard the returned copy because we want to use the one on ISession. We then go on making series of changes to entities using the same ISession instance. When we are done, we call the Commit method on transaction to tell NHibernate that it should go ahead and synchronize with database the changes made to entities. At this point, NHibernate generates required SQL statements, wraps them in a database transaction statement, and sends to database for execution. If any error occurs at any point, we can call the Rollback method. This would roll back any changes that may have been made but not committed.

Note

If a transaction is disposed without calling the Commit or Rollback method, then NHibernate automatically calls the Rollback method. This also means that there is actually no need to call the Rollback method explicitly because we would not call the Commit method when there is an error and straight away dispose the transaction. NHibernate can then call Rollback. But it is better to explicitly call Rollback as a good coding practice and help future developers of your application understand things better.

Here I have used a try…catch block to show how to roll back database operations in case of errors. This is not required. You can use any mechanism to detect that an error has occurred and transaction is still active before you roll back the transaction.

I want to bring to your attention the following two important aspects that may not have been conveyed accurately by the preceding code:

  • Calls to BeginTransaction, Commit, and Rollback do not need to appear in the same method or even in the same class. They can appear anywhere in your code as long as they are called. This gives tremendous flexibility when writing your code without forcing yourself to compromise on code quality/design and still being able to use unit of work.
  • Use of NHibernate transactions is orthogonal to using ISession to save, update, or delete entities. You continue to use ISession for these operations as you would have used it whether a transaction is in force or not. Transaction aspect can be handled independently and completely outside of your domain logic code.

Though ISession and ITransaction offer an implementation of unit of work, lot of developers choose to write their own implementation in order to have better control. We will look at both ways in this chapter.

That completes our list of architectural principles which may be useful in designing better applications using NHibernate. We would now move into implementing some simple features using our NHibernate knowledge and keeping these principles in mind. We may not go through end-to-end implementation of these features. We would mainly concentrate on parts that affect data access layer and its use in other layers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset