The IoC mind set: Validation
I think that I mentioned that I believe that having a good IoC container at your disposable can make you really shift the way you are structuring your code and architects solutions.
Let us take for instance Validation as an example. The two examples that jump to mind are Castle.Components.Validation and the Validation Application Block. They both have the same general approach, you put an attribute on a property, and then you use another class to validate that the object meet its validation spec.
This approach work, and it is really nice approach, for very simple input validation. Required fields, length of a string, etc. Being able to declaratively specify that is so very nice. But it breaks down when you have more complex scenarios. What do I mean by that? After all, both libraries offers extensibility points to plug your own validators...
Well, as it turn out, when we go beyond the realm of simple input validation, we reach into the shark filled waters of business rules validation. Those rules are almost never applied across the board. Usually, they are very targeted.
Business rule: A customer may only open a service call for products that the customer has purchased
Expressing this rule in code is not very hard, but it doesn't benefit much from the above mentioned libraries.*
Nevertheless, you want to have some sort of a structure in your validation code. You want to be able to add new validation easily. Here is the back of the envelope solution for this. Hopefully it will make it clearer why I think that having IoC makes it so very easy to handle things.
public interface IValidatorOf<T> { void Validate(T instance, ErrorSummary errorSummary); } public class Validator { IKernel kernel;//ctor injection, not shown public ErrorSummary Validate<T>(T instance) { ErrorSummary es = new ErrorSummary(); foreach(IHandler handler in kernel.GetHandlers(typeof(IValidatorOf<T>))) { ((IValidatorOf<T>)handler.Resolve(CreationContext.Empty)).Validate(instance, es); } return es; } }
And yes, that is the entire thing. Come to think about it, I think that we need to add GetAllComponentsFor<T>() to Windsor, but that is another matter. Now, if I want to add a new validation, I inherit IValidatorOf<T> with the right T, and that is it. It gets picked up by the container automatically, and the rest of your code just need to call Validator.Validate(myServiceCall); and handle the validation errors.
* Actually, a standard way to report validation errors is very nice.
Comments
Really nice approach. One stupid question though: how do you reuse validation logic between validators? Suppose you have duplicate validation code in two or more validator classes. Do you use Specification classes for this, and then use these specifications in the validators?
This is an interesting discussion. I think that maybe part of the issue that crops up is how we name things. Clearly, the input validation that the Castle Validators and the VAB handle is a completely different beast than the "validation of business rules" you speak of.
So, how about we don't call both "validations" in the first place? To me, the second scenario you present could be more accurately described as "Enforcing Business Constraints".
Jan,
There are rarely reuse in those cases, because the situation is usually unique.
But a base class would do the job just fine.
In VAB you can do this by using the self-validation capability. You can flag an attribute on a method that has logic to perform the cross property / business validation. Whenever you create a validator for that type it will automatically invoke the self validation method.
A nice idea.
How about turning on and off validation rules depending on the state of the object, and on the actions of user?
Some examples:
If product type is "book", then ISBN field is required, otherwise it is not required.
If product type is "car", then engine number is required, otherwise it's not required.
These are very simple examples, there are much more interesting than thse :)
Another interesting topic very much related to busines rules are bussiness actions. Say, we have a form for managing a ticket. Now what bussiness would want to have is if ticket type is "feature request", and if this ticket status is still "not approved" for two weeks, then send an email to the author of the ticket, and to the head of help desk department.
Personally, i see this very related to validation rules.
Darius,
isn't that part of the Validation logic? I would implement that as part of the specific validation rule, not of the Validator.
Business actions IMO completely unrelated to validation. The main difference is that validation is applied after changes only. Model data is expected to remain valid once it was successfully validated and invalid data will not be saved.
Workflows (or business actions) are not only applied when data is changed. They might be applied anytime, by any other component or subsystem. Such actions can make data become invalid without changing it.
The only thing that's similar is that both validation rules and workflow items can be stored in similar manner in an IoC-Container.
-Markus
Wouldn't kernel.GetHandlers require that the IValidatorOf<> service type is set for all the validation components? So, assuming that one uses Binsor, there would be a separate reflection loop or condition that registers validators using this specific interface?
Can kernel.GetAssignableHandlers be used instead, so one would not have to distinct validation components from others in configuration file? Or there is a significant overhead in GetAssignableHandlers?
And another thought about a situation when there are many business classes that derive from some (abstract) base class, and there are some validations that apply to this base class. Then one can introduce an inner loop for getting validators for base types as well.
Oren
Any tips on the best place to start to learn about IoC.
Books, websites, sample apps....?
even though you ask the question to oren,
i would like to answer it
http://msdn2.microsoft.com/en-us/library/aa973811.aspx
This is a great article written by Oren. Uses windsor in implementation. A sample is far omre better than theory so you'll gain a lot from this article.
@Stiven,
See also Alex Henderson's tutorial series:
http://wiki.bittercoder.com/(X(1)S(3hr4rlju4pqj2o3iyaxxre45))/ContainerTutorials.ashx
Darius,
The type is registered in the container so when the validator of type book is requested the container will return the correct service to validate a book.
Here is an example:
<component id="book.validator"
service="Example.IValidator`1[[Example.Book, Example]], Example"
type="Example.Validator`1[[Example.Book, Example]], Example">
</component>
I have used a similar scheme before for validation. While it worked really well, the one downside was that it was difficult to prioritize the validation results.
For example, one validation may fail because something is missing, and then another validation fail for the same basic reason, but a different symptom.
No easy, maintainable solution, because each isolated validation does not have sufficient information to prioritize itself over others.
Thanks for the pointers to the articles
I have built experimentally a validation framework where exactly ORDER was handled well, but things get complex very soon.
The fwk will parse the code and detect dependencies between validation methods and invoke them in the "logical" order...
Thats one nice, simple but very flexible solution. I like it! Good cobination of Castle features...
Comment preview