Thursday, October 27, 2016

Effective Domain Model Validation with Hibernate Validator

Let’s face it, domain model validation has always been a pretty untamable beast (and most likely this won’t change anytime soon) because the validation process itself is strongly bound to the context where it takes place. It’s feasible, however, to encapsulate the mechanics of validation by using some external class libraries, rather than messing up our lives doing it ourselves from scratch. This is exactly where Hibernate Validator comes into play, the reference implementation of Java Beans Validation 1.0 / 1.1 (JSR 303), a robust validation model based on annotations that ships with Java EE 6 and higher.

This article will walk you through using Hibernate Validator in a pragmatic way.

The Contextual Nature of Domain Model Validation

Independent of your language of choice, building a rich domain model is one of the most challenging tasks you can tackle as a developer. And on top of that you’ll have to make sure that the data that hydrates the model is valid, thus assuring that its integrity is properly maintained. Unfortunately, in many cases making domain objects valid in the context of the containing application is far from trivial, due to the intrinsic contextual nature of the validation process per se.

To put it in another way: If the arguments taken by a domain object are supplied by the application’s environment (for instance by using plain Dependency Injection, Factories, Builders and so on) rather than by an external upper tier, then validating the object should be straightforward and constrained to a very limited scope.

Conversely, if the arguments in question are injected from an outer layer (a user interface layer is a good example of this), the validation process can be cumbersome and tedious, in most cases leading to having chunks of boilerplate code scattered across multiple application layers.

In the end, everything boils down to this simple yet fundamental question: What makes a domain object valid? Should its state be validated before being persisted or updated in a database, or when passed around to other layer(s)? The logical answer is: It depends. Remember that validation is always contextual! So no matter what approach you use to decide whether your domain objects are valid, Java Beans Validation will make the process easier.

[author_more]

Introducing Java Bean Validation with JSR 303

Prior to Java EE 6, Java didn’t provide a standard way of validating the fields of a domain class by mean of a centralized mechanism. But things have changed for the better since then. The Java Beans Validation specification makes it fairly easy to selectively validate class fields (and even entire classes) by using constraints declared with a few intuitive annotations.

At the time of this writing, JSR 303 has only two compliant implementations that you can pick up out there, Apache BVal and Hibernate Validator. The latter is the reference implementation, which can be consumed as a standalone library, completely decoupled from the popular ORM framework.

With that said, let’s see now how to get started using the Java Beans Validation / Hibernate Validator tandem for performing effective domain model validation in the real world.

Defining a Basic Domain Model

As usual, a nice way to show how to utilize Java Beans Validation for validating a domain model is with a concrete example. Considering that the standard implements an annotation-based validation schema, the classes that are part of the domain model must always be constrained with annotations.

In this case, for clarity’s sake, the domain model I want to validate will be composed of only one naive, anemic class, which will be the blueprint for user objects:

public class User {

    private int id;

    @NotEmpty(message = "Name is mandatory")
    @Size(min = 2, max = 32,
            message = "Name must be between 2 and 32 characters long")
    private String name;

    @NotEmpty(message = "Email is mandatory")
    @Email(message = "Email must be a well-formed address")
    private String email;

    @NotEmpty(message = "Biography is mandatory")
    @Size(min = 10, max = 140,
            message = "Biography must be between 10 and 140 characters long")
    private String biography;

    public User(String name, String email, String biography) {
        this.name = name;
        this.email = email;
        this.biography = biography;
    }

    // Setters and Getters for name, email and biography

}

There is nothing worth discussing except for the constraints declared on top of each field. For instance, the @NotEmpty(message = "Name is mandatory") annotation states that the name field must be, yes, a non-empty string. Even though it’s pretty self-explanatory, the message attribute is used for defining the message that will be displayed if the constrained field raises a violation when being validated. Even better it’s possible to customize many constraints with parameters in order to express more refined criteria. Consider the @Size(min = 2, max = 32, message = "...") annotation. It expresses, well, exactly what the message says. Not rocket science, right?

The specification provides a few more, handy annotations. For the full list, feel free to check them here.

Validating Constraints

At this point, we’ve managed to define a constrained model class by using Java Beans Validation, which is all well and fine. But you might be wondering what are the practical benefits of doing this? The laconic answer is: none - so far. The class is ready to be validated, sure, but the missing piece here is having a mechanism, capable of scanning the annotations, checking the values assigned to the constrained fields, and returning the validation errors (or in JSR 303 terminology, the constraint violations). And that’s exactly how Hibernate Validator works under the hood.

But let’s be frank: The above explanation would be just technical gibberish if we don’t see at least a contrived example that shows how to use HIbernate Validator for validating the User class:

Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
User user = new User("John Doe", "no-mail", "");
validator
    .validate(user).stream()
    .forEach(violation -> System.out.println(violation.getMessage()));

That was really easy, wasn’t it? The snippet first grabs an instance of Hibernate Validator through the static buildDefaultValidatorFactory() and getValidator() methods that are part of the Bean Validation API, and uses the validate() method for validating a potentially invalid user object. In this case, the constraint violations are displayed in the console by using streams and lambdas altogether. It’s possible, however, to get the same result by using a standard for loop rather than the newer forEach.

Encapsulating Validation Logic in a Service Component

Of course, it’s ridiculously simple (and very, very tempting, to be frank) to start dropping Validator instances here and there, and validate the constrained classes in multiple places, even in the wrong ones! But that would be a flagrant violation of the DRY Principle (aka a WET solution) that would lead to duplicated code across several layers. Yes, definitely bad design.

Continue reading %Effective Domain Model Validation with Hibernate Validator%


by Alejandro Gervasio via SitePoint

No comments:

Post a Comment