views:

143

answers:

2

I'm writing an ASP.NET MVC application using NHibernate as my ORM. I'm struggling a bit with the design though, and would like some input.

So, my question is where do I put my business/validation logic (e.g., email address requires @, password >= 8 characters, etc...)?

So, which makes the most sense:

  1. Put it on the domain objects themselves, probably in the property setters?
  2. Introduce a service layer above my domain layer and have validators for each domain object in there?
  3. Maintain two sets of domain objects. One dumb set for NHibernate, and another smart set for the business logic (and some sort of adapting layer in between them).

I guess my main concern with putting all the validation on the domain objects used by NHibernate. It seems inefficient to have unnecessary validation checks every time I pull objects out of the database. To be clear, I think this is a real concern since this application will be very demanding (think millions of rows in some tables).

Update: I removed a line with incorrect information regarding NHibernate.

+1  A: 

To clear up a couple of misconceptions:

a) NHib does not require you to map onto properties. Using access strategies you can easily map onto fields. You can also define your own custom strategy if you prefer to use something other than properties or fields.

b) If you do map onto properties, getters and setters do not need to be public. They can be protected or even private.

Having said that, I totally agree that domain object validation makes no sense when you are retrieving an entity from the database. As a result of this, I would go with services that validate data when the user attempts to update an entity.

John Rayner
Thanks for the quick response and clarifications.
manu08
A: 

My current project is exactly the same as yours. Using MVC for the front end and NHibernate for persistence. Currently, my validation is at a service layer(your option 2). But while I am doing the coding I am having feelings that my code is not as clean as I wish. For example

public class EntityService
{
    public void SaveEntity(Entity entity)
    {
        if( entity.Propter1 == something )
        {
            throw new InvalidDataException();
        } 
        if( entity.Propter2 == somethingElse )
        {
            throw new InvalidDataException();
        }  
        ...
    }
}

This makes me feel that the EntityService is a "God Class". It knows way too much about Entity class and I don't like it. To me, it's feels much better to let the Entity classes to worry about themselves. But I also understand your concern of the NHibernate performance issue. So, my suggestion is to implement the validation logic in Setters and use field for NHibernate mapping.

Wei Ma
OK, but won't NHibernate need to use reflection to access the fields (I don't really want to make them public) which would also be slow? Is it also using reflection to access public setter properties?
manu08
It's using reflection for both properties and fields. The reflection is cached as much as possible when the session factory is build, to minimize the performance penalty once it's running. The disadvantage of this approach is that is takes 5-15 seconds to build the session factory during startup of the application.
Paco
As Paco said, using reflection IS slow. However it's at the cost of the .NET framework. It has nothing to do with database query. As long as you are using NHibernate, you will have to be prepared to paid the initial start up price. My suggestion will help to improve the load/store performance by removing the validation logic.
Wei Ma
Thanks Wei. I think I will be going with moving the validation to a service layer above the domain. This also helps in the sense that all of my validation can happen in one spot, since some validation will require knowledge of the other domain objects (e.g., no two accounts can share the same email address).
manu08
Ha, that make sense. If your validation requires inter-object knowledge, then the service layer idea looks more attractive. Maybe I should reconsider my intention for refactoring my code, too. :)
Wei Ma