What I've loved most about developing an open-source project is the ideas that I get from others who look at the work and either A) validate ideas, B) suggest that something stinks, or C) call a royal WTF and force people (e.g., me) to explain ideas more fully. It's usually during these explanation attempts that light bulbs start to come on and ideas are refactored and become more substantiated (or at least more defendable). The S#arp Architecture forums have been a gold mine, IMO, for the discussion of the practical application of balanced DDD techniques on real-world projects. After feedback that I've already gotten on my recent post concerning application services and CQS and listening to and adding my two cents concerning ideas on the S#arp forums, I'd like to share a few ideas (that I shared on the forum) concerning drawing the line among application layers, the management of DTOs and View Models, and a general discussion of separation of concerns. (As Chris Carter correctly (and a tad bluntly) explained, don't take these ideas at face value...use them as a point of discussion and contemplation to come to your own conclusions with your project team...and let me know what you think.)
Every great developer knows that any problem can be solved by adding another layer of abstraction. ;)
Obviously, with additional layers of abstraction and indirection, we also introduce complexity and additional objects which need to be maintained. Over the past year, I have had struggles concerning just how far the "DTO paradigm" should be taken to effectively separate layers from the domain and "best practices" for how they should be used. I don't think the answer can be found in a best practice (but I also hate to leave it simply at "it depends"). I truly feel that the most important practice is for a project development team to decide, and firmly agree upon, which approach will be taken on a project and for the agreed upon approach to be followed with discipline (and enforced via code reviews) by the team.
On a couple of recent projects, the biggest problems we ran into weren't concerning which approach to leveraging DTO for layer separation was "best," but on coming to a firm layering decision and having the team discipline to adhere to those practices. Because of these "gray" areas, which we didn't come to a firm team agreement on, there were inconsistent practices used by the developers which led to rot, particularly between the controller (or code-behind) and application services layer.
So while we can discuss which approach is best or most effective, I think the greater concern is for the team to pick a direction, to stick to it, and to diverge or refactor as a team decision.
But as I said, I don't want to leave it at "it depends." ;) So after reading everyone's ideas here and contemplating further on past projects and the ideas I recently put forth in http://devlicio.us/blogs/billy_mccafferty/archive/2010/03/05/better-application-services-and-cqs-with-s-arp-architecture-1-0-q3-2009.aspx, here are a couple of ideas that I've been trying to adhere to (and/or will be adhering to on my next project). The layers referenced below may be viewed in a package diagram here for better clarity of what's discussed.
- Usually, one object should be used to populate a form while a second should be used to collect data from a form. The former would contain information for default/selected values along with drop-down values, for examples, while the latter would encapsulate which options were selected when the user submitted the form. Personally, I use a <EnityName>FormViewModel to populate the form and use an entity to collect from the form...but I see it equally valid to instead use an intermediary DTO to collect from the form if further separation is warranted. Additionally, I feel it is OK for a FormViewModel object to have references to domain objects to make it easier to transfer data to the form for input population. This is an arguable point and would be just as valid to only pass DTOs in the view model for a cleaner separation between view and domain, with the acceptance that there will be more objects and maintenance that comes with that approach.
- I believe that view models should be maintained either in the application services layer OR in a separate ViewModels class library if a project team feels the additional separation is warranted (although I can't envision much reason to do so). (I'll be modifying my sample project post to reflect the former.) Either way, they should not be maintained in .Core to keep a very clean separation from domain concerns. While the name "ViewModel" implies that they are a view concern, they only describe what should be displayed, not how it should be displayed. Accordingly, there is still a very clear separation of concerns between the view layer (which the user interacts with) and the view model classes. Furthermore, the application services expose methods which should be usable by any type of client. In line with this, a ViewModel or FormViewModel class does not impart a particular mechanism for the implementation of the view layer - again, it only describes what data should be available to the view. So even if view model classes are maintained in app services, this does not preclude there use by various client types; e.g., they could be equally consumed by ASP.NET, ASP.NET MVC, Spark, SilverLight, etc. (The only caveat is that if the view model classes maintain references to domain objects, then it will be more difficult to expose them for serialization via web services and WCF. Accordingly, a project team should firmly decide if ViewModels may contain references to domain objects.)
- I believe that DTOs should be maintained in a separate assembly and have no reference to .Core or be maintained in .Core if warranted with the acceptance of decreased "shareability." DTOs should almost always be serializable; i.e., transferable via web services and WCF. This allows the DTOs class library to be easily shared with a consumer of web services which may expose these objects, for easier deserialization. Some of the motivations for deciding when to use DTOs would be to provide a clean separation between the view and app services (if a project team feels its warranted), to expose data to web service consumers, and to better enable Command/Query Separation.
- Repositories may return Entities, Value Objects or DTOs, in addition to other "basic" types; e.g., primitives, arrays, etc. Ideally, repositories would not be used by domain objects (even via their interfaces) and would have their interfaces maintained in the application services layer. If the repository interfaces are maintained in .Core, then this forces DTOs to be maintained in .Core as well with the possibility that domain references may "leak" into DTOs. If the repository interfaces are maintained in .ApplicationServices, then DTOs may be maintained in a cleanly separated library without worrying about the line between DTOs and domain objects being grayed. But data interfaces (and DTOs) may be maintained in .Core if the team feels the simplification is warranted.
With all this said, I still think the most important decision is deciding a path to take for your project, having the project team agree upon it, and maintaining the discipline to stay on course.
Your feedback on these ideas is most welcome!
03-06-2010 9:41 AM