Hexagonal / Clean Architecture
Trois noms circulent pour à peu près la même idée. Ports et adaptateurs, proposé par Alistair Cockburn en 2005 — l’« hexagone » n’a jamais eu six côtés pour une raison particulière, il s’agissait seulement de ne pas dessiner un rectangle avec un haut et un bas. Onion architecture, formulée par Jeffrey Palermo en 2008. Clean architecture, popularisée par Robert C. Martin en 2012. Leurs différences réelles sont traitées en fin de page ; elles sont plus faibles qu’on ne le croit.
Les trois répondent au même défaut de l’architecture en couches : la logique métier y dépend de l’infrastructure, c’est-à-dire que la partie stable dépend de la partie volatile. Et les trois appliquent le même remède, qui n’est rien d’autre que le principe d’inversion des dépendances érigé en règle d’architecture.
La règle de dépendance
Toute dépendance du code source pointe vers l’intérieur. Rien de ce qui est à l’intérieur ne connaît quoi que ce soit de l’extérieur.
Le domaine ne contient aucun import d’infrastructure : ni ORM, ni client HTTP,
ni annotation de framework. C’est une contrainte vérifiable — et c’est le seul
test qui compte vraiment pour savoir si l’architecture est respectée.
Tip
Cette vérification s’automatise, et l’on parle alors de tests d’architecture —
ou de fitness functions. ArchUnit en Java, import-linter en Python,
dependency-cruiser en TypeScript, ou simplement les frontières de crates et la
visibilité pub(crate) en Rust : la règle de dépendance cesse d’être une intention
pour devenir un test qui échoue.
Ports et adaptateurs
Un port est une interface définie par le domaine. Un adaptateur est l’implémentation qui la relie au monde réel. Il en existe deux sortes, et les confondre est l’erreur la plus fréquente.
| Port d’entrée (driving) | Port de sortie (driven) | |
|---|---|---|
| Question posée | Que sait faire l’application ? | De quoi l’application a-t-elle besoin ? |
| Qui appelle | L’adaptateur appelle le domaine | Le domaine appelle l’adaptateur |
| Forme | Cas d’usage, service applicatif | Interface de dépôt, de notification |
| Adaptateurs typiques | Contrôleur HTTP, CLI, consommateur de file | Repository SQL, client SMTP, client d’API |
| Qui l’implémente | Le domaine | L’infrastructure |
Les deux sont des interfaces appartenant à l’intérieur. La différence est le sens
de l’appel : un port d’entrée est offert par le domaine, un port de sortie est
exigé par lui. C’est ce second cas qui réalise l’inversion, et qui explique
pourquoi l’interface CommandeRepository est déclarée dans le domaine et non dans
la couche de persistance qui l’implémente.
Chaque élément, un par un
C’est la partie qui manque à la plupart des présentations : savoir ce qu’on met dans chaque case, et surtout ce qu’on n’y met jamais.
Le domaine
Le centre. Il contient tout ce qui resterait vrai si l’application changeait entièrement de technologie.
| On y met | Entités, objets valeur, agrégats, services de domaine, événements de domaine, erreurs métier, interfaces de ports |
| On n’y met jamais | Un import d’ORM, de client HTTP, de framework web ; une annotation de sérialisation ; un SELECT ; une URL ; un chemin de fichier ; un appel à l’horloge système |
| Le test | Le dossier compile-t-il en supprimant tout le reste du projet ? |
Les erreurs métier y ont leur place, et c’est souvent oublié :
PlafondDepasse, ReservationVide sont des concepts du domaine, pas des codes
HTTP. La traduction en 409 ou 422 est le travail de l’adaptateur entrant.
Les ports d’entrée — les cas d’usage
Un port d’entrée décrit une chose que l’application sait faire. Sa granularité est celle d’une intention utilisateur, pas celle d’une entité.
| On y met | Le chargement de l’agrégat, un appel de méthode métier dessus, la sauvegarde, la publication des événements |
| On n’y met jamais | Une règle métier — pas de if métier ; un objet HTTP ; un type de l’ORM |
| Nommage | Un verbe à l’infinitif : ValiderCommande, AnnulerReservation. Jamais CommandeService |
| Le test | Si l’on retire toutes les lignes d’orchestration, reste-t-il une décision ? Si oui, elle est au mauvais endroit |
Warning
Le piège le plus courant à ce niveau est le cas d’usage par entité :
GererCommande avec dix méthodes. On a alors reconstruit la couche service du
3-tier à l’intérieur de l’hexagone, avec les mêmes pathologies et une indirection
de plus. Un cas d’usage, une intention, une méthode publique.
Les ports de sortie
Ce dont le domaine a besoin, exprimé dans son vocabulaire. C’est ici que se joue l’inversion, et c’est ici que se trouvent les ports qu’on oublie.
| Port | Rôle | Pourquoi c’en est un |
|---|---|---|
| Dépôt | Charger et enregistrer un agrégat | Évident |
| Notification | Prévenir quelqu’un | Évident |
| Passerelle | Interroger un système tiers | Évident |
| Horloge | Donner l’instant courant | Un Date.now() dans le domaine rend toute règle de date intestable |
| Générateur d’identifiants | Produire une identité | Un identifiant venu de la base impose de sauvegarder pour connaître l’identité |
| Générateur d’aléatoire | Tirage, échantillonnage | Même raison que l’horloge |
| Publication d’événements | Diffuser vers l’extérieur | La cible est une décision d’infrastructure |
Les trois lignes en gras sont celles que presque personne ne pose au départ, et qui reviennent toujours. Une règle du type « une réservation provisoire expire au bout de vingt minutes » n’est testable de façon déterministe que si l’instant courant arrive par un port.
Tip
Le nom du port doit décrire un besoin métier, jamais une technologie.
NotificationClient et non Mailer ; ReferentielTarifaire et non
ClientSoapGescom. Le test est simple : si le nom du port changerait en changeant
de fournisseur, c’est que le port est mal nommé.
Les adaptateurs entrants
Ils traduisent une sollicitation extérieure en appel de cas d’usage.
| On y met | La désérialisation, la validation de forme, l’authentification, la traduction des erreurs métier en codes de retour, la pagination des réponses |
| On n’y met jamais | Une règle métier ; un accès direct au dépôt ; un objet du domaine sérialisé tel quel |
| Un par mode d’accès | HTTP, CLI, consommateur de file, tâche planifiée, test d’acceptation |
| Le test | Peut-on ajouter un second adaptateur — une commande CLI — sans dupliquer une ligne de logique ? |
C’est la propriété qui justifie à elle seule l’architecture sur beaucoup de projets : le jour où une file de messages doit déclencher le même traitement que l’API, c’est un fichier de plus et rien d’autre.
Les adaptateurs sortants
Ils implémentent les ports de sortie, et absorbent tout l’écart entre le domaine et le monde réel.
| On y met | Le SQL ou l’ORM, les clients HTTP, la correspondance domaine ↔ persistance, les reprises sur erreur, les délais d’attente, la traduction des erreurs techniques |
| On n’y met jamais | Une décision métier ; un if sur un statut métier |
| Le test | Les tests de cet adaptateur peuvent-ils être remplacés par un adaptateur en mémoire sans que le domaine s’en aperçoive ? |
Les objets de frontière
Trois représentations du même concept coexistent, et c’est volontaire :
Le DTO appartient à l’adaptateur entrant, la ligne à l’adaptateur sortant, et aucun des deux n’entre dans le domaine. C’est le coût le plus critiqué de l’approche — traité plus bas — et c’est aussi ce qui permet de renommer une colonne sans casser un client.
La racine de composition
Le seul endroit qui connaît toutes les implémentations concrètes.
| On y met | La lecture de la configuration, l’instanciation des adaptateurs, le câblage, le démarrage du serveur |
| On n’y met jamais | Une règle métier, une traduction |
| Signal d’alerte | Une racine de deux mille lignes signale un domaine découpé trop fin |
Le domaine, en code
Aucune dépendance sortante, aucune annotation, aucun type venu d’une bibliothèque.
Le constructeur privé — ou le dataclass(frozen=True), ou le Result du
constructeur Rust — n’est pas décoratif : il garantit qu’une Commande invalide
n’existe pas. C’est la différence entre une entité et une structure de données,
et c’est ce qui rend la validation inutile ailleurs.
Les adaptateurs, en code
Ils dépendent du domaine ; le domaine ne les connaît pas. Un adaptateur sortant implémente un port, un adaptateur entrant appelle un cas d’usage.
Les commentaires marquent le vrai travail d’un adaptateur : traduire. Le
mnt_ttc_cts de la base et le Montant du domaine sont deux représentations
distinctes, et c’est l’adaptateur qui absorbe l’écart — c’est un
Adapter au sens du GoF, appliqué à
l’échelle d’une frontière d’architecture.
La racine de composition, en code
C’est le seul fichier qui viole le sens de lecture habituel : il voit tout.
Le bénéfice : le test
Tout ce qui précède se paie en fichiers et en interfaces. Voici la contrepartie.
Pas de base de données, pas de serveur SMTP, pas de conteneur, pas de mock framework — et une exécution en millisecondes. Ce test ne vérifie pas un détail technique : il vérifie une règle métier, et il continuera de passer après une migration vers un autre moteur de stockage.
Note
Un adaptateur en mémoire écrit à la main vaut mieux qu’un mock généré. Il oblige
à respecter le contrat du port pour tous les cas — un parId qui retrouve
réellement ce que enregistrer a écrit — là où un mock configuré appel par
appel finit par décrire un comportement qu’aucune implémentation réelle n’a
jamais eu.
Les organisations de fichiers
C’est la question la plus concrète et la moins traitée. La règle de dépendance ne dit rien de l’arborescence : plusieurs organisations la respectent, avec des propriétés très différentes.
A — Par couche
L’organisation par défaut, celle de tous les tutoriels.
Simple à expliquer, et elle a un défaut qui apparaît vite : une évolution fonctionnelle touche trois dossiers éloignés, et deux développeurs sur deux fonctionnalités différentes travaillent en permanence dans les mêmes fichiers. Convenable jusqu’à une dizaine de cas d’usage.
B — Par nature de port
Rend l’hexagone littéralement visible dans l’arborescence.
Pédagogiquement excellente — sur une équipe qui découvre l’approche, elle rend la règle de dépendance évidente. En contrepartie, elle disperse encore plus une fonctionnalité, et elle vieillit mal au-delà d’un contexte.
C — Par fonctionnalité, puis par couche
L’organisation qui passe à l’échelle, et celle que je recommanderais par défaut au-delà d’une dizaine de cas d’usage.
Une évolution métier touche un dossier. C’est aussi ce que Robert C. Martin appelle une screaming architecture : ouvrir le dépôt apprend ce que fait le logiciel, et non quel framework il utilise. Et surtout, chaque dossier de premier niveau devient un candidat naturel à l’extraction — en module, puis en service si le besoin apparaît un jour.
D — Modules ou paquets séparés
La même chose que C, mais la frontière est vérifiée par l’outil de construction et non par la discipline.
La frontière tient par les dependencies de chaque package.json, dans un
workspace npm, pnpm ou Nx. Un import interdit ne résout tout simplement pas.
Python n’a pas de mécanisme de visibilité entre paquets : la frontière se vérifie
avec import-linter, dont les contrats déclarent que domaine ne peut importer
ni infrastructure ni aucune bibliothèque tierce. C’est un fichier de
configuration et une étape de CI.
C’est la version la plus robuste des quatre : la crate domaine ne déclare
pas ses dépendances interdites, donc le code qui les utiliserait ne compile pas.
Aucune configuration, aucun linter : le système de paquets fait le travail.
E — Tranches verticales
Un dossier par cas d’usage, avec ses ports et ses adaptateurs à côté.
Radicalement local : tout ce qui concerne un cas d’usage tient sous les yeux. Cela suppose d’accepter une certaine duplication entre tranches, et de savoir résister à la tentation de « factoriser » un dossier commun qui recréerait le couplage. Efficace sur des cas d’usage nombreux et peu semblables entre eux.
Comparaison
| Lisibilité d’une fonctionnalité | Passage à l’échelle | Frontière vérifiable | Coût initial | |
|---|---|---|---|---|
| A — Par couche | Faible | Faible | Non | Nul |
| B — Par port | Faible | Faible | Non | Nul |
| C — Par fonctionnalité | Élevée | Élevée | Non | Faible |
| D — Paquets séparés | Élevée | Élevée | Oui | Moyen à élevé |
| E — Tranches verticales | Très élevée | Élevée | Non | Faible |
La trajectoire la plus courante, et la plus saine : commencer en A pour apprendre, passer en C dès qu’il y a plus d’un domaine fonctionnel, et n’aller en D que sur le cœur de métier, là où la frontière mérite d’être garantie plutôt que promise.
Trois questions qui reviennent toujours
Où mettre les ports ? Trois placements, du plus simple au plus rigoureux : un
seul fichier ports.ts par contexte — pratique jusqu’à une dizaine ; un fichier
par port dans domaine/ports/ — le meilleur compromis ; ou le port à côté du cas
d’usage qui l’exige, ce qui rend visible qu’un port n’existe que parce que
quelqu’un en a besoin, et qui est la seule façon de garder des ports petits.
Où mettre les DTO ? Dans l’adaptateur, jamais dans le domaine. Un DTO existe pour une frontière précise : le DTO HTTP appartient à l’adaptateur HTTP. Deux adaptateurs entrants ont deux jeux de DTO, et vouloir les partager est le premier pas vers un objet unique qui traverse tout.
Où mettre la correspondance ? Du côté de la représentation étrangère. La
traduction Commande ↔ ligne SQL vit dans l’adaptateur Postgres, pas dans le
domaine et pas dans un dossier mappers/ central — qui finirait par dépendre de
tout.
À partir de quand c’est pertinent
L’architecture en couches a une propriété d’échelle précise : elle encaisse très bien le nombre d’entités, très mal la densité de règles. L’hexagone a la propriété symétrique, et c’est ce qui détermine quand il est pertinent :
L’hexagone ne rentabilise que la densité de règles. Sur un domaine mince, il ne rend strictement aucun service, et son coût est intégral.
Le bénéfice attendu est un produit de trois facteurs :
Un produit — donc si un seul facteur vaut à peu près un, le résultat est nul. Un CRUD avec une seule API HTTP et deux ans de vie ne tire rien de l’inversion, quel que soit son nombre de lignes.
Les zones
| Règles par cas d’usage | Points d’entrée | Durée de vie | Verdict | |
|---|---|---|---|---|
| Sur-conçu | 0 – 2 | 1 | < 2 ans | N’y allez pas. 3-tier assumé |
| Zone grise | 3 – 5 | 1, mais un second se profile | 2 – 5 ans | Renforcer le 3-tier d’abord |
| Pertinent | 5 – 10 | 2 et plus | > 5 ans | L’hexagone se rembourse |
| Indispensable | > 10, et elles changent | 3 et plus | Indéterminée | Cœur de métier — voir DDD |
Les trois tests honnêtes
Plutôt que des seuils, trois questions dont la réponse tranche vraiment.
« Nommez trois règles métier qu’il serait pénible de tester avec une base de données. » Si l’exercice est difficile, le domaine est mince et l’inversion ne protège rien. C’est le test le plus discriminant des trois.
« Combien de fois par mois modifiez-vous une règle métier ? » Si la réponse est zéro, la protection n’a aucune valeur : on paie une assurance contre un événement qui n’arrive pas.
« Le même traitement doit-il être déclenché autrement que par l’API ? » Une tâche planifiée, une file, un import, une commande d’exploitation. Si oui, l’hexagone est déjà justifié à lui seul par ce point.
Tip
La décision n’est pas globale. On applique l’hexagone par sous-domaine : le cœur de métier en ports et adaptateurs, le référentiel et le back-office en 3-tier assumé. C’est presque toujours meilleur qu’un choix unique appliqué partout, et c’est la logique de dosage décrite dans Sous-domaines.
Ce que ça coûte
Le passer sous silence rendrait tout le reste suspect. Le coût est réel, et il se compte.
Le nombre de fichiers
Pour un cas d’usage trivial — enregistrer une entité et répondre 201 :
| 3-tier | Hexagonal | |
|---|---|---|
| Fichiers à créer | 1 à 3 | 5 à 7 |
| Fichiers à ouvrir pour lire le flux | 2 | 4 à 5 |
| Interfaces à implémentation unique | 0 | 2 à 3 |
Sur un CRUD de quarante endpoints, cet écart est un multiplicateur de travail sans contrepartie. Sur cinq cas d’usage denses en règles, il est négligeable devant le temps gagné en test.
La correspondance
C’est la critique la plus fondée, et elle ne se réfute pas. Un même concept existe en trois représentations : le DTO d’entrée, l’objet du domaine, la ligne de base. Ajouter un champ, c’est le déclarer trois fois et écrire deux traductions.
Les réductions possibles sont partielles et toutes imparfaites : générer la correspondance — au prix d’un outil de plus ; réutiliser le DTO comme entrée du domaine — au prix de la contrainte qu’on cherchait à poser ; ou n’appliquer le mapping complet qu’aux agrégats du cœur, en laissant le reste en accès direct. La dernière est la plus raisonnable, et c’est encore un arbitrage par sous-domaine.
L’indirection
Chaque flux traverse deux ou trois interfaces qui n’ont, en production, qu’une seule implémentation. « Aller à la définition » atterrit sur une signature, et il faut une seconde étape pour trouver le code réel. C’est un coût de lecture permanent, payé par tous les développeurs à chaque investigation.
L’apprentissage
Le coût le plus sous-estimé. Il faut compter quelques semaines avant qu’une équipe cesse spontanément de mettre les règles dans les adaptateurs, et le résultat intermédiaire — des ports partout et les règles au mauvais endroit — est pire que le 3-tier de départ, puisqu’il en cumule le couplage et l’indirection.
Warning
La demi-mesure est le pire résultat possible. Des interfaces partout mais un objet
ORM qui traverse le domaine, ou une entité annotée @Entity au centre de
l’hexagone, cumulent le coût de l’architecture et le couplage des couches. Si le
domaine doit dépendre de l’infrastructure, autant assumer une
architecture en couches simple
et lisible.
Les limites
Distinctes du coût : ce sont des questions auxquelles cette architecture ne répond pas, ou mal.
Elle est conçue pour l’écriture
La limite la plus importante en pratique. Un dépôt d’agrégats est le bon outil pour appliquer des règles ; c’est un mauvais outil pour afficher quelque chose. Une liste paginée, triée, filtrée sur six critères, avec trois jointures d’affichage, ne passe pas proprement par un port de dépôt — on finit soit avec un port à trente méthodes, soit avec des agrégats chargés puis jetés à 95 %.
La réponse honnête est d’assumer deux chemins : les écritures par les ports et les agrégats, les lectures par des requêtes dédiées qui renvoient directement ce qu’affiche l’écran. C’est le point de départ de CQRS, et c’est une décision à prendre tôt.
Elle ne dit rien du découpage
La règle de dépendance est verticale : elle ordonne l’intérieur et l’extérieur. Elle ne dit rien des frontières horizontales entre domaines fonctionnels. Un hexagone unique de deux cents mille lignes reste un monolithe illisible, avec un domaine parfaitement pur.
Hexagonal et contextes délimités répondent à deux questions différentes, et il faut les deux.
Les transactions traversent la frontière
La difficulté technique la plus concrète, et la moins documentée. Une transaction est un concept d’infrastructure qui doit encadrer un cas d’usage, lequel appartient à l’intérieur. Aucune solution n’est propre :
| Solution | Ce qu’elle coûte |
|---|---|
| Injecter une unité de travail comme port | Un concept d’infrastructure entre dans le domaine, sous un nom neutre |
| Décorer le cas d’usage à la composition | La frontière transactionnelle devient invisible dans le code du cas d’usage |
| Ouvrir la transaction dans l’adaptateur entrant | L’adaptateur décide d’une propriété métier — l’atomicité |
| Un agrégat par transaction, et rien d’autre | La solution DDD ; impose la cohérence différée, ce qui n’est pas toujours acceptable |
La dernière est la plus cohérente avec le reste, et c’est celle que retiennent les agrégats. Elle suppose de l’assumer explicitement.
L’ORM résiste
Le chargement paresseux, le suivi automatique des changements et les collections gérées supposent que l’objet appartienne à l’ORM. Un domaine pur interdit tout cela, donc impose une correspondance explicite — et l’on perd des fonctionnalités pour lesquelles on paie déjà.
L’asynchronisme colore les ports
Dès qu’un port est async, tout ce qui l’appelle le devient, jusqu’au cœur du
domaine. Le domaine « sans dépendance technique » se retrouve à dépendre du modèle
d’exécution. Le contournement — garder les règles pures dans des fonctions
synchrones et confiner l’asynchronisme aux cas d’usage — fonctionne, mais demande
une vigilance constante.
Elle ne protège pas d’un mauvais modèle
À retenir en dernier parce que c’est le plus important : un domaine anémique derrière des ports reste anémique. L’architecture garantit que les règles ne dépendent de rien ; elle ne garantit pas qu’il y ait des règles, ni qu’elles soient au bon endroit. Un hexagone impeccable autour de classes sans comportement est un coût pur.
C’est pourquoi l’hexagone seul ne suffit pas, et pourquoi il se lit avec le DDD : l’un dit où mettre le domaine, l’autre dit ce qu’il y a dedans.
Ports & adaptateurs, Onion, Clean : ce qui les sépare vraiment
Les trois noms désignent la même idée centrale — la règle de dépendance — et diffèrent sur la quantité de structure qu’ils imposent à l’intérieur de la frontière.
| Ports & adaptateurs | Onion | Clean | |
|---|---|---|---|
| Auteur, date | Alistair Cockburn, 2005 | Jeffrey Palermo, 2008 | Robert C. Martin, 2012 |
| Métaphore | Un hexagone, sans haut ni bas | Des pelures concentriques | Quatre anneaux |
| Ce qu’il structure | La frontière uniquement | La frontière et l’intérieur | La frontière et l’intérieur |
| Vocabulaire | Port, adaptateur | Domain model, domain services, application services | Entity, use case, interface adapter, presenter, gateway |
| Apport propre | La symétrie : entrée et sortie traitées pareil | Le découpage interne explicite | La règle de dépendance énoncée universellement ; la scission entités / cas d’usage |
| Silencieux sur | L’organisation interne | Les adaptateurs entrants | Rien — c’est le plus prescriptif |
Ce que chacun apporte réellement
Ports & adaptateurs est le plus minimal et le plus fidèle à l’intention d’origine. Son apport propre est la symétrie : il n’y a pas de « haut » ni de « bas », seulement un dedans et un dehors, et l’application doit pouvoir être pilotée indifféremment par un utilisateur, un autre programme, un test automatisé ou un traitement par lots. C’est exactement ce que la vision en couches empêche de penser. Cockburn a d’ailleurs poussé le nom « ports et adaptateurs » précisément parce que « hexagonal » conduisait ses lecteurs à compter les côtés et à chercher six ports.
Onion ajoute un découpage interne explicite : modèle de domaine au centre, puis services de domaine, puis services applicatifs. Utile à une équipe qui découvre — elle sait où poser chaque fichier — et générateur de cérémonie quand les anneaux sont respectés pour eux-mêmes. En revanche, Onion parle peu des adaptateurs entrants, là où l’hexagone en fait un citoyen de première classe.
Clean est une synthèse, et elle apporte trois choses qui lui sont propres. La règle de dépendance énoncée comme un principe universel, ce qui est la formulation la plus reprise aujourd’hui. La distinction entités / cas d’usage : les entités portent des règles vraies en dehors de cette application, les cas d’usage sont propres au produit — distinction qui ne sert à rien tant qu’une seule application existe, et qui devient précieuse quand un back-office et une API publique partagent un métier. Et les présentateurs : dans la version stricte, un cas d’usage ne retourne rien, il appelle un port de sortie de présentation. C’est la partie la plus ignorée de Clean, et souvent à raison — elle double le nombre d’interfaces pour un bénéfice qui ne se manifeste que sur des interfaces utilisateur très variées.
En pratique
L’usage courant a tranché : « hexagonal » désigne aujourd’hui la famille entière, et personne ne vous reprendra pour avoir dit « hexagonal » en décrivant des anneaux de Clean. Les différences de substance se réduisent à deux questions concrètes :
- Faut-il séparer entités et cas d’usage ? Oui si plusieurs applications partagent le domaine, non sinon.
- Faut-il des présentateurs ? Presque jamais, sauf interfaces multiples et très différentes.
Pour le reste, choisissez un vocabulaire et tenez-le. Celui de « ports et adaptateurs » est le plus recommandable : il est le moins ambigu — un port est une interface, un adaptateur l’implémente —, il ne suggère aucun nombre, et il ne prescrit rien qui ne soit justifié.
Comparaison avec l’architecture en couches
| En couches | Hexagonale | |
|---|---|---|
| Sens des dépendances | Vers le bas, du métier vers la technique | Vers l’intérieur, de la technique vers le métier |
| Propriétaire des interfaces | La couche qui implémente | Le domaine qui consomme |
| Test du métier | Nécessite l’infrastructure | Adaptateurs en mémoire |
| Changer de stockage | Touche le métier | Touche un adaptateur |
| Points d’entrée multiples | Duplication ou couche partagée | Un adaptateur de plus |
| Passe à l’échelle de | Du nombre d’entités | De la densité de règles |
| Coût d’entrée | Nul | Élevé : ports, correspondance, composition |
| Terrain favorable | CRUD, domaine mince, durée courte | Domaine riche, longue durée de vie |
Références
- https://alistair.cockburn.us/hexagonal-architecture/ — l’article d’origine
- https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/
- https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html — l’argument en faveur de l’organisation par fonctionnalité
- https://netflixtechblog.com/ready-for-changes-with-hexagonal-architecture-b315ec967749 — retour d’expérience
- https://www.amazon.fr/dp/1839211962 — Tom Hombergs, Get Your Hands Dirty on Clean Architecture : le seul ouvrage qui traite sérieusement l’organisation des fichiers et le coût de la correspondance