<groan>
Not this argument again!</groan>
The short answer: I don't know.
The long answer: Yeah, I really don't know.
There is still no widely accepted answer on this one. Reasonable people disagree. One camp pushes it up closer to the UI, the other down lower so it shelters the domain from invalid states. Some like it sprinkled across layers; some like it centralized.
I think Steve Sanderson is correct that validation falls on a continuum from easy UI validation, like required fields, to complex business rules that need to be checked closer to or in the domain entities.
I also think Jimmy Bogard is correct that context matters when it comes to validation. This object is valid for what? Saving to the database? Emailing to sales? Displaying on the screen? The validation rules depend on the the behavior you are validating.
These days, I think of validation as either UI Validation (required fields, min/max length, etc.), Domain Validation (start date/time must be before end date/time, order must have at least one item, etc.), or Database Validation (is this value unique, is this ID value going to throw a foreign key violation). This is based on when you can validate in the call chain more than what it's valid for. I still like validation in context, but those business rules end up in my Domain Validation logic with method names like CanSendEmailToCustomer()
.
Sharp Architecture uses NHibernate Validators as attributes on the domain entities. The best thing about this is your validation is centralized. It's sitting right there in your domain, and you know where to go if there is a rule change.
The other good thing about this approach is your entity should always be valid when saved. The built-in Repository<TEntity>
calls in Sharp Architecture enforce validation before inserts and updates, so you're covered there. If you want to call MyEntity.IsValid()
in a method, you can do that as well.
The bad thing is those validation attributes can start looking like UI concerns when they have user-centric error messages in them ("Hey, user, that's not a valid format for a phone number"). And if there is a rule that doesn't lend itself to attributes (e.g., orders must have at least one order item), you are on your own.
I started with validation attributes on my domain entities, and when my attribute messages got too UI-looking, I moved to using Data Annotations on DTOs in my application service layer for UI validation. The DTOs are made to be client facing, so I'm not bothered by user-specific messages in them.
Then when Domain or Database Validation problems come up, a ValidationException
is thrown up through the application service to the UI so the calling app can either display or handle it. I haven't played with it yet, but I think this is what xVal is doing, too.
The more I think about it, the more I see my web app as just another client calling into my application service layer which is encapsulating my domain. So my web app is the "user" and the message needs to be just enough for that user, not necessarily the end user. Maybe a validation exception token is sent out from the application service layer to the UI, and the UI translates that to a string from a very UI-centric resource file?
Bu I'm still not happy with this. I feel like I have the right kind of validation in the right places, but I would still like a more centralized approach for easier refactoring of business rules. I've used the Specification Pattern before, and it does a great job of centralizing the rules. But making yet another class for yet another required field and adding it to yet another Composite Specification? Seems like your walking away from a lot of the productivity attributes can give you.
Obviously, my thoughts on validation are still not fully formed. I'm still arguing with myself. My dream validation would be:
- centralized for easy refactoring and rule changes
- automatically called for routine things so I don't forget (like saving the order to the database)
- allow ad hoc calls for non-routine things (like validating the order confirmation email can be sent to the customer)
- handle simple validation like attributes so I can be productive
- handle complex validation like specifications so I'm not limited
- UI-agnostic so the centralized validation goo isn't holding end-user strings, except when I'm too lazy to set up the resource file, in which case strings can be passed up from this layer
I told you I didn't know! :)