Hi,
I am writing application with many different presentation platforms (web mvc is one of them), which implies that all functionality should be served through service layer and all validation should happen inside that layer. This is done using JSR-303 within AOP aspect, which throws custom runtime exception containing list of violated constraints on failure. This enables presentation layer to catch validation exception and provide verbose feedback to user - in case of web mvc this is done through translation from ConstraintViolation to spring BindingResult instance (just like in SpringValidatorAdapter) and adding errors to model.
In code this looks like that:
Question 1Code:public class ValidationException extends RuntimeException { public Set<javax.validation.ConstraintViolation> getViolatedConstraints(); } @Service public interface SomeService { public void SomeAction(DomainObject someObject) throws ValidationException; } @Controller public class SomeController { public String handle(/* ... */) { /* ... */ try { someService.SomeAction(someObject); // display success } catch(ValidationException ex) { BindingResult convertedBindingResult = MyValidationUtils.toBindingResult(ex.getViolatedConstraints(), someObject, "someObject") map.add(convertedBindingResult.getModel()) // display failure } } }
Is this architecture correct?
/Question 1
Everything seems correct, though, I have some problems due to separation of data binding and business validation. There may be some validation errors (namely type mismatches) detected in data binder and the question is how to handle them?
For example imagine simple add user controller. User submits form filled with data, which spring web mvc binds to my domain User object. Unluckily real user entered string into integer type field, resulting in data binding error on Age property. Nevertheless, the object is passed to controller as method argument and controller is about to do its job. It should send user
back to registration form with all errors listed. All errors, not just type mismatch messages, but also business violations on correctly entered fields (like not unique email address, name or some more complex rules like having to register exactly between 03:15 and 03:16AM). The problem is it should call the service layer, since validation logic is there, but on the other hand it shouldn't, because if data binding error occurred on non-required field, then service layer will happily proceed, since that property value will be null.
The problem in code:
Question 2Code:@Controller public class SomeController { public String handle(@ModelAttribute SomeObject someObject, BindingResult bindingResult) { // what to do with bindingResult?! try { someService.SomeAction(someObject); // display success } catch(ValidationException ex) { BindingResult convertedBindingResult = MyValidationUtils.toBindingResult(ex.getViolatedConstraints(), someObject, "someObject") map.add(convertedBindingResult.getModel()) // display failure } } }
How to handle binding errors with validation done in service layer?
/Question 2
Solution A
Simply don't call service layer if binding errors are detected. However this is not acceptable, because user will be informed only of type mismatch errors during first submit and business violations for correctly entered fields on second (e.g. not unique email, which is business level requirement). This is not user friendly and can lead to frustration.
Solution B
Add additional parameter to service layer method to pass previously detected violated constraints. Service method then checks if business validation rules were violated or any violations were passed. Additionally those passed violations should make validator to stop checking @NotNulls on certain properties, since there will be nulls due to data binding failure.
Is there any better way? I just don't believe, that there is no simple way of doing it, since this seems like a pretty common usage.
Regards
Adrian


). The problem is it should call the service layer, since validation logic is there, but on the other hand it shouldn't, because if data binding error occurred on non-required field, then service layer will happily proceed, since that property value will be null.
Reply With Quote