The validation is defined as a classic cross cutting concern… well… if we are talking about that we need to validate something everywhere I can agree with that definition, but if we are talking about the validation of domain entities I don’t agree.
The validation of a domain-entity shouldn’t happen in the persistence-layer nor in the presentation-layer and even less in the persistence itself.
Validate something in the DB (persistence) is too late and you will have some unneeded round-trips, by the way, in some very special cases is acceptable and even inevitable (read it as optimistic-lock for example).
Validate in the persistent-layer can be useful if you want a “short-cut” where put the validation (for example if you modify an entity instance, somewhere, and you don't exactly know where put the validation of the new state).
Validate in the UI, well… there you can validate only what you are showing.
The natural place for the entities-validation is the service-layer (service in DDD meaning).
If I think so, why I like NHibernate.Validator so much ?
The matter is that, in the service-layer, I’m needing to validate entities-instances provided by NHibernate and I need a validation system that know how NHibernate work with entities (perhaps, soon, somebody will realize it for EF4).
If you are using something else, than NHV, to validate complex-entities-graph (with valid associations, valid collections and so on), be careful and have a look to what is happening with SQL-Queries (activating log4Net for the SQL, or testing NH-Statistics, or using NHProf, or whatever).
That said I can begin explaining some integration points leaving you the choice of its usage.
The “integrator”
The class you must use to perform the integration is NHibernate.Validator.Cfg.ValidatorInitializer.
In the ValidatorInitializer there are only two extension-methods and before write this post I wrote the documentation of both methods; now you can read it there.
A full integration may look like:
var nhvConfiguration = new FluentConfiguration();
nhvConfiguration
.SetDefaultValidatorMode(ValidatorMode.UseExternal)
.Register(Assembly.Load("Dll.Where.ValidationDefAre")
.ValidationDefinitions())
.IntegrateWithNHibernate
.ApplyingDDLConstraints()
.And
.RegisteringListeners();
var nhibernateConfig = new Configuration().Configure();
validatorEngine = new ValidatorEngine();
validatorEngine.Configure(nhvConfiguration);
nhibernateConfig.Initialize(validatorEngine);
About the ValidatorEngine and the Shared-Engine-Provider you can read in: Diving in NHibernate.Validator.
Events integration
The integration of events is basically the registration of a IPreInsertEventListener and a IPreUpdateEventListener. If you, or the NHibernate’s auto-flush, are going to save or update an invalid instance you will receive an InvalidStateException (there is happening something else, but don’t worry).
DDL integration
The DDL integration is more DBA oriented than everything else… yes DBA oriented… even to make happy the DBA in yourself (I mean some old-school DBA).
In practice, having something like this:
public class Entity
{
public int Value { get; set; }
public string Description { get; set; }
}
public class EntityValidation : ValidationDef<Entity>
{
public EntityValidation()
{
Define(e => e.Value).IncludedBetween(10, 99);
Define(e => e.Description).NotNullableAndNotEmpty().And.MaxLength(50);
}
}
with a poor persistence mapping like this
<class name="Entity">
<id type="int"/>
<property name="Value"/>
<property name="Description"/>
</class>
if you will create the schema, using NHibernate’s SchemaExport, the script will be:
create table Entity (
id INT not null,
Value INT null check( Value>=10 and Value<=99) ,
Description NVARCHAR(50) not null,
primary key (id)
)
as you can see you have a perfect tuning, like in a concert, with the DataBase, even for the CHECK-CONSTRAINT of the property Value, and, if you want, you can even create some special Trigger (your old-school DBA will be euphoric with that).
What is wrong there ?
First of all, now you have transformed a very nice and flexible software validation in something written in the stone (now somebody should maintain the constraints, even in the DB, when you will need to change the range of allowed values).
Second, and perhaps even more catastrophic… in the past week I saw a NHibernate’s user euphoric because he found a way to integrate DataAnnotations, with NHibernate, through Fluent-NHibernate; his example was using the RequiredAttribute (NotNullable in NHV)… very cool!!! no ? well… now try to apply it in a graph mapped with table-per-class-hierarchy, where some subclass have a not-nullable property, and let me know which will be the result (only for this reason we should have a SoftNotNullableAttribute).
The matter is that, for the same reason you are not using stored-procedures to do everything, you shouldn’t integrate your nice software validation with the underling DDL of your RDBMS.
The FashionThe fashion/elegant part, of the integration, is that you can start defining your domain model with POCOs then externally map the persistence, then externally map the validation integrated with persistence, and, as a kind of magic, your POCOs will checked before persist anything; all without any kind of invasion in your existing code.
but it’s only a
kind of magic, I’m not so sure that it’s
so beautiful.