Essayer les Value Object, c'est les adopter

Qui suis-je pour parler en ces lieux ?

Je suis le susnommé Kevin Nadin
Twitter : @kevinjhappy

 

Je travaille à Darkmira comme développeur PHP, c'est une boite avec pleins de dev cool comme toi, ou toi, ou...

non pas toi ;)

Value Object, quésaco ?

En un mot:
Mini classe qui représente une donnée

(bon, ça fait plus qu'un mot tout ça)

Exemple: je veux gérer un email, donc je fais une classe avec une donnée

<?php

class EmailAddress {
	
    private $emailAddress;
    
}

J'ai mis ma propriété en privé volontairement, je rajoute un getter

<?php

class EmailAddress {
	
    private $emailAddress;
    
    public function toString() 
    {
    	return $this->emailAddress;
    }
}

Et je passe par le constructeur pour....
ben construire mon objet :D

<?php

class EmailAddress {
	
    private $emailAddress;
    
    public function __construct(string $emailAddress)
    {
    	$this->emailAddress = $emailAddress;
    }
    
    public function toString(): string
    {
    	return $this->emailAddress;
    }
}

Et là on a déjà un Value Object !!

Buuuut... Wait...

Ça sert à rien ton truc !

<?php

class EmailAddress {
	
    private $emailAddress;
    
    public function __construct(string $emailAddress)
    {
    	$this->emailAddress = $emailAddress;
    }
    
    public function toString(): string
    {
    	return $this->emailAddress;
    }
}

Pourquoi on ne manipule pas une variable string directement ??

1- Immutabilité

 

On applique ici un principe de Defensive Programming, les variables que l'on crée ne sont plus modifiables !

Il n'y a pas de setter dans ma classe, et le paramètre est privée, donc une fois crée en passant par le constructeur il n'est plus modifiable

2- Domaine

 

On applique aussi un principe de DDD "Domain Driven Design", une variable string ne "signifie" rien alors qu'une classe "EmailAddress" est bien plus parlante dans le contexte dans lequel elle est utilisée !

On sait ici que l'on parle d'un e-mail, et pas d'un nom de famille par exemple.

3- Validateur possible

 

On peut compléter le constructeur pour permettre de rajouter des conditions

 
<?php

class EmailAddress {
	
    private $emailAddress;
    
    public function __construct(string $emailAddress)
    {
    	if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
            throw new Exception('Erreur sur email');
        }
    	$this->emailAddress = $emailAddress;
    }
    
    public function toString(): string {/*...*/}
}

4- Exception spécifique

 

Et sur la condition que nous avons ajouté, on lève une exception dédiée pour cette classe

 
<?php

class EmailAddress {
	
    private $emailAddress;
    
    public function __construct(string $emailAddress)
    {
    	if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidEmailAddressException($emailAddress);
        }
    	$this->emailAddress = $emailAddress;
    }
    
    public function toString(): string {/*...*/}
}

class InvalidEmailAddressException extends LogicException {/*...*/}

5- Defensive Programming, again !

 

Cette classe n'a pas à être étendue, on ferme la porte aux modifications par héritage
=> la classe passe en "final"

 
<?php

final class EmailAddress {
	
    private $emailAddress;
    
    //...
}

Ok pour la classe

<?php

final class EmailAddress {
	
    private $emailAddress;
    
    public function __construct(string $emailAddress)
    {
    	if (filter_var($emailAddress, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidEmailAddressException($emailAddress);
        }
    	$this->emailAddress = $emailAddress;
    }
    
    public function toString(): string
    {
    	return $this->emailAddress;
    }
}

mais dans quel but ?

1- Rajoute une couche de contrat dans vos services

=> sépare les rôles

<?php
class LoginService {

    public function login(string $email, string $password) {
        // TODO: vérifier l'email
        // TODO: vérifier la validité du mot de passe
        //...
    }
}
<?php
class LoginService {

    public function login(EmailAddress $email, VerifiedPassword $password) {
    	//...
    }
}

Devient :

2- Rend le code plus compréhensible

<?php
class LoginService {

    public function login(EmailAddress $email, VerifiedPassword $password) {
    	//...
    }
}

Une variable string pourrait laisser la place à tout et n'importe quoi, ici on sait que l'on passe un Email valide et un mot de passe vérifié

3- Augmente la maintenabilité

Si vous souhaitez rajouter une vérification supplémentaire sur les emails, c'est très facile car la vérification est centralisé.

4- Permet la réutilisabilité

(...oui c'est Français...)

Le Value Object peut être réutilisée facilement à tout moment où nous aurions besoin de manipuler un email

5- Encourage le typage fort

Parce que c'est important ;)

6- Découplage du Framework

Les Value Object n'ont aucune dépendance envers aucune autre classe, ils se suffisent à eux-même

 

Cependant il est possible de faire des Value Object utilisant d'autres Value Object

Exemple de VO dans un VO part 1

<?php
final class DateTimeInFuture {

    private $dateTime;
    
    public function __construct(DateTime $dateTime)
    {
    	if ($dateTime < new \DateTime()) {
        	throw new DateTimeNotInFutureException($dateTime);
        }
    	$this->dateTime = $dateTime;
    }
    
    public function toDateTime(): DateTime
    {
    	return $this->dateTime;
    }
}

Exemple de VO dans un VO part 2

<?php
final class DateTimeRangeInFuture {
    private $startDate;
    private $endDate;
    
    public function __construct(
        DateTimeInFuture $startDate, 
        DateTimeInFuture $endDate
    ) {
    	if ($startDate->toDateTime() > $endDate->toDateTime()) {
            throw new InvalidDateTimeRangeException($startDate, $endDate);
        }
    	$this->startDate = $startDate;
        $this->endDate = $endDate;
    }
    
    public function start(): DateTime
    {
    	return $this->startDate->toDateTime();
    }
    public function end(): DateTime
    {
    	return $this->endDate->toDateTime();
    }
}

Ah ouais... pourquoi pas :D

(j'aurai pu y penser tout seul d'abord ...)

Il ne vous reste plus qu'à vous laisser tenter !

Essayer les Value Object,

By Kevin JHappy

Essayer les Value Object,

Lightning talk for AFSY 2019-08-27

  • 565