Nguyen Le Phong

Les fondamentaux de l'architecture logiciellePartie 2 sur 6

Clean, Onion & Hexagonal Architecture : Une étoile du nord, trois noms

Elles ressemblent à trois architectures rivales. En réalité, c'est la même idée — les dépendances pointent vers l'intérieur, les frameworks restent à la périphérie — dessinée de trois façons différentes. Voici ce que chacune apporte vraiment, et laquelle choisir.

Si vous avez passé un peu de temps à lire sur l'architecture logicielle, vous avez probablement croisé trois noms qui reviennent ensemble sans cesse : Hexagonal Architecture, Onion Architecture et Clean Architecture. Chacune a ses propres diagrammes, son propre vocabulaire et ses propres défenseurs passionnés. Les lire toutes les trois en une seule session peut donner l'impression d'écouter trois personnes se disputer pour savoir quelle est la meilleure façon de dire la même phrase.

Voici la version honnête : ce sont les mêmes idées, découvertes indépendamment à des époques différentes, dessinées avec des images légèrement distinctes. Chacune ajoute une petite nuance que les autres ne soulignent pas aussi fort. Une fois que vous avez vu l'étoile du nord commune, les différences deviennent évidentes — et vous pouvez choisir le vocabulaire que votre équipe préfère déjà.

L'unique idée que les trois partagent

Chacune de ces architectures repose sur une seule règle, parfois appelée la Règle de Dépendance : les dépendances dans le code source doivent toujours pointer vers l'intérieur, vers les règles métier, et jamais vers l'extérieur, vers les frameworks, les bases de données ou les I/O.

Imaginez-la comme la gravité. Votre domaine — la logique qui rend votre produit utile — se trouve au centre. Tout ce qui l'entoure (bases de données, frameworks web, SDK de paiement, fournisseurs d'e-mail, outils CLI) gravite à l'extérieur. Les couches extérieures ont le droit de connaître les couches intérieures. Les couches intérieures n'ont absolument pas le droit de savoir que les couches extérieures existent.

Pourquoi est-ce important ? Parce que les choses qui changent le plus souvent se trouvent à l'extérieur : vous changez de fournisseur de paiement, migrez vers une nouvelle base de données, réécrivez le frontend, ajoutez une application mobile. Si vos règles métier sont enchevêtrées avec ces détails, chaque changement se propage dans le code qui vous importe le plus. Si elles sont isolées, un changement de fournisseur est un simple remplacement à la périphérie — le cœur ne s'en aperçoit jamais.

Alistair Cockburn, Jeffrey Palermo et Robert C. Martin ont chacun repéré ce pattern sous un angle différent. Cockburn a dessiné un hexagone et a appelé les points de connexion « ports ». Palermo a dessiné des anneaux concentriques et les a appelés « couches ». Martin a dessiné des cercles concentriques et a donné des noms stricts à chaque couche. Dans tous les diagrammes, les flèches pointent dans le même sens : vers l'intérieur.

La règle unique

Les dépendances dans le code source pointent vers l'intérieur. Le domaine définit des interfaces ; le monde extérieur les implémente. Inversez cette direction n'importe où et vous avez cassé l'architecture — peu importe le nom que vous utilisez.

Hexagonal Architecture (Ports & Adapters)

Alistair Cockburn a introduit l'Hexagonal Architecture en 2005 sous le nom de Ports & Adapters. L'intuition centrale est symétrique : votre application possède deux types d'extérieur — des choses qui la pilotent (un utilisateur qui clique sur un bouton, un exécuteur de tests, un job planifié) et des choses qu'elle pilote (une base de données, un fournisseur d'e-mail, une API de paiement). Les deux côtés communiquent via des ports — des interfaces que le domaine possède — et des adapters — les implémentations concrètes qui connectent les vraies technologies à ces interfaces. La forme hexagonale est juste un indice visuel signalant qu'il existe de nombreux ports, pas seulement « en haut » et « en bas ». Pour le traitement complet des ports, adapters, côtés primaire/secondaire, et des exemples de code, l'article précédent de cette série — Ports & Adapters — le couvre depuis les premiers principes. La conclusion principale ici est que l'Hexagonal est la plus symétrique des trois : elle traite les appels entrants et sortants avec la même rigueur, et son vocabulaire (port, adapter, pilotant, piloté) est le plus concret et mécanique.

Onion Architecture

Jeffrey Palermo a décrit l'Onion Architecture en 2008. Il a conservé la même règle de dépendance vers l'intérieur mais a choisi une image différente : des anneaux concentriques, comme les couches d'un oignon. L'anneau le plus intérieur est le Modèle de domaine — vos entités et objets-valeurs fondamentaux, de purs concepts métier sans aucune technologie. L'entourant se trouve l'anneau des Services de domaine, qui contient la logique coordonnant plusieurs objets du domaine mais qui ne sait toujours rien du monde extérieur. Plus loin se trouve l'anneau des Services applicatifs, où vivent les cas d'utilisation et où l'orchestration se produit. Tout à l'extérieur se trouvent les détails d'infrastructure et d'interface utilisateur — bases de données, frameworks web, APIs externes.

Ce que l'Onion met en avant, que les deux autres ne soulignent pas aussi fort, est la distinction entre modèle de domaine et services de domaine. Palermo écrivait pour des équipes .NET qui confondaient souvent « la chose qui représente une commande » avec « le service qui traite les commandes » — une séparation réellement utile. L'Onion rend aussi la stratification très visuelle : vous pouvez regarder la liste des imports de n'importe quelle classe et savoir immédiatement à quel anneau elle appartient en fonction de ce qu'elle est autorisée à importer. Si un service de domaine essaie d'importer une bibliothèque de base de données, il est manifestement dans le mauvais anneau.

Clean Architecture

Robert C. Martin (Uncle Bob) a publié Clean Architecture en 2017, en synthétisant des idées qu'il affinait depuis des années. Il a dessiné les mêmes anneaux concentriques mais a donné à chacun un nom ferme et un objectif ferme. De l'intérieur vers l'extérieur : les Entités (les règles métier à l'échelle de l'entreprise — les choses qui seraient toujours vraies même sans ordinateur), les Cas d'utilisation (les règles métier spécifiques à l'application — exactement ce que le logiciel est supposé faire pour un utilisateur), les Adaptateurs d'interface (contrôleurs, présentateurs, passerelles — le code qui traduit entre le monde des cas d'utilisation et le monde extérieur), et les Frameworks & Drivers (l'anneau le plus extérieur — bases de données, frameworks web, bibliothèques UI, pilotes de périphériques).

L'ajout le plus distinctif que fait Clean Architecture est la couche des Cas d'utilisation explicitement nommée. Dans beaucoup de codebases, « cas d'utilisation » est une idée informelle — vous pouvez avoir un UserService qui fait dix choses différentes. Martin insiste pour faire de chaque cas d'utilisation un artefact de premier rang, nommé explicitement : une classe ou une fonction dont le nom dit littéralement ce qu'elle fait (PlaceOrder, RegisterUser, GenerateInvoice). Il appelle ça une « architecture qui crie » — quand un nouveau développeur ouvre la structure du projet, il devrait immédiatement voir ce que le système fait, et non quel framework il utilise. Martin a également formalisé le concept de Frontières — des seams d'interface explicites à chaque traversée d'anneau — et l'idée que les données traversant une frontière doivent être des structures de données simples et sérialisables, et non des objets de domaine riches portant un comportement caché.

Architecture qui crie

La phrase de Martin « architecture qui crie » traduit une aspiration simple : ouvrez n'importe quel dossier du projet et vous devriez immédiatement comprendre l'intention métier, pas la stack technique. Un dossier appelé use-cases/ contenant PlaceOrder.ts et RegisterUser.ts crie son intention. Un dossier appelé controllers/ contenant tout le reste ne chuchote rien d'utile.

Côte à côte : correspondance des vocabulaires

Le principal obstacle pratique lorsqu'on lit sur les trois est qu'elles utilisent des mots différents pour des concepts qui se recoupent. Voici le tableau de correspondance :

Concept Hexagonale (Cockburn) Onion (Palermo) Clean (Martin)
Le cœur le plus intérieur Domaine (pas de noms d'anneaux spécifiques) Anneau Modèle de domaine Anneau Entités
Cas d'utilisation métier Logique applicative à l'intérieur de l'hexagone Anneau Services applicatifs Anneau Cas d'utilisation (explicites, nommés)
Couche de traduction Adapters (pilotant + piloté) Anneau Infrastructure Anneau Adaptateurs d'interface
Technologie la plus extérieure En dehors de l'hexagone (HTTP, DB, etc.) Anneau UI / Infrastructure Anneau Frameworks & Drivers
Point de jonction Port (interface possédée par le domaine) Interface implicite à chaque limite d'anneau Frontière (interface explicite à chaque traversée)
Accent principal Ports symétriques ; testabilité ; remplaçabilité de chaque adapter Anneaux stratifiés ; distinction entre modèle de domaine et services de domaine Cas d'utilisation nommés explicitement ; structure qui crie ; frontières de données strictes
Idéal quand… Vous avez de nombreuses intégrations et avez besoin que chaque adapter soit remplaçable indépendamment Modèles de domaine riches avec une orchestration de services non triviale Grandes équipes qui ont besoin d'un vocabulaire commun et d'une structure de dossiers « qui crie »

Là où elles diffèrent vraiment en pratique

La règle commune cache de vraies différences d'emphase qui apparaissent quand vous vous asseyez pour écrire du code.

L'Hexagonale est la plus mécanique. Elle vous donne des emplacements précis et nommés : voici l'adapter pilotant, voici le port qu'il appelle, voici le cas d'utilisation qui le gère, voici le port de sortie, voici l'adapter piloté. Si vous suivez ces emplacements fidèlement, la structure s'écrit presque d'elle-même — et parce que les deux côtés sont symétriques, la discipline qui vous empêche de laisser une base de données s'infiltrer dans le domaine est la même qui empêche le domaine de fuir dans la couche HTTP. Le compromis est que l'Hexagonale ne vous dit pas comment structurer l'intérieur de l'hexagone — elle reste muette sur la question de savoir si votre cœur doit avoir des sous-couches comme le modèle de domaine versus les services de domaine.

L'Onion est la plus stratifiée. Là où l'Hexagonale trace une seule frontière (à l'intérieur de l'hexagone versus à l'extérieur), l'Onion trace plusieurs anneaux à l'intérieur du cœur. C'est précieux quand votre domaine est assez grand pour avoir besoin de sa propre structure interne — en distinguant les entités pures des services de domaine qui les coordonnent, et les services de domaine des services applicatifs qui orchestrent les cas d'utilisation. La métaphore de l'anneau est intuitivement visuelle, mais plus vous êtes strict à son sujet, plus vous avez besoin de discipline : il est tentant de sauter par-dessus des anneaux quand vous êtes pressé, et l'Onion ne vous donne pas de vérification mécanique comme le fait le vocabulaire port/adapter de l'Hexagonale.

Clean est la plus prescriptive. Martin nomme chaque couche et chaque concept, ce qui est à la fois un atout et une source de débat. L'atout : les équipes partagent un vocabulaire sans ambiguïté — quand deux ingénieurs discutent d'une « frontière de cas d'utilisation », tout le monde sait ce que ça signifie. La source de débat : la structure prescrite peut sembler lourde pour les petites applications — créer des interfaces InputBoundary, OutputBoundary et UseCase explicites pour chaque fonctionnalité représente beaucoup de cérémonie quand vous construisez un prototype. Clean Architecture insiste aussi sur des objets de transfert de données (DTOs) simples traversant chaque frontière d'anneau, ce qui ajoute du code mais rend les seams très explicites. Le bénéfice arrive à grande échelle, quand prévenir « la fuite d'objets riches » entre les couches devient un vrai problème et non une hypothèse.

En résumé : l'Hexagonale vous donne les orientations mécaniques les plus claires sur la façon de câbler les choses ensemble. L'Onion vous donne la stratification interne du domaine la plus claire. Clean vous donne le vocabulaire le plus clair et la structure de dossiers la plus opiniâtre. La plupart des codebases réels mélangent les trois sans s'en rendre compte.

La même idée de dépendance vers l'intérieur dessinée sous forme d'anneaux concentriques — identique à l'hexagone de la partie 1, juste une forme différente. Domain Entities / Model Use Cases / Domain Services Adapters / Interface Adapters / Application Services Frameworks · Databases · UI · External APIs Dependencies always point inward — the same rule as the hexagon in part 1.
Les trois architectures dessinent la même image : un domaine intérieur intact par la technologie, entouré d'anneaux qui ajoutent des détails vers l'extérieur, avec chaque flèche de dépendance pointant vers l'intérieur. L'Hexagonale le dessine comme un hexagone avec des ports étiquetés ; l'Onion et Clean le dessinent comme des cercles concentriques avec des anneaux nommés. La forme change ; la direction des flèches ne change jamais.

Laquelle choisir

Parce que les trois partagent la même règle centrale, choisir entre elles est vraiment une question de vocabulaire, de taille d'équipe et des nuances que votre situation demande.

Équipe & stade Approche suggérée Pourquoi
Solo / startup précoce Hexagonale, légèrement. Un ou deux ports autour de la base de données et du fournisseur le plus volatile. La vitesse compte plus que la complétude. Quelques interfaces vous donnent des tests rapides et un chemin de remplacement ; la cérémonie complète peut attendre.
Petite équipe, en croissance (≈ Série A) Onion ou Hexagonale avec des interfaces de port explicites sur tous les I/O. Les tests deviennent un goulot d'étranglement, et les nouvelles recrues doivent comprendre le domaine rapidement. Les étiquettes d'anneaux (ou les noms de ports) rendent la structure lisible d'un coup d'œil.
Taille moyenne (plusieurs squads) Vocabulaire Clean Architecture comme contrat partagé entre les équipes. Les squads se marchent dessus quand les frontières sont floues. Nommer chaque cas d'utilisation et chaque frontière donne aux équipes une couture nette pour travailler de façon indépendante.
Grande entreprise Les trois ensemble, avec des frontières explicites, des contrats versionnés et plusieurs adapters par port. Les exigences réglementaires, les systèmes hérités et la multi-procuration fournisseurs exigent toute l'explicité que seules les frontières complètes Clean + Hexagonale fournissent.

Quelques patterns réels qui correspondent à ce tableau :

Une startup fintech (8 ingénieurs) a utilisé deux ports — PaymentGateway et LedgerRepository — et a laissé tout le reste direct. Quand ils ont changé de fournisseur de paiement dix-huit mois plus tard, c'était une seule nouvelle classe adapter et un changement de câblage en une ligne. Le reste du codebase n'a pas été touché. C'est l'Hexagonale à son plus épuré et son plus efficace.

Une entreprise SaaS de taille moyenne (60 ingénieurs) a adopté les étiquettes d'anneaux Onion comme règle de revue de code : « rien dans l'anneau du modèle de domaine n'importe depuis l'extérieur de l'anneau, sans exception ». Les nouveaux ingénieurs comprenaient la politique de frontières dès leur première revue de PR. Le vocabulaire des anneaux est devenu un raccourci — « vous traversez les anneaux ici » était un commentaire de revue complet que tout le monde comprenait.

Une banque d'entreprise (plusieurs centaines d'ingénieurs, codebase vieux de 20 ans) a utilisé le vocabulaire complet de Clean Architecture, avec des interfaces UseCase nommées, des objets DTO explicites à chaque traversée d'anneau et deux adapters par port de sortie (ancien mainframe + nouveau système de core-banking fonctionnant côte à côte). La cérémonie était lourde, mais les auditeurs et les équipes de conformité pouvaient lire l'architecture comme une spécification, et les squads pouvaient se déployer indépendamment parce que les frontières étaient de vraies coutures dans le code.

Ne mélangez pas le vocabulaire sans glossaire

Le seul résultat vraiment mauvais est d'utiliser les trois noms de façon interchangeable dans la même équipe sans glossaire partagé. Si la moitié de l'équipe dit « port » et l'autre moitié dit « frontière », la revue de code devient un exercice de traduction. Choisissez un vocabulaire, écrivez-le, et tenez-vous y. La règle sous-jacente — les dépendances pointent vers l'intérieur — est ce qui compte.

Points clés à retenir

  • Une règle, trois diagrammes. L'Hexagonale, l'Onion et Clean Architecture appliquent toutes la même Règle de Dépendance : les dépendances dans le code source pointent vers l'intérieur, vers les règles métier, jamais vers l'extérieur vers les frameworks ou les I/O.
  • Hexagonale (Cockburn, 2005) vous donne les orientations les plus mécaniques : ports et adapters, côtés pilotant et piloté symétriques. Idéal quand vous avez de nombreuses intégrations à permuter.
  • Onion (Palermo, 2008) ajoute des anneaux concentriques explicites à l'intérieur du domaine, séparant le modèle de domaine des services de domaine et des services applicatifs. Idéal quand votre domaine est assez grand pour avoir besoin d'une structure interne.
  • Clean (Martin, 2017) nomme chaque couche et fait des cas d'utilisation des artefacts de premier rang. Ajoute l'« architecture qui crie » — la structure de dossiers déclare l'intention métier. Idéal pour les grandes équipes qui ont besoin d'un vocabulaire partagé et de DTOs stricts à chaque frontière.
  • Elles sont additives, pas concurrentes. La plupart des codebases matures mélangent les trois : câblage port/adapter Hexagonal, discipline d'anneaux Onion à l'intérieur du domaine, nommage des cas d'utilisation Clean au niveau de la couche applicative.
  • Adaptez la dose à l'équipe. Deux ports Hexagonaux suffisent pour une startup. La cérémonie complète Clean rapporte à l'échelle enterprise. Ne payez pas pour un vocabulaire dont vous n'avez pas encore besoin.
  • Choisissez un vocabulaire par équipe et écrivez-le. Le nom compte moins que la cohérence.

Le prochain article de cette série approfondit d'un niveau le mécanisme qui rend les trois possibles : Dependency Injection & Inversion of Control — le câblage pratique qui connecte les adapters aux ports à l'exécution sans que le domaine ne touche jamais un framework.