Technique · Mis à jour le

Value Object : le must-have du développeur

Les Value Objects (VO pour les intimes) font partie de la boîte à outils de tout bon développeur. Ils renforcent la qualité des données, limitent les risques d’injection et rendent le code plus souple. Voyons en détail ce qu’ils apportent.

Le principe

Que vous développiez en PHP, en Python, en Java, en Kotlin ou dans un autre langage, le principe reste le même : au lieu de manipuler des types primitifs (un entier, une chaîne…), vous les enrobez dans des types que vous définissez vous-même.

Un Value Object a trois caractéristiques :

  • il est toujours valide : il vérifie sa donnée au moment de sa création, et refuse d’exister sinon ;
  • il est immuable : une fois créé, sa valeur ne change plus ;
  • il se compare par sa valeur : deux adresses e-mail identiques sont égales, même s’il s’agit de deux objets distincts.

Un premier exemple : l’âge

Imaginons une application réservée aux personnes majeures. Plutôt que de stocker l’âge dans un simple entier, vous créez un objet Age. À sa création, il vérifie que l’âge est d’au moins 18 ans.

Vous utilisez ensuite cet objet partout dans votre application. Ainsi, la validation s’applique automatiquement à chaque étape.

Si la validation échoue, libre à vous de choisir la réaction : renvoyer une erreur, ignorer l’âge saisi, appliquer une valeur par défaut…

Un exemple en code : l’adresse e-mail

@Value
public class EmailAddress {

    private String value;

    public EmailAddress(String value) {
        if(!EmailValidator.getInstance().isValid(value)){
            throw new IllegalArgumentException(String.format("L'adresse e-mail {%s} est invalide", value));
        }
        this.value = StringUtils.lowerCase(value);
    }
}

Ici, on ne peut construire l’objet qu’à partir d’une chaîne de caractères. À la construction, on vérifie la donnée reçue. Si elle est valide, on la convertit automatiquement en minuscules.

En utilisant cet objet dans toute l’application, on est donc sûr :

  • d’appliquer partout les mêmes règles de validation ;
  • d’avoir une donnée toujours formatée de la même façon ;
  • de rejeter les valeurs farfelues, y compris les tentatives d’injection de code.

Attention toutefois : un Value Object est une couche de protection supplémentaire, pas une solution miracle. Il ne remplace pas les requêtes préparées pour se protéger des injections SQL.

Et avec les langages récents ?

Les langages modernes facilitent encore la création de Value Objects :

  • en Java, depuis la version 16, les record sont immuables et se comparent par leur valeur. L’annotation @Value de Lombok n’est plus nécessaire ;
  • en Kotlin, les data class et les value class jouent le même rôle ;
  • en PHP, depuis la version 8.2, les classes readonly garantissent l’immuabilité.

Voici le même exemple avec un record Java :

public record EmailAddress(String value) {
    public EmailAddress {
        if (!EmailValidator.getInstance().isValid(value)) {
            throw new IllegalArgumentException("L'adresse e-mail %s est invalide".formatted(value));
        }
        value = value.toLowerCase();
    }
}

Des données plus fiables

Imaginons maintenant qu’une donnée invalide soit lue en base de données (un petit malin l’a peut-être modifiée à la main). Le Value Object refuse de se créer. Vous pouvez alors demander à l’utilisateur de saisir une valeur correcte, comme si la donnée invalide n’avait jamais existé.

Ainsi, vous renforcez la qualité des données que vous gérez et vous évitez que les mauvaises données ne se propagent.

En résumé

Ce principe s’applique à de petits objets. Au lieu d’utiliser des types primitifs, préférez des objets dotés d’un peu d’intelligence. Vous verrez que votre application deviendra plus robuste, plus maintenable et plus souple.

À lire aussi

Articles sur le même sujet