View on GitHub

Java Modular Validators

to be used as a utility library in projects to avoid the overhead of writing custom validation frameworks

Download this project as a .zip file Download this project as a tar.gz file

Modular Validators

A lightweight validation framework for the JVM, allowing for internal and external validation logic to be combined in a transparent and reusable way.

Quality Gate Status All Contributors JavaDoc


User Guide

This section will provide a brief overview of the different parts of the framework, and how they can be used to create a modular validation solution. The library is divided into two modules for now:

Reusable Validators

The modular-validators-core module is the most important part of the library, as it contains the building blocks for creating custom validators. You can find the classes and interfaces in the supplied javadocs, or by browsing the source code of the module. We advise you to start by creating a custom validator, and get a feel for the code that you are able to write with this library.

Creating a specification

The main entrypoint of the core module is the ModularRuleset class. This class is a container for multiple Constraint objects, and provides you with the ability to assess multiple constraints against a single object. The results of the validation are stored in a Rationale object, which can be queried for the results of the validation. By default, all rules are assessed and included in the rationale.

As an example, say we have a simple record that represent a email contact address, like so:

private record EmailContact(
        UUID userIdentifier, 
        String email, 
        String name, 
        String lastName
) {}

We want to ensure that the email address is valid, and that the name and last name are not empty. We can create a ruleset like so:

ModularRuleset<EmailContact.class> emailChecker = aValid(EmailContact.class)
  .must(Objects::nonNull, "not be null")
  .must(haveNonNullField(EmailContact::email), "have a non-null email")
  .iHaveSpoken();

Note that the must and may methods can be supplied with a Predicate of your chosen type, and a message that represents the specification. This enables you to create custom specification rules, that can even call external services if so desired. For instance, you could create a specification rule that checks if the email address is valid by sending a test email to the address.

.must(mailContact -> emailService.sendTestMailTo(mailContact.email), "be able to receive a test email")

Applying the specification

Once you have created a specification, you can apply it to an object like so:

emailChecker.constrain(
        new EmailContact(
                randomUUID(), 
                "invalid email", 
                "Bob", 
                "The Builder"
        )
    );

This with return a Constrained object, which can be queried for the results of the specification validation, or used in a chain of further processing (similar to Java’s Optional). The JavaDoc of the Constrained class contains more information on how to use this object.

Project and Repository Information

Goal

The goal of this project is to provide a lightweight rule enforcement and validation framework for Java projects, allowing for internal and external validation logic to be combined in a transparent and reusable way.

It will help avoid the need for writing complex custom validation solutions, by providing developers with pre-built rules and enforcers that can be combined in a modular way. The sections below describe the most common issues with existing (simplistic) validation frameworks, and how this project aims to adress them.

If you are interested in seeing potential use cases for this project, please refer to the ValidationDogfoodTest included in the src/main/test directory of the repository. These tests cases are intended to provide a real-world example of how the different parts of the framework can be used.

Value statement

Problem

The coding of validation logic is generally tedious. While many simple validation frameworks exist to date, most of them suffer from the same design issues:

Proposed solution

Our modular validator solution aims to remedy this, so developers have the option to chose their own style of validation. The aim is to simplify externally driven validation by providing extension points upon which developers can hook in their own api calls. On top of this, the validator objects will behave as monads. This means they will be usable in both the valid and invalid state. This allows to chain logic on top of the validator objects if so required, rather than being forced to write exceptional code paths.

The aim is to make validation of object state a first level concern when designing applicative flows, rather than a side-thought that is often forgotten.

References

Contributing

Thank you for your interest in this repository. If you would like to help out, consider the following:

There are various ways to contribute to this repository. You can help us out by:

If you would like to contribute code, please read the How to Contribute guide included in the repository.

Contributors

Ebenmelu Ifechukwu
Ebenmelu Ifechukwu

👀