Architecture : Action Domain Responder (ADR)

Attention :

Le design pattern ADR est basé sur le triptyque ActionDomainResponder. Il est l’une des alternatives au design pattern MVC.

Il est possible de vulgariser le pattern de la façon suivante :

Pourquoi ?

Le pattern MVC invite à créer des controllers composés d'une ou plusieurs actions. A l'usage, ces controllers peuvent devenir conséquents voir problématiques car trop complexes. L'ADR permet de limiter cette complexité globale. Il sera toujours possible d'avoir du code complexe, mais cette complexité sera limitée à une action dans un contexte précis.

Étant donné que l'ADR est composé d'un triptyque, le pattern encourage naturellement la POO et un SRP simple dans le sens ou les traitements métiers seront plus naturellement délégués à des “services”.

Enfin, l'ADR via son Responder assure une cohérence dans le rendu des réponses de l'application.

Comment ?

Si une Action est sensiblement la même chose qu’un controller à savoir être en charge du traitement de la requête entrante, le Responder peut quant à lui être conçu de manière différente.

Le moyen le plus simple consiste à mutualiser les Responder en les dédiant à un type de réponse. Dans une application HTTP classique, on pourra ainsi créer différents types de Responder :

Plus complexe, mais conforme à la définition initiale du pattern, le ou la développeur·euse dédiera une action donnée à un Responder unique. On utilisera alors cette spécialisation pour les cas où le formatage de la réponse est complexe.

Afin de conserver la qualité de notre application, ce Responder pourra encapsuler un Responder commun (via la composition).

Enfin, l'utilisation la plus complexe consistera quant à elle à ajouter un Payload afin de spécialiser la réponse ainsi que son contenu.

Dans une application orientée DDD, l'Action et le Responder seront situés dans la couche User Interface. L'emplacement de la couche Domain dépend quant à elle des choix d'architecture de l'application. N'y voyez, une fois de plus, qu'un terme commun à d'autres approches de conception.

Exemples

<?php

namespace App\UI\Front\Action;

use App\UI\Front\Responder\HelloResponder;
use App\Application\Service;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;

final class HelloAction
{
    private Service $service;

    private HelloResponder $responder;
    
    public function __construct(
        Service $service, 
        HelloResponder $responder
    ) {
        $this->service = $service;
        $this->responder = $responder;
    }
    
    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        // 1. entrée
        $data = (string) $request->getBody();
        
        // 2. traitement (Domain)
        $service = ($this->service)($data);
        
        // 3. sortie (Responder)
        return ($this->responder)($service);
   }
}
<?php

namespace App\UI\Front\Responder;

use App\Application\Service;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\HtmlResponse;

final class HelloResponder
{   
    public function __invoke(Service $service): ResponseInterface
    {       
        $response = $service->getData();
        
        return new HtmlResponse($response);
   }
}

Symfony

L'utilisation de l'ADR n'est pas mise en avant dans Symfony. Il faudra donc veiller à sensibiliser l'équipe à ce sujet. A contrario, l'autowiring de Symfony étant performant, il est possible d'utiliser l'ADR sans aucune modification particulière du framework. Il n'y a donc aucune contrainte technique.

De par sa conception, on évitera d'injecter les différents services utilisés par une Action dans la méthode invoke et on privilégiera l'injection via le constructeur.

Si l'utilisation de Twig n'a pour le moment jamais été remise en question par les auteurs de Symfony, des changements dans les best practices ont eu lieu à plusieurs reprises sur les controllers. L'utilisation de l'ADR a pu permettre de limiter l'impact lors des migrations. Il est concrètement possible de les implémenter depuis la version 2.7 de Symfony.

Questions/Réponses

Aucune TwitterEmail

Conclusion

L'ADR est une alternative très intéressante au MVC et adaptée au flux Request/Response. Le coût technique de son utilisation n'est pas important, mais son impact est fort. C'est également un cadre plus strict avec un fort pouvoir d'adaptation.

Certain·e·s lui reprocheront sont coût cognitif avec comme argument principal l'impact sur la directory structure du projet (il existe un fichier par action), mais il est alors facile de répondre que ce coût est largement compensé lorsque l'on visualise le code.

Pour terminer, cette segmentation permet également de faciliter les tests unitaires.