Introduction
Is NHibernate as an ORM tool or framework better suited for a data centric application or for an application developed by applying domain driven design? Well, that should NOT be the question here. NHibernate can serve equally well both approaches. But which one would you choose?
A data centric design
The modeling of the data structure is the center piece of this kind of design. The database is the most important part of our architecture. The DBA plays a very important role.
Data Layer
That's ok for simple problems like managing a collection of customer addresses and nothing more. But it miserably fails as soon as we get into more complicated business. Let's e.g. assume that all of the sudden our address management tool needs to be extended to a full fledged CRM solution.
Possible Problems
- Management night mare with possibly hundreds of stored procedures
- No clear structure in the data. It seems that each entity (or table) has more or less the same importance
- We concentrate to much on the data and not on the business processes and/or functionality
Our principle questions are focused around
- Do we have all data that we need (tables and columns there in)?
- Did we normalize the data sufficiently?
Instead of
- What do we want to automate and why do we want to do so?
- How can we measure that we have achieved what we want (criterions!)?
User Interface
On the other hand we concentrate too much on e.g. data binding on the GUI and how to best present (too much) data on the screen for the user.
A sample taken from a real application
This is a sample from a real application (although simplified). At the beginning there was a data model (ERD) which was elaborate by two persons in a "heroic" several men month long effort. Let's show an extract of this data model
It might be clear after staring a while on the ERD that we want to manage employees and their assigned tasks. It's also clear that an employee is a person and that it has an address and two different titles. Further on each employee can have an associated photo. Finally we see that the data regarding an employee is split into two different tables [Employee] and [EmployeeDetails] obviously for scalability reasons. Since photos can become very large (compared to the rest of the data) they are kept in a separate table [EmployeePhoto] which possibly resides in separate database file on another disk.
Stored Procedures
Traditionally Microsoft has suggested us to write stored procedures to access and manipulate the data in the various tables. So usually one would write the following kind of stored procedures for each table (here as an example we use the Address table)
- usp_AddressInsert(...)
- usp_AddressUpdate(...)
- usp_AddressDelet(...)
- usp_AddressSelectById(...)
if we look at a table like [Employee] we would probably have many more stored procedures representing various querying scenarios, e.g.
- usp_EmployeeSelectAll()
- usp_EmployeeSelectActive()
- usp_EmployeeSelectByDepartment(...)
- etc.
Data access objects (DAO)
Now having the stored procedures in place we write a DAO for each table to produce a object oriented wrapper around the database (the business object should not know anything about the database). We thus will have the following class for e.g. the Address table
public class AddressDAO
{
public void Insert(...) {...}
public void Update(...) {...}
public void Delete(...) {...}
public Address GetById(...) {...}
}
A domain driven design
The modeling of the business processes is the center piece of this kind of design. The so called domain model is the center of our architecture. The domain expert (a representative of the customer with deep knowledge of the domain for which the application is planned) plays a very important role during all the development phases (analysis, design and implementation). The development often is more evolutionary and goes in "cycles". It's often a so called agile application development.
Distilling the ubiquitous language
Together with the domain expert we want to find and use the ubiquitous language which describes best the business we are talking of. In our simple case this ubiquitous language is "hidden" in the following fragments of a discussion
"... So we want to keep track of our employees. Especially we want to know where the employee lives and how we can contact him at home as well as in the office. To better identify an employee (and for other usage as well, e.g. to apply for a business travel visa) we want to have a photo of him in the system. ..."
"... yeah, each employee has a business card of course where he's title and job description is marked... thus I guess we have to store this information - we call it title - in the system as well since the relevant text for a new business card shall be generated automatically... By the way, the titles should be available in German and in English. ..."
"... each employee has a list of tasks to carry out during his daily work. This list should be maintained and updated by the system. Tasks can be produced automatically by the fact that some event occurred, e.g. there is a redemption of a bond. Other tasks are a result of a contact with a customer. And last but not least the group manager can define some tasks for an employee or a group of employees. ..."
"... as an employee I want to see a list of my open tasks. I want to also see if tasks are over due and which tasks will occur in the near future. ... I also want to see the priority of each task and what it's current status is. ..."
I have marked in bold the key terms for our simplified domain. These nouns will be good candidates for entities or value objects.
A first model of our domain
We can immediately locate two hot candidates for entities. These are employee and task. We also immediately grasp that a employee might have a list of associated tasks. Every thing else is just detail for the moment. So let's draw a nice diagram
Refining the model
Up to now our model does not contain much structure and business logic. Let's start with the question how we could better model the
employee entity. At the moment the employee contains just a bunch of properties and no clear internal structure is visible. To find out e.g. whether an instance of employee is in a valid state is rather cumbersome. Comes the
value object to the rescue! What about this?
In the above model I have tried to group properties which belong together. Let's take as an example the address. A valid address is represented by an address line, a postal code and a city (in the real world we would also have a link to a country there). An address line alone does not really make sense. Again: we can validate an address only if we have the whole group of relevant properties at hand. On the other hand it does not really make sense to put the address validation logic into the employee entity itself. Thus the extraction of a new class - the address class - is certainly an improvement. In the terminology of DDD the address is now a value object. It has no identity by itself and belongs to and characterizes an entity (the employee). A value object can be recreated any time by just copying the content of its properties. Further on a value object is immutable. Once created it cannot be changed. Two instances of a value object are equal if the content of their fields match. A value object can never be in an invalid state.
Let's have a look at the code of the address class then
public class Address : IEquatable<Address>
{
public Address(string addressLine1, string addressLine2, string postalCode, string city)
{
if(addressLine1==null)
throw new ArgumentException("Address line 1 cannot be undefined.");
if(postalCode==null)
throw new ArgumentException("Postal code cannot be undefined.");
if(city==null)
throw new ArgumentException("City cannot be undefined.");
AddressLine1 = addressLine1;
AddressLine2 = addressLine2;
PostalCode = postalCode;
City = city;
}
public string AddressLine1 { get; private set; }
public string AddressLine2 { get; private set; }
public string PostalCode { get; private set; }
public string City { get; private set; }
public bool Equals(Address other)
{
if(other==null) return false;
return AddressLine1 == other.AddressLine1 &&
((AddressLine2==null && other.AddressLine2==null) ||
(AddressLine2 != null && AddressLine2 == other.AddressLine2)) &&
PostalCode == other.PostalCode &&
City == other.City;
}
public override bool Equals(object obj)
{
return Equals(obj as Address);
}
public override int GetHashCode()
{
return string.Format("{0}|{1}|{2}|{3}",
AddressLine1, AddressLine2, PostalCode, City).GetHashCode();
}
}
Note that all properties are read-only to account for the fact that a value object is immutable. Thus an address can only be constructed through its constructor. Note also that I have implemented the interface IEquatable<T> and overridden the two methods Equals and GetHashCode to be able to compare two instances of type Address for equality. Pay attention on the special treatment of the AddressLine2 property in the Equal method. It's the only property that can be null. All others properties cannot be null. This requirement is enforced in the constructor where I check each non-nullable parameter and throw an exception if the requirement is not met.
The above code is a typical sample of how one would implement a value object.
I think it makes sense that also the Employee entity is always in a valid state. A possible solution how to achieve that is to also make all properties of the entity read-only and force any modification to use dedicated methods. I would also provide a constructor which expects all mandatory values, e.g.
public Employee(Name name, Title title1, Address homeAddress, Address officeAddress,
Contact homeContact, Contact officeContact)
{
if (name == null)
throw new ArgumentException("Name of employee cannot be undefined.");
if (title1 == null)
throw new ArgumentException("Title 1 of employee cannot be undefined.");
if (homeAddress == null)
throw new ArgumentException("Home address of employee cannot be undefined.");
if (officeAddress == null)
throw new ArgumentException("Office address of employee cannot be undefined.");
if (homeContact == null)
throw new ArgumentException("Home contact of employee cannot be undefined.");
if (officeContact == null)
throw new ArgumentException("Office contact of employee cannot be undefined.");
Name = name;
Title1 = title1;
HomeAddress = homeAddress;
OfficeAddress = officeAddress;
HomeContact = homeContact;
OfficeContact = officeContact;
}
Once an employee exists in the system and I want to e.g. change its home address then I use a dedicated method of the employee class
public void ChangeHomeAddress(Address newHomeAddress)
{
if (newHomeAddress == null)
throw new ArgumentException("Cannot change home address of employee to undefined.");
HomeAddress = newHomeAddress;
}
This method again asserts that after the updating the employee instance is again in a valid state.
Summary
For me it's obvious that the domain driven approach is definitely the better way to go when implementing an application which models complex business processes. A more data centric approach may still make sense when dealing with a forms-over-data type application which does not involve complex business processes. But even when implementing a data centric application I would certainly never ever use stored procedures to access the data. I would instead use an ORM tool like NHibernate to access the database. The only exception I can see for justifying the use of stored procedures is in reporting scenarios where some massive data mapping, filtering and/or aggregation might be needed which is best done directly on the database server (in-process for maximum speed).
Enjoy
