Wiring the Validation Logic
Having the validation logic separated out in a validator abstraction is important, but, I think the more important part and the most confusing part of implementing validation is the wiring of the validation logic with the rest of the application. Follow the comments section of Colin's posting and my last statement will justify for itself. Various readers have posted their observations on how to wire the logic within the scope of the various tiers of a typical Web based application.
One of the pioneers in this wiring has been the Rails' in-model validation, which Cedric sees as an improvement over validation tied to the presentation framework. Rails offers validation logic as part of the model, which wires itself nicely within the framework and gets invoked automagically before persistence of the model object. Rails' validation engine offers some amount of context sensitivity as well through the protected hooks like
validate
, validate_on_create
, validate_on_update
, which the application developers can override to plug in custom rules. There can be complicated use cases where this model is not a 100% fit, but as DHH has mentioned - this is one of those "most people, most of the time" things. We're not after the "all people, all of the time" kind of framework coverage. Quite in alignment with the Ruby philosophy :-).In the Java community, we do not have the clairvoyance of the Ruby world, we do not have a DHH to give us the dictum. The result is inevitable and we have Commons Validator, Struts, WebWork (powered by XWork validator), RIFE, Spring, Tapestry, Stripes, Wicket and many other variants implementing the same practice in their own different ways. It's time for a JSR that will bring harmony to the chaos of implementing the validation logic across all tiers of the application - enter JSR 303: Bean Validation!
Validators - Separate Abstraction ?
The main point in Colin's blog was to advise people to identify *all* the rules that define *valid* data, and the uniqueness of that data is just as much a validation rule as saying the username must not be null. He speaks about the necessity of a validator abstraction, which is provided so well by Spring. In one of his responses, he mentions
It simply boils down to this; identify *all* the validation logic and then apply it in a single coherent, atomic, explicit operation. Validators are powerful things, they can do more than just the *syntax* of the data, they can, and should also check the *semantics* of the data.
But, should we have the Validator as an abstraction separate from the domain object or follow the Rails philosophy and club 'em together with the domain object ? Working with Spring, I would like to have validators as a separate abstraction for the following reasons :
- Validators are tier agnostic and can be used to collaborate with multiple layers -
- Business Layer for enforcing business logic on "semantically valid" objects
- Persistence layer to ensure valid objects get persisted
- MVC layer to enforce data binding from request parameters
- Validators in Spring have their lifecycles controlled through the IoC - hence these singletons can be nicely wired together through DI
- Spring MVC requires validators as separate objects
Validators - Collaborating across Layers
In his blog, Colin goes on to say that he does not want to get down into whether this validation should be applied at web layer, or middle layer, or both etc. But, as I mentioned above, the collaboration of the validators with the participants of the various tiers of application is an area where people get confused the most.
Let me try to summarize my understanding of how to engineer the validator abstraction across the application layers, so that we can reuse the abstraction, avoid code duplication and have the validation logic tied to the domain model (since it is the domain objects that we are trying to validate). My understanding will be based on the implementation of Spring MVC, which does a nice job of engineering this glue.
Step 1: Form a Validator Abstraction
The domain class has an associated validator class. This is a deviation from the Rails implementation, but this allows a nice refactoring of the validation logic into a separate abstraction than the domain class - in fact the wiring of the validator with the domain class can be done through mixins (or as we say in the Java world - the interceptors). I think XWork engineers validators based on this interceptor technology.
Step 2: Wire Controllers to Populate Domain Object
In a typical Web application, we have Controllers that represent a component which receives
HttpServletRequest
and HttpServletResponse
instances just like an HttpServlet
and is also able to participate in an MVC workflow. Spring offers a base interface Controller
, while Struts has the notion of an Action
. This controller intercepts the request and creates a Command object out of the request parameters. The framework takes care of this object creation and population through the usual JavaBeans engine of property setters and getters and additional property editors (as in Spring). This Command object can be the domain object itself or it may have the domain object wrapped in it - the application developer knows how to get the domain object out of the command object.Step 3: Validate the Domain Object Before Submission
Once the controller has successfully populated the command object, it executes all the validators registered to validate the object. Instead of automatically triggering the validation as part of data binding, Spring also offers callbacks to do the same as post-processing of the binding phase. The following snippet is from the Spring samples (jpetstore):
import org.springframework.samples.jpetstore.domain.Account;
protected void onBindAndValidate(HttpServletRequest request,
Object command, BindException errors)
throws Exception {
// command object
AccountForm accountForm = (AccountForm) command;
// domain object (POJO)
Account account = accountForm.getAccount();
// validate the object
getValidator().validate(account, errors);
...
}
The above strategy describes how the validation logic is bubbled up from the domain layer and is used by the controller to send error messages to the user from the web tier. All the validation logic is centralized in the validator abstraction and is executed through
validator.validate(..)
and the errors being propagated through the exception structure.Conclusion
- Validators are domain level objects.
- Validators are separate abstractions from the domain classes.
- Validators can be "mixed-in" through interceptors if required.
- Validators encapsulate "syntax" as well as "semantics" of the domain object.
- In a typical Web application, validators can be wired together with Controllers, DAOs etc. to provide services at the other tiers of the application. Yet they are not coupled with any abstraction of the other layers of the application.
1 comment:
Spring has org.springframework.validation.Validator
why does the Spring example not use that approach?
Post a Comment