- https://www.youtube.com/watch?v=07x54QhhFFY
- https://www.meetup.com/fr-FR/DDD-Paris/events/270068472/
Programmation fonctionnelle et architecture hexagonale : l'architecture hexagonale ne cloisonne pas les effets de bord. Les services du domaine appellent des repositories qui ont des effets de bord, ce qui rend le domaine difficilement réutilisable. Dans ce cas on va préférer utiliser un pattern qui s'appelle "functional core, imperative shell".
Sur des services très techniques, qui n'ont pas de logique métier. Alors l'architecture hexagonale coûte cher : isoler le métier nécessite de dupliquer la représentation du domaine dans les adapteurs de droite et dans les adapteurs de gauche. (Note : voir la discussion sur les DTOs plus bas, qui tempère cette sensation.)
Piège : quand on expose une API Rest on expose peu de métier, on expose des états et on voit les consommateurs réimplémenter des logiques métier, qui vont avoir des impacts sur l'évolutivité du système.
C'est un pattern très simple, attention à l'over engineering. Si le branchement ne se fait pas facilement (adapteur -> port) c'est signe d'over engineering. L'essence du pattern c'est une interface qui expose les comportements qu'on attend des infrastructure technique, et une classe qui implémente cette interface pour venir fournir les comportements techniques attendus, et de l'injection de dépendances pour venir injecter ça en fonction du contexte. Rien d'autre. Pas besoin de stack Spring avec de l'injection de dépendance à 14 niveaux. Ce n'est pas complexe ou réservé à de gros projets.
Les développeurs ont souvent la base de donnée comme doudou, c'est quelque-chose qui rassure. C'est des archis qui enlèvent ce doudou, ça peut faire peur, il faut rassurer et montrer que c'est possible autrement.
Martin Fowler : l'architecture c'est tous les choix qu'on aurait aimé faire correctement au début du projet.
Une application n'est jamais qu'un seul métier, une application est toujours la composition de plein de métiers qui essaient de collaborer. Choisir tout de suite le bon style d'architecture est une pression, mais la vraie contrainte, la vraie pression vient du fait d'essayer de faire entrer tout les métiers qu'on veut faire collaborer dans un seul style d'architecture. Alors que si je prends un exemple tout bête, imaginons que je veux coder une application de livres communautaires pour pouvoir réserver des livres, il y a un métier tout bête c'est le catalogue des livres. Je dois pouvoir en fonction de l'ISBN récupérer quel était ce livre (description plus photo). C'est un métier à part ce catalogue. Il est probable que ce métier là peut être fait en crud. Au contraire toute la logique de réservation, de points de fidélités, de malus si les livres sont rendus en retard, il y a un métier un peu compliqué, c'est intéressant de choisir une architecture hexagonale. Mais j'aurais toujours tendance à quand même favoriser une architecture hexagonale car j'ai déjà fait des erreurs du genre "ça c'est du crud, c'est sûr" et petit à petit je me suis rendu compte qu'il y avait de plus en plus de règles métier. J'ai trouvé par expérience qu'il est difficile de partir du crud pour aller vers de l'hexagonale / DDD, alors qu'il est beaucoup plus facile de partir de l'hexagonal et d'enlever les duplications pour revenir vers du crud. Les métiers eux aussi ne sont pas un bloc monolithique.
Que je fasse du DDD ou pas, la notion de transaction (qu'est-ce qui doit être écrit en même temps ou échouer en même temps) a forcément une influence sur la manière dont je vais concevoir les agrégats. L'architecture hexagonale ne dispense pas de se poser la question "Est-ce que ce que j'ai écrit dans mon métier va bien se comporter avec la BDD".
Ça fait plus de code, beaucoup de modèles, parfois dupliqués entre présentation, persistance. Beaucoup de code à écrire en plus par rapport à des frameworks RAD comme Rails. Bien tester ces traductions d'un modèle à l'autre.
Attention à la fuite de la logique métier de l'hexagone vers les adaptateurs. Ils ne doivent pas orchestrer. Se poser la question si une mini orchestration fait partie d'anti corruption pour éviter de complexifier le domaine, ou bien si elle devrait plutôt être dans le métier. Les adaptateurs peuvent jouer le rôle de médiateurs vers d'autres domaines.
Tester avec ou sans base ? Pour la substitution dans les tests : attention, c'est pas parce-qu'on a une signature qui est identique qu'on va avoir un comportement identique (comportements non documentés, latence) => tests d'intégration avec les vrais services derrière. On peut ne tester que les répositories par exemples.
Écrire du SQL à la main : je peux utiliser la puissance du SQL (requêtes d'analytics assez sympa) sans intermédiaire. Très bien pour les queries. Et testez les en intégration.
Property based testing sur le métier ? Définir les propriétés qui doivent être toujours vraies.
C'est un pattern qu'il ne faut pas voir en isolation. Il y a des choses qui vont aller autour, du CQRS, de l'event sourcing, des use cases d'architectures (aspect transactionnel), plein de choses qui vont apporter des réponses à d'autres problèmes. C'est avec une autre typologie d'architecture qui vient se combiner à l'architecture hexagonale qu'on vient potentiellement apporter des solutions à d'autres problèmes.
"Il me faut un DTO pour la partie REST, un autre DTO pour la partie base de données." J'ai pas appris à aimer ça mais j'ai appris à dire oui, effectivement, dans 95 % des cas ça fait écrire beaucoup de code, mais on s'y fait. J'ai arrêté d'essayer de prendre mes agrégats et d'essayer de les persister tel quel dans la base. Ça ne me choque pas d'utiliser des DTOs intermédiaires pour faire les deux. La stratégie consiste en revanche à essayer de minimiser le nombre de fois où on en a besoin. Vu que j'aime bien faire l'hexagone plutôt en utilisant un command bus, ça ne me dérange pas que la commande que je met dans le bus soit serializée en JSON parce-qu'elle vient de REST et comme ça ça me fait une couche d'intermédiaire en moins à tester. Et bien sûr le pattern magique pour ne pas avoir besoin de faire de DTOs pour persister c'est de faire de l'event sourcing.
La question des DTOs est pertinente, parce qu'il y a cette sensation que ça fait beaucoup de code. Alors que regarde, dans Rails j'ai qu'un machin magique que j'appelle "modèle" et il fait tout ! Il persiste dans la base, il génère des formulaires et c'est fantastique. J'ai pas forcément de contre-argument magique contre ça en fait.
Si on a de bons outils pour refactorer ça se passe beaucoup mieux. Le fait d'avoir ces intermédiaires permet de ne pas être trop conformistes par rapport aux modèles qui venaient d'autres APIs dont on se nourrit et de rafiner, de renommer nos nommages dans notre domaine.
S'il n'y avait pas les DTOs, on perdrait une étanchéité nécessaire au pattern. Ça explicite le franchissement de frontière.
Peu de livres en parlent, on trouve un chapitre dans le red book (Implementing Domain-Driven Design) et Growing Object-Oriented Software Guided by Tests en parle.