Java Tips #01 — Handle validation in an elegant manner using chain of responsibility design pattern
Hi ! In this article I would like to present you how business requirements validation can be handled in a “stylish” way.
Case study : User registration form validation
You may say that it’s easy and boring, and you’ll probably be right — but that’s something that every Java developer has implemented at least once in the past, and that’s why I decided to choose this topic. Whether you are using an anemic domain model, hexagonal architecture or other architecture model, at some point you will have to perform validation — it might happen in your REST controller, before you will pass the request down to the services layer, or if you are using ports & adapters architecture in your facade.
Below examples come from a Spring Boot application — basic knowledge of Spring and Hibernate frameworks will be useful.
Let’s describe basic business requirements, which have to be fulfilled in order to sign up a new user.
- Valid registration data — first name, last name, email, username and password
- Username has to be unique
- Email has to be unique
Basic classes to store user’s form and validation result
These properties can be wrapped in SignUpCommand class. I like to use *Command classes, because there I can define when the command’s properties are valid and I can simply make them immutable using @Value annotation.
I am using javax.validation constraints annotations to verify whether command object is valid or not. Annotation Value from Project Lombok was used, because I wanted command object to be immutable. Registered user’s data should be the same as the data filled in a sign up form.
To store validation result, you can use a wrapper class like this:
In my opinion it’s quite convenient method return type and actually it’s better than throwing an exception with validation message.
To “chain” validation steps, you can use a class like… ValidationStep ! It is a superclass for these validation steps, which we want to “chain” with each other.
Validation chaining example
Let’s introduce an interface for sign up validation:
And now we can implement it, using the classes defined above and the chain of responsibility pattern:
What is happening here?
- DefaultSignUpValidationService is a class which implements the SignUpValidationService interface.
- Annotation @Service is being used to turn this class into a Spring bean.
- Annotation @AllArgsConstructor from Project Lombok is being used to generate a constructor with UserRepository.
- UserRepository will be injected in the generated constructor. UserRepository simply extends JpaRepository from Spring Data JPA.
- In validate method I’ve used Chain of Responsibility design pattern, to chain all of the validation steps. Let’s describe them one by one.
CommandConstraintsValidationStep — in this step the command object is being validated. All of javax validation annotation will be verified. It’s very handy that we don’t have to write these validators on our own. If an object is valid, then we let the flow go to the next step. If not — ValidationResult will be returned immediately. In this case, I’ve decided to perform validation manually, but it can also be done automatically using @Validated annotation — You can check it out in this article on Baeldung.
UsernameDuplicationValidationStep — this step verifies … if username is duplicated ! If it is — then invalid ValidationResult will be returned immediately.
EmailDuplicationValidationStep — email duplication verification. Because there’s no next step, if an email is unique, then ValidationResult.valid() would be returned.
I defined all of above steps as private static inner classes, because it’s an implementation detail how the validation will be performed. It is a better idea to test only the public methods — validate(SignUpCommand command).
By doing so, at any point you can add/edit/remove validation steps, or make a refactor and stop using this pattern at all. Because your tests will verify only the interface contract, you won’t have to refactor tests too, even in contract stays the same.
Comparison to other approaches: Throwing exceptions
I can see this pattern being used often. Generally using exceptions to change the flow of code execution is a bad practice. Also it is worth considering, if such common operation as validation of business requirements should throw an exception, when data provided by application user is not valid ?
Returning booleans / validation wrapper classes
It’s a better approach, because it does not use exceptions, but in my opinion it’s too repetitive and it makes this method unnecessarily big.
In my opinion this “style” of writing validation is more elegant than throwing exceptions or returning booleans. You can split and move validation logic to separate classes. It is easy to read what are the steps of validation. It’s not the common usage of this design pattern, but I find it very useful in this situation.
Please let me know if you liked this article :) If you prefer other approaches to validation, do not hesitate to share them in the comments!
Words by Aleksander Kołata, Senior Full Stack Developer at Altimetrik Poland
Copywriting by Kinga Kuśnierz, Content Writer at Altimetrik Poland