la pause dev'

· Java · architecture · architecture hexagonale

Architecture hexagonale: Partie 1

Création d'un hexagone

Cet article est la première partie d’une explication sur l’architecture hexagonale. Elle se décompose en 2 partie, la suite se trouve ici. Le but de cette suite est de faire un exemple complet, partir vraiment dans la pratique. Si tu veux plus d’information théorique sur l’architecture hexagonale, tu peux lire cet article.

Dans cette partie, j’ai construit une application (l’application takebook) avec un unique hexagone, en langage java, sous maven. Tu peux le télécharger sur ce repository. La commande maven “clean install” sera peut être nécessaire au niveau du module parent pour faire marcher l’application.

Mon domaine d’exemple, les bibliothèques

Faire de l’architecture hexagonale, cela veut dire que l’on a un domaine complexe a géré. Si on fait juste un projet technique, sans domaine, on perd l’intérêt de cette architecture. Ainsi j’ai mis en place un domaine pour l’application d’exemple, elle permet de gérer des prêts de livres de différentes bibliothèque.

Voici le scénario: Différentes bibliothèques municipales. Nos bibliothèques n’ont pas d’application pour gérer l’emprunt des livres entre ces différentes bibliothèques, nous intervenons pour combler ce problème.

Le principe de prêt d’un livre est:

  1. Le livre est disponible, si il se trouve sur une étagère

  2. Un utilisateur emprunte un livre si il est disponible

  3. L’utilisateur retourne le livre dans une bibliothèque

  4. Le livres est à nouveau disponible des que l’on le remet sur une étagère, etc …

En ce qui concerne les bases de données et comme les interfaces graphiques, les bibliothécaires (utilisateurs de l’application) utilisent actuellement différentes solutions et désirent garder chacun la leur. L’architecture hexagonale va beaucoup nous aider pour gérer ce problème …

Mon Hexagone (le module “domain”)

On retrouvera le seul hexagone. Il se trouve dans le module “domain”. Commençons par le pom.xml de ce module. Si on regarde au niveau dépendance, on a uniquement des librairies de tests. Aucun framework (comme Spring, JEE, REST, JPA …) doit être présent. Dans l’hexagone, on gère uniquement le métier.

J’ai découpé l’application en différents modules. Ce n’est pas obligatoire, mais cela permet bien de voir que le module “domain” ne possède aucune dépendance technique. (hormis pour les tests, mais cela ne compte pas. :))

Il y a 2 packages intéressants. On retrouve nos classes modèles (Book, Library …) dans le package “model”. Les portes d’entrée et de sortie se trouvent dans le package “port”. La porte d’entrée “LibraryBookInputPort” sera notre moyen d’utiliser les actions pour modifier l’état d’un livre. Les portes de sortie “BookOutputPort”, “LibraryOutputPort” et “ShelfOutputPort” permettent de récupérer et sauvegarder les données, quelque soit le moyen (BDD, fichiers, autres …).

Moins de Blabla, plus d’exemples

Ok, pour montrer comment cela marche notre hexagone, on va le tester. J’ai fait un exemple simple en mode console qui se trouve dans le module “console”. Regardons le runner “ConsoleApp”.

On a un adaptateur unique pour les portes de sortie “ConsoleInternOutputAdapters”. J’ai fait une implémentation pour 3 interfaces, afin de gagner du temps. Cet adaptateur utilise des listes d’objet, pas de base de donnée, la donnée reste stocker dans l’application.

On affiche nos résultats, du coup, on utilise la facade “ConsoleFacade” afin d’interagir avec l’utilisateur dans la console.

Lancez le runner “ConsoleApp” pour tester tout ça. Saisissez l’action dans la console java pour pouvoir interagir avec l’application. Voici un exemple scénario avec le retour console (liste de bibliothèque, retour de livre et emprunt de livre), à toi de faire le tiens …

Pour lister l'ensemble des bibliothèques, saisissez dans la console LISTLIB

Pour lister l'ensemble des étagères d'une bibliothèque, saisissez dans la console SHELVESLIB

Pour lister l'ensemble des livres d'une bibliothèque, saisissez dans la console BOOKSLIB

Pour lister l'ensemble des livres empruntés par un utilisateur, saisissez dans la console BOOKSBORROWER

Pour emprunter un livre, saisissez dans la console BORROWBOOK

Pour retourner un livre, saisissez dans la console RETURNBOOK

Pour ranger un livre, saisissez dans la console PUTAWAYBOOK



LISTLIB

Voici la liste des bibliothèques (identifiant -> nom):

101 -> Librarie A

102 -> Librarie B



BOOKSLIB

Saisissez l'identifiant de la bibliothèque

101

Voici la liste des livres (identifiant -> titre -> auteur -> détails):

1001 -> Mémoire de Pierre -> Pierre -> Retourné à Librarie A



SHELVESLIB

Saisissez l'identifiant de la bibliothèque

101

Voici la liste des étagères de la bibliothèque (identifiant -> label):

501 -> LibA001

502 -> LibA002



PUTAWAYBOOK

Saisissez l'identifiant du livre à ranger

1001

Saisissez l'identifiant de l'étagère

501

Le livre Mémoire de Pierre a été rangé



BORROWBOOK

Saisissez l'identifiant du livre à emprunter

1001

Saisissez le nom de l'emprunteur

Bob

Le livre Mémoire de Pierre a été emprunté



BOOKSBORROWER

Saisissez l'utilisateur:

Bob

Voici la liste des livres (identifiant -> titre -> auteur -> détails):

1001 -> Mémoire de Pierre -> Pierre -> Emprunté par Bob

Le plus important dans l’architecture hexagonale (et ailleurs): Les tests

Tu peux voir dans les différents tests du module “domain” dans le dossier “test”. On retrouve le code fonctionnel bien testé, avec différents scénarios.

Les tests vérifient les portes d’entrée et simulent (mock) les portes de sortie pour vérifier que le fonctionnel soit respecté. C’est extrêmement simple et facile de pouvoir tester un hexagone et c’est une grande force de cette architecture. La classe “LibraryBookInputPortTest” est bon exemple.

Techno différentes, un même domaine

Allez, plus rigolo comme exemple, on va implémenter le framework Spring avec notre application. Tu trouve l’application dans le module “spring”, lances le runner “SpringApp” et testes via l’url http://localhost:8080/takebook/index.html.

Attention: si tu ne comprends rien à Spring, tu risques de galérer un peu.

Spring possède une notion d’injection de dépendance (Spring IoC). L’implémentation des portes de sorties possèdent des annotations Spring, ce qui permet d’auto-gérer des injections liées à des interfaces.

Dans la configuration de l’application (toujours la classe “SpringApp”), on indique que la porte d’entrée est injectable et elle a comme dépendances, les interfaces de portes de sorties auto-injectées. Ainsi, on ne modifie pas l’hexagonal avec une dépendance Spring.

Toute la magie de cette application: L’ensemble des actions du domaine se font dans l’hexagone déjà utilisé pour la première application en mode console. Des techno différentes, un même domaine exécuté.

J’utilise Spring REST pour créer une API utilisable et j’ai un petit client rapide fait en vue.js (dans le dossier static).

Conclusion

Je te conseille de lire ces vidéos qui m’ont beaucoup aidées:

J’espère que cet exemple t’a permis de mieux comprendre l’architecture hexagonale. Si tu as des questions ou des remarques, n’hésites pas à les envoyer.

Photo libre de droit d’Element5 Digital provenant de Pexels.