Jimmy Bogard presented a well written argument for always keeping validation out of entities in a domain driven design except in "the absolute worst case." But, IMO, there is simply no black and white verdict on where validation must live in a DDD application and must be carefully considered at the beginning of any project, taking into consideration the motivations behind validation, the scope of the project, the maturity of the development team, and the balance between philosophical purity and practical maintainability.
There are two clear project scenarios which come to mind when I try to answer where validation should live. The first is wherein a DTO layer is used to transfer information between the view and domain layers. For example, you may have a Customer object in your domain layer and an associated Customer DTO in another layer to which you map the Customer information, to then give to the view for presenting Customer information to the user. The second scenario is a project wherein the entities within the domain layer are shared directly with the view to present the data to the user. For example, instead of maintaining a separate DTO layer and mapping domain object to the DTOs to be given to the view, the view could be given the Customer object directly. So instead of talking via a separately maintained DTO to show a Customer's name, the view would simply ask the Customer object itself for the information.
The primary benefit of a DTO layer is that it provides a clean separation between the view and the domain model. (This is admirable and even ideal from a theoretical perspective.) The major drawback is that you end up maintaining a nearly object-for-object replicated layer, reflecting the properties of the entities and value objects found within the domain layer. This layer can become quite cumbersome and resistant to change as a project enters maintenance. The latter approach, wherein the entities are exposed directly to the view, removes the surgical separation of the view's knowledge of the domain, thus eliminating the need to maintain DTOs for every bit of information given to the view. The major drawback to this is that a developer could start doing all of their business logic in the view itself (within server side scripting tags anyway) and that an entity could be put into an invalid state if they're also being used to capture data directly from a form. The former is a reflection of developer maturity and team agreement. (But if you have developers trying to do their business logic within server side script tags in the views, you'll likely have bigger worries than debating the finer points of the art of DDD.) The latter, wherein an entity can end up in an invalid state, is cause for further conversation...
Is it still domain driven design if an entity can enter an invalid state? The core of domain driven design is that the primary focus is on the domain and domain logic. Accordingly, the model is a reflection of the domain. But does allowing an entity to enter an invalid state preclude the model from being a reflection of the domain, or that the domain is no longer the primary focus of the project?
Suppose your business accepts purchase orders via fax machine. This purchase order is an entity, albeit a paper one at that. Now suppose that someone faxes you a purchase order but forgets to add their email address, which you require to fulfill the purchase order. Is it no longer a purchase order simply because they've left off their email address? I would argue that it's an incomplete purchase order that cannot yet be fulfilled until the remaining details are captured; i.e., it's an entity in an invalid state. Accordingly, it's not one you're going to want to take action on and "file away." That doesn't mean that your business is flawed or that you have to yell at the person who placed the order (analogous to throwing an exception). It also doesn't mean that you must maintain two forms, one for the user to submit the information on, and another for you to copy the information over to if the first form was 100% completed. You'd simply ask the person for the additional information and proceed, accordingly. The point is that allowing an entity to enter an invalid state should not be seen as being mutually exclusive from developing a domain driven application.
So where validation lives depends on which approach you take to your application. If you maintain a DTO layer, then the DTOs which capture information from forms should be validated and you should have additional logic in place to ensure that your entities can never enter an invalid state. The easiest way to do this is to expose a constructor which accepts the minimum amount of information for the entity to be in a valid state and to use design-by-contract rules to ensure that the arguments passed to the constructor are consistent with a valid state, accordingly. You could also pass the DTO object itself to the constructor and make sure the DTO is valid before settings its values to the properties of the entity being constructed.
Alternatively, if you do not maintain a separate DTO layer and expose entities to the view for consumption and updates, then it is necessary to maintain the validation within the entities themselves to ensure that they do not get persisted in an invalid state and to communicate to the user what needs to be done to get the entity into a valid state.
Earlier, I mentioned that maintaining a separate DTO layer is the correct approach from a theoretical perspective. But it comes with a cost to maintainability, simplicity, and readability. Exposing the entities directly speeds development and makes the design easier to change down the road (since you don't have to keep two layers in synch with each other) but diminishes the purity of separation between view and domain and allows entities to be in invalid state, temporarily as they may be. Accordingly, you must consider the balance between philosophical ideals and practical maintainability on any project. When it comes down to it, the client that you're developing for couldn't care less which approach you're taking...as long as it gets them their solution delivered in a timely manner, with a high level of quality, and which they're not going to have to pay an arm and leg to have changes made to it down the road. It's up to you and your team to determine which approach is most appropriate for the task at hand, meeting the goals of both the client and the development team, while considering what's in the best interests of the long term maintainability of the project.
Even though I'm suggesting that there's no hard rule to which approach you should take with respect to domain/DTO organization and the appropriate location of validation, I do have a suggestion for a rule of thumb. For very small and simple projects (e.g., a small ecommerce store or an Access-like CRUD app), the active record pattern is a solid approach and you should adhere to the validation guidance of whichever implementation you use (e.g., Castle ActiveRecord). Alternatively, for a large proportion of web-based business applications, which fall into the large, middle portion of a normal curve if you will, maintaining validation within the domain layer is perfectly adequate while still supporting a maintainable, domain driven design. (The use of NHibernate Validator attributes within entities is fine in these situations, IMO.) Finally, for very large and/or complicated projects, or one with a large team (7 or more?) separated into very discrete roles, maintaining a separate validatable DTO layer, separate from the entities, and keeping entities from ever entering an invalid state is likely a good approach. (Jimmy Bogard's AutoMapper is a great tool for assisting with transferring information between DTOs and entities BTW.)
So while the use of a DTO layer and separated validation from entities is appropriate in some situations, and ideal from a philosophical perspective, the use of validatable entities is very practical and maintainable in a good proportion of web application scenarios as well. When it comes right down to it, it's up to your team to decide which approach is best for the project at hand.
Billy McCafferty
Posted
02-17-2009 1:08 PM
by
Billy McCafferty