views:

546

answers:

4

I dig a lot of things about the DDD approach (Ubiquitous language, Aggregates, Repositories, etc.) and I think that, contrary to what I read a lot, entities should have behavior rather then being agnostic. All examples I see tend to present entities with virtual automatic properties and an empty constructor (protected or worst, public) and that's it. I consider this kind of objects more like DTOs then entities.

I'm in the process of creating a framework with its specific API and I don't want to be tied to an ORM. So I built the domain first (without thinking of persistence) and now I would like to use NHibernate as persistence tool so I added a new project to my current solution to help ensure that my model isn't altered to support NHibernate. This project should be an implementation of the abstract repositories that live inside my domain. And now the difficulties arise.

Since it is my first time with NHibernate (I'm also trying Fluent Nhibernate but it seems even more restricting) I would like to know :

  1. Is it possible to use NHibernate without altering a DDD model that is part of a framework
  2. The things (constraints) that are necessary for NHibernate to work as expected and efficiently (virtual properties, empty constructors, etc.) I think this list would be helpful to a lot of people who are starting to learn NHibernate.

Please keep in mind that I'm building a framework so the Open/Closed Principle is very important for me.

P.S.: Sorry if my english is not good, I'm from Montreal and I speak french.

Edit 1: Here is one problem I have with NHibernate now - http://stackoverflow.com/questions/1516752/how-to-map-type-with-fluent-nhibernate

+1  A: 

The short answer to your question is that it is not possible, but if don't need lazy loading the required alterations are trivial.

No matter what, you will have add default constructors to classes that do not already have them. If you are willing to forgo lazy-loading, those default constructors can be private, and you don't have to make any other changes to your domain model to use NHibernate.

That's awfully close to persistence ignorance.

Having said that, if you want lazy-loading, you'll need to make several changes (outlined in other answers to this question) so that NHibernate can create proxies of your aggregated entities. I'm personally still trying to decide whether lazy-loading is an enabling technology for DDD or if it's a premature optimization that requires too many intrusive changes to my POCOs. I'm leaning toward the former, though I really wish NHibernate could be configured to use a specific constructors.

You might also take a look at Davy Brion's blog (I particularly liked Implementing A Value Object With NHibernate), which is really illuminating if you're interested in domain-driven-design and avoiding anemic domain models.

Jeff Sternal
+3  A: 

In my experience, the only thing that NHibernate requires of a domain is virtual properties and methods and a default no-argument constructor, which as Jeff mentioned, can be marked private or protected if need be. That's it. NHibernate is my OR/M of choice, and I find the entire NHibernate stack (NHibernate, NHibernate Validator, Fluent NHibernate, LINQ to NHibernate) to be the most compelling framework for persisting POCO domains.

A few things you can do with NHibernate:

  • Decorate your domain model with NHV attributes. These constaints allow you to do three things: validate your objects, ensure that invalid entities are not persisted via NHibernate, and help autogenerate your schema when using using NHibernate's SchemaExport or SchemaUpdate tools.
  • Map your domain model to your persistent storage using Fluent NHibernate. The main advantage, for me, in using FNH is the ability to auto map your entities based on conventions that you set. Additonally, you can override these automappings where necessary, manually write class maps to take full control of the mappings, and use the xml hbm files if you need to.
  • Once you buy into using NH, you can easily use the SchemaExport or SchemaUpdate tools to create and execute DDL against your database, allowing you to automatically migrate domain changes to your database when initilizing the NH session factory. This allows you to forget about the database, for all intents and purposes, and concentrate instead on your domain. Note, this may not be useful or ideal in many circumstances, but for quick, local development of domain-centric apps, I find it convenient.
  • Additionally, I like using generic repositories to handle CRUD scenarios. For example, I usually have an IRepository that defines methods for getting all entites as an IQueryable, a single entity by id, for saving an entity, and for deleting an entity. For anything else, NH offers a rich set of querying mechanisms -- you can use LINQ to NHibernate, HQL, Criteria queries, and straight SQL if need be.

Th only compromise you have to make is using NHV attributes in your domain. This is not a deal breaker for me, since NHV is a stand-alone framework which adds additional capabilities if you choose to use NHibernate.

I have built a few apps using NH, and each has a persistence ignorant domain with all persistence concerns separated into its own assembly. That means one assembly for your domain, and another for your fluent mappings, session management, and validation integration. It's very nice and clean and does the job well.

By the way: your English is pretty darn good, I wish my French was up to par ;-).

spot
Good answer. But what is "NHV attributes" exactly, as I am new to NHibernate?
Marcel
http://nhforge.org/wikis/validator/nhibernate-validator-1-0-0-documentation.aspx
spot
+5  A: 
  • For NHibernate:
    • All mapped classes require a default (no-arguments) constructor. The default constructor does not have to be public (it can be private so that it is not a part of the API), but it must exist. This is because NHibernate must be able to create an instance of the mapped class without passing any arguments. (There are workarounds, but don't do that.)
    • All mapped properties for which lazy-loading will be required must be marked virtual. This includes all reference properties and all collection properties. This is because NHibernate must be able to generate a proxy class deriving the mapped class and overriding the mapped property.
    • All mapped collection properties should use an interface as the property type. For example, use IList<T> rather than List<T>. This is because the collections types in the .NET Framework tend to be sealed, and NHibernate must be able to replace a default instance of the collection type with its own instance of the collection type, and NHibernate has its own internal implementations of the collection types.
    • For NHibernate, prefer Iesi.Collections.Generic.ISet<T> to System.Collections.Generic.IList<T>, unless you are sure that what you want is actually a list rather than a set. This requires being conversant in the theoretical definitions of list and set and in what your domain model requires. Use a list when you know that the elements must be in some specific order.

Also note that it's typically not easy to swap object-relational mapping frameworks, and in many cases it is impossible, when you have anything beyond a trivial domain model.

Justice
Do you have any idea what the author of this article (http://davybrion.com/blog/2009/03/implementing-a-value-object-with-nhibernate/) means when he says the following? "Note: NHibernate allows a private default constructor for Value Objects, but for Entities you will need a default public or protected constructor as private is not sufficient."
Jeff Sternal
I don't know what is intended. NHibernate can use reflection to create instances via the private default constructor. When doing so is not allowed, perhaps because the code is running in Medium Trust, perhaps NHibernate will turn to creating a derived class with a public default constructor which simply calls the parent's protected or public default constructor.
Justice
I followed up with him, and it turns out the author considered lazy loading to be a constituent feature of entities (therefore requiring NHibernate to be able to proxy them).
Jeff Sternal
That explains it to. NHibernate would need to generate a derived class, where the default constructor of the derived class would need to delegate to the default constructor of the base class - so the default constructor of the base class must be nonprivate.
Justice
A: 

I would probably layer things like so:

Layer 4: Your Framework with your domain classes

Layer 3: Data Transfer Objects

Layer 2: NHibernate domain classes

Layer 1: NHibernate Repository Implementation

I'd probably want to code gen a lot of that since much of it is just "pass through" to keep clean separation of concerns.

Michael Maddox