A vanilla but powerful way of doing validation in Java

Zefeng Lin
5 min readApr 25, 2021

My simple way of doing validation for software development in Java without using any library

Photo by Sarah Dorweiler on Unsplash

We talk about business logic validation all the time during software development to all teams, all projects, may that be a validation for API contract, validation for domain model, or validation at rest.

It doesn’t seem so important, but the best teams usually bring this up at the earliest stage of their software development lifecycle. Because if you have planned validation right, you would save a significant amount of time in the long term(even after you leave the project). It has been a question for me for many years, and I would like to share with you all my vanilla way of doing this.

The problem

There are 1000+ ways of doing validation in many languages. In Java, there are also tons of libs/frameworks that can do parts of the task in validation. And it takes time to understand all of them and pick the one suit your need.

I think the major problem of using a lib to do validation is that you have to be clear about:

  1. the scope of the lib
  2. what it does and what it’s not expecting to do
  3. what results/exceptions should it throw if things go wrong
  4. how to align that within the team
  5. does it have any documentation for best practices?
  6. And if you want that to play with other framework/libs how would they fit together.

I feel it’s a lot of work. And which software developer doesn't want more free time for your favorite game or sports. So I came up with my vanilla Java way. Simple and powerful.

Example

Let’s say you’re developing a JSON API to create a user. For simplicity, you will have schema like

{
username: immutable, unique, required
email, mutable, unique, required
firstName, mutable, non-unique, optional
lastName, mutable, non-unique, optional
password, mutable, non-unique, required
}

As you can tell, it’s only 5 fields but each field has different business requirements. What kind of framework/lib/methodology would you use to make sure of data integrity?

Here is what I would do in Java (not compilable though).

First, you need a request DTO your chosen framework can serialize the JSON string from request body into so that you get a typed class instead of doing string manipulation later on.

public class CreateUserRequest {
private final String username;
private final String email;
private final Optional<String> firstName;
private final Optional<String> lastName;
private final String password;

public CreateUserRequest(String username, String email, String firstName, String lastName,
String password) {
if (username == null || username.isBlank()) {
throw new IllegalArgumentException("Invalid username blah blah blah");
}

if (email == null || email.isBlank()) {
throw new IllegalArgumentException("Email must be blah blah blah");
}

if (password == null || password.isBlank()) {
throw new IllegalArgumentException("Password must contain blah blah blah");
}

this.username = username;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
}
... getters and if optional use Optional<Type> in the getter
}

It’s pretty verbose right? Part of reasons being Java is verbose by nature but it doesn’t have black magic! People might use some annotation to auto-generate null check but it comes to the cost of learning behavior of the annotation and debugging when it’s generating extra code or not generating code at all.(pitfalls that I’ve been stepped on in the past all the time) Believe me it’s a good amount of time spent upfront and it will reduce tons of code in layers below because it reduces problem space by eliminating ambiguity.

Second, you need a consumer of your request DTO. It could be a service object that bridge your request to data access layer. Using a controller-service-dao architecture has been very popular in the industry for many years and there’s good reasons but recently I ditched it in favor of controller-command-repo architecture. I will post more details about the difference in a later publish.

public class NewUser {
private final String username;
private final String email;
private final String firstName;
private final String lastName;
private final String password;

public NewUser(String username, String email, Optional<String> firstName, Optional<String> lastName,
String password) {
if (username.length() > 10) {
throw new IllegalArgumentException("Invalid username blah blah blah");
}

if (!EmailUtils.isEmail(email)) {
throw new IllegalArgumentException("Email must be blah blah blah");
}

if (firstName.isPresent() && firstName.length() > 15) {
throw new IllegalArgumentException("First Name must be blah blah blah");
}

if (lastName.isPresent() && lastName.length() > 15) {
throw new IllegalArgumentException("Last Name must be blah blah blah");
}

if (PasswordValidator.validate(password)) {
throw new IllegalArgumentException("Password must contain blah blah blah");
}

this.username = username;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
}
}
Note:
1. EmailUtils could be a third-party lib that does common email format validation(I am sure there's tons out there)
2. PasswordValidator could be just a static method within your project that describe requirements from product expectation and meet your company's security standard.

This NewUser is an immutable object in the core of your model that does further business logic validation other than null check. It totally trusts the layer above to give it valid parameters. You might create more model objects like UserName , Password , Email in a real production ready codebase and further break the validation up into each of them but for short I will just use String here and do the validation in the NewUser constructor.

So far we’ve got the NewUser object and hooray it’s already been a long journey. We trust every object we created is valid and otherwise it wouldn’t come this far. Now the problem space is reduced to data persistence.

Remember all previous code is to deal with one single user creation request and it should only care if that request is valid so far. But you have many users already in your system(or I am sure you will). This system could be a database for your user creation service or it could be an identity provider(IDP) that you can register new user to. It might have data integrity requirements in addition to your pure business logic like username/email should be unique key or security requirements like your password should be salted.

Here comes the UserRepository , a layer to bridge your model to the external system.

public class UserRepository {

private final UserPersistence persistence;

public void create(NewUser newUser) {
try {
persistence.save(newUser);
} catch (Exception ex) {
// logging and convert external service exception to your model exception here
}
}

... other domain functions
}

There might not be as much validation in the code above but since your persistence layer is doing the validation for you so you just need to handle those exceptions correctly!

In the End

You may notice that it’s vanilla Java and I don’t use any fancy library. Fewer dependencies are better. Java is already powerful and precise. But my point is

  1. Validation is really a must-have thing in your codebase
  2. Each layer should concern about different types of validation.
  3. Use vanilla Java could be verbose to write but eventually, we want code that’s easy to read, fewer dependencies, and doesn’t have black magic.
  4. You can break up validation into each object and create the object in confidence.
  5. Once you gain confidence creating validated objects, your consumer will also be confident consuming them.
  6. Don’t forget about unit tests!

--

--