Bean Validation in the Service layer (or any other layer you want)

Avoiding code redundancy with programatic validation

Vandeilson Nobre
6 min readOct 11, 2022

When we are developing an endpoint of an API that receives a POJO as a payload, it is expected that we validate the input data, for a number of reasons. And one of the most common ways of doing this is through Bean Validation. It is commonly used in the controller layer (speaking of a MVC application), which will run the validation as soon as the controller receives the HTTP request. It works nicely!

There is only one thing, though: imagine that you could receive an input (a POJO) not only through a HTTP request, but maybe your application could be consuming a Kafka topic, or it could read data from a CSV file, or excel spreadsheet. Or even during your workflow, due to your business logic, you need to instantiate your object and you have to make sure that it follows all the constraints you have stablished for it.

In this case, a possible (and very common) solution is to create a class with a few methods that will run through your object and validate it. Field by field. Again, it works nicely!

The downside of doing this, however, is that you will have code redundancy, since you have the POJO with the bean validation annotations AND a class that does the exact same thing. Thus, in case you need to change/update the POJO constraints, you will have to do this in two different places.

In this article, I am going to show you how to use Bean Validation not only in the controller layer, but also in any layer of your application you might need to. You will have all your validation logic concentrated in one place with a minimum of code!

1 — Getting Started

It is required no more than basic knowledge in Spring boot and Java/Kotlin in order to get the max out of this article.

That's what I'm using right now:

  • Spring Boot 2.7.4
  • Kotlin
  • Postman

1.1 — First things first

I recommend going to spring initialzr website and creating a new project with Kotlin and the Spring Web dependency. Choose maven or gradle as you like (I'm going for Maven)

Now, add the following to your pom.xml:

Please, note that I have specifically used the 6.0.15.Final hibernate-validator version. Apparently there are some incompatibility issues with the 7.X versions, and a solution would be to downgrade the lib for any 6.X (see more at this link, if you want)

2 —Validation in the Controller layer

First of all, let's create our Invoice data class:

Secondly, we must create a controller with an endpoint that will receive an Invoice class as request body. (Note that there is no business logic applied here)

You might have noticed two things here:

A: If you already familiar with the Bean Validation annotations, you might be wondering what the @Sanitized is. Actually, it is an extra annotation that I have created, and I'll soon be teaching you how to make your own custom validations! (I'll provide the link HERE once the article is done, I promise! ❤)

B: When coding in Kotlin you have to use the "@field:" before the annotation. It is not necessary in Java. But that's how it needs to be done in Kotlin, otherwise it won't work.

This happens because some annotations can be used not only in object fields, but also in methods or constructors. And you have to explicitly tell which case is yours. The @Sanitized on the other hand only works in fields, that's why you don't have to make it explicit.

2.1 — Brief explanation on what the annotations do

A quick explanation on the annotations used:

  • Positive: Used in numeric values, it only allows positive numbers, not including zero. PositiveOrZero allows the number 0.
  • Size: Used in Strings, arrays and map, validate how many characters are within the property. You can set a minimum, maximum or both values.
  • NotEmpty: Used in Strings, arrays and map, check if the property is not null or empty (e.g. String with only white spaces).
  • Future: validate if the date is in the future, not including the present. FutureOrPresent includes the present.
  • Sanitized: As mentioned above, is a special, custom annotation. Not included in the hibernate validator. It checks if the input String have some special keywords, for security purposes.

2.2 — Testing the automatic validation in the controller Layer

When we make a POST request in postman, Spring boot automatically deserialize the JSON data, instantiate an Invoice object and run the validation. In case there is any violation to the constraints, it throws a MethodArgumentNotValidException, which I have handled in order to display in a more friendly way. All the error messages are provided to you automatically.

Response of the validation ran through the Controller layer.

3 — Validation in the service layer (or any other layer you need.)

Let's create a second endpoint, which will NOT receive any payload of any kind, and it calls for a method from the service.

In the service class, we need to inject the SmartValidator. It will be responsible for going through your bean and run the validation. Very important to say, you do not need to change any annotation in the Invoice class. It stays the same.

The createRandomInvoice method will basically instantiate an Invoice object, run the validation and, in case there is any constraint broken, throw an exception.

When we use the validate method from the SmartValidator interface, we need to provide the bean which will be validated and the BeanPropertyBindingResult that will store the actual result as a FieldError list.

Iterating that list, we can have access to the field that had its constraint broken, the value that was sent and the default error message.

We, then, create a map where the key is the name of the field and the value is the error message and the value that was sent(that was just my choice on how to handle the list of errors — do as you prefer ❤).

3.1 — Testing the programatic validation in the service Layer

After doing a POST request, we can debug and evaluate bindingResult after the validation is finished so we can actually see all the errors (or an empty list in case everything is fine)

And finally, there is the response in Postman the way we expected it to be.

Response of the validation ran through the Service layer.

As you can see, the automatic and programatic validation work the same way. All you need to do is to make sure your class has all the bean annotations needed, and then you can validate your object in any moment of your workflow, in any layer you need, at any moment avoiding code redundancy.

I do hope that you could learn something new today! Thank you for reading!

--

--

Vandeilson Nobre

I'm a backend software developer, SE student and I absolutely love travelling.