One of the most important aspects of any application code is to ensure that we are having correct data before it can be processed or shared with any other system. This ensures that we are working with valid data and avoid any issues which can result due to data inconsistencies.
Problem at hand
We spend considerable time in checking things like
- Possible Null references
- Empty Array
- Empty Strings
- Invalid values
The amount of these checks can rise exponentially in case we are dealing with huge model objects. For Example:
class Car {
List<Tyre> tyres; // Required
List<Door> doors; // Required
}
class Tyre {
int tyreSize; // Mandatory and non-zero
String tyreMake; // Optional
}
... and similarly for Door class and so on.
One way to make sure we have proper data is to put such restrictions in object creating itself using parameterized constructors. However, there are certain cases where we can’t restrict object creation in case we are having incomplete data.
One such case can be calling an external service in which objects are created post getting a response from the service. In such cases I want my application to get the response and create the objects(even if they have inconsistencies). Only once this is done I can then check for data completeness and can inform our clients for possible data unavailability with proper error messages.
Classically we used to put multiple if-else statements to cover such negative cases.
if(tyres != null && !tyres.isEmpty()) {}
Again, you can imagine how the amount of these IF checks can grow in case we have many such restrictions or have quite verbose model objects.
Solution
To solve this we can use bean validations which I will be showing here. You can download the complete code from my GitHub and can try it out. The demo here is coded using the Micronaut Framework but similar ideas are available in Spring and other popular frameworks.
Bean Definition
@Introspected
class Car {
@NotNull
@NotEmpty
@Valid
List<Tyre> tyres; // Required
@NotNull
@NotEmpty
@Valid
List<Door> doors; // Required
}
- @Introspected – To enable Micronaut bean introspection.
- @NotNull – The data should not be null
- @NotEmpty – The collection should not be empty
- @Valid – Enables a deeper level of validation. A property marked will Valid will enable bean validation for the type of that property. For example, if I put @Valid on Tyre tyre, this will make sure that the validation step will check all the annotation which are placed inside the Tyre class.
Bean Validation
Set<ConstraintViolation<Car>> violations = validator.validate(car);
This for me is the power of bean validation. With this single statement, I have checked my whole object for all possible data inconsistencies. In addition to this, if I have @Valid on my instance variables, I am checking the complete object irrespective of how verbose is my object composition.
Now I can check if my violations set is empty and if it is I can process/transmit the data as it is valid.
In case I have violations, the ConstraintViolation will give me details like what property contains the invalid value and what is the wrong value.
Hope this helps. I think once you have made yourself accustomed to such validations you will be using it in almost every project.
All the best and Happy Coding!