I've recently implemented an enterprise application using NHibernate. In order to isolate the UI from changes to the domain model, the Data Transfer Object pattern (Fowler 03, MS) was decided upon. This gave us the additional benefit of being able to create our Dto's in such a way as that they would be easily bound to the UI, reducing complexity in the front end and giving us the ability to support different UI's with minimal effort.
The application architecture is something like this:
To achieve this, several hand-coded two-way Assembler classes that constructed Dto's from Domain Objects (aka business entities) and then updated those Domain Objects were written. For simple classes this was an easy approach, and we could have used CodeSmith to generate some of the code for us.
However, due to our reasonably complex Dto's that weren't 1-1 with the Domain Objects our assemblers started becoming incredibly complex. While it is a tradeoff of the Dto/Assembly/Domain Object patterns that the complexity and nastiness associated with the loose coupling everywhere else in the application stack lives in the Assembler, it was a painful enough process to force me to re-consider the approach next time around.
The main difficulty arises when persisting changes made to the Dto. Since the Dto doesn't contain all of the data from the Domain Object in a 1-1 mapping, the process of updating our domain objects goes something like this:
Our main Dto was unable to be retrieved as a single domain object graph, and so calls to multiple Dao's are made inside the assembler to perform the domain object update.
As you can imagine, the Assemblers rapidly became fragile and contained a number of performance issues due to the recursive "chatty" calls across Dao's for the same information.
The alternative approach that was used on a less complex project was to perform the retrieval of the required domain objects outside of the assembler, and pass them all is as parameters to the assembler method. This approach fell over with the complex Dto's due to the service having to know everything about the assembly operation, pass through a massively complex set of parameters (which would have become unmanagable) and most importantly it required that the complete set of domain objects was retrieved even if a single value in the Dto was changed.
Which brings me to today, and I'm no closer to finding a solution that meets our requirements. I have assessed the architectural tradeoffs of the Dto/Assembly/Domain Object patterns in detail, and while not always appropriate it meets the needs of our application and none of the alternatives offer the flexibility and separation that is required.
So, on to the approach!
Three unique scenarios must be managed by the Assembly classes;
This isn't really an issue. A Factory based approach that constructs the parentDto by instantiating a new Dto and assigning empty Dto's to each child property. Items associated with DropDownLists e.t.c are populated in individual Dto's and bound to properties of the ResponseDto object.
Can be time-consuming to write depending on the degree of change between the Domain Objects and Data Transfer Objects, but still not really a major headache. The Services call Data Access Objects in the persistence layer which return domain objects. The Assemblers take domain objects as arguments and construct Data Transfer Objects that usually have a "flatter" structure. Multiple domain objects are usually required to construct a single Dto, and they are not always retrieved in the same object graph which can complicate things.
List items are optionally populated depending on the requirement (e.g. Read Only or Updatable)
The first scenario is for the retrieval of read only Dto's. In this case, only the ID and DisplayValue of the property bound to a list is constructed.
In this case, the entire List is populated, given that in most cases some UI logic is bound to the additional properties of objects within the lists.
Still unresolved and I welcome any thoughts on the approach!
Options being considered at the moment: