web-dev-qa-db-fra.com

Ports et adaptateurs/architecture hexagonale - clarification des termes et mise en oeuvre

Après avoir lu différentes sources sur l'architecture des ports et des adaptateurs, dont Alistair Cockburn's article d'origine , je ne suis toujours pas sûr du sens précis des termes "port" et "adaptateur", en particulier en ce qui concerne la correspondance entre ces concepts et artefacts de mise en œuvre.

Plusieurs sources (par exemple, this post ) impliquent que les ports de ce modèle d'architecture sont des artefacts situés à l'extérieur, suivis des adapters de la couche intermédiaire qui assurent la traduction entre les les ports et le application qui est au cœur.

Cependant, dans l'article original de Cockburn, les ports apparaissent à l'extérieur ainsi qu'à l'intérieur de la couche adapter en fonction du sens de la communication:

  • Communication entrante: "Lorsque des événements arrivent du monde extérieur depuis un port, un adaptateur spécifique à la technologie le convertit en appel de procédure utilisable ou en message et le transmet à l'application."
  • Communication sortante: "Lorsque l'application a quelque chose à envoyer, elle l'envoie via un port à un adaptateur, qui crée les signaux appropriés nécessaires à la technologie de réception (humaine ou automatisée)."

En fait, pour moi, ni l'approche «tout extérieur» ni l'approche «intérieur et extérieur» n'a de sens - je verrais dans le ports des artefacts toujours placés à côté de l'application, quelle que soit la direction de la communication. Cela serait également compatible avec les métaphores port et adapter: E. g. ayant un appareil avec un port série, pour connecter un autre appareil sans port série à celui-ci, il me faudrait un adaptateur qui adapte les communications entrantes et sortantes du point de vue de mon appareil.

En ce qui concerne la mise en œuvre de cette architecture, la définition de ports apparaît plutôt comme une partie de mon application où les différents adaptateurs sont considérés comme "extérieurs" à mon application. Par exemple. une implémentation d'un seul port pourrait consister en une facade (à appeler par les adaptateurs pour la communication entrante) et une interface (à mettre en œuvre par les adaptateurs pour la communication sortante).

Quelle est la signification correcte des termes port et adapter et comment ces concepts peuvent-ils être mappés sur des artefacts d'implémentation?

METTRE À JOUR:

Trouvé cet article qui ressemble à ma compréhension. La question demeure de savoir s'il existe un accord quelconque.

24
lost

inf3rno a donné une bonne réponse qui clarifie la question initiale, mais il peut être utile de souligner quelques autres utilisations des ports et des adaptateurs.

D'après ce que j'ai compris le port est une expression de votre interface .

Le port:

  • Définit l'exposition de la fonctionnalité du noyau (pour les ports "entrants")
  • Définit la vision de base du monde extérieur (pour les ports 'sortants')

L'adaptateur:

  • Est situé à l'extérieur du composant (hexagone)
  • Est utilisé pour garantir que le transport entre le port et la cible se déroule de manière à satisfaire le contrat avec l'interface du port
  • Est-ce que vous remplacez (en utilisant l'injection de dépendance) pour tester l'hexagone

Le port doit accepter l'adaptateur et s'assurer que l'adaptateur implémente l'interface. Ensuite, il devrait simplement appeler les méthodes/fonctions appropriées sur l'adaptateur.

Le port doit être inclus dans les tests de communication. Dans ce cas, les noyaux de deux hexagones voisins (ou un hexagone et un service) sont "simulés" et testent le port/l'adaptateur/l'adaptateur/l'assemblage du port.

Pour plus d'informations, vous pouvez consulter la présentation de Hexagonal Microservices donnée par James Gardner au Skillsmatter Microservices de Londres meetingup in July 2014 .

20
Vlad Mettler

Je pense que c'est un concept assez simple. Vous avez le noyau de l'application, qui ne dépend de rien en dehors de celui-ci. Par exemple, il ne dépend pas des frameworks HTTP, des pilotes de base de données, des frameworks de mailing, etc. Ce noyau a une interface très spécifique en fonction de votre domaine de problème. Le code de votre noyau d'application ne devrait donc changer que si votre domaine de problème change. .

Par exemple, vous avez des articles de blog et vous souhaitez leur ajouter des catégories. Les catégories doivent donc circuler dans l’ensemble du système, de la communication HTTP aux bases de données en écriture et vice-versa en lecture.

Et maintenant, si vous voulez remplacer votre base de données MySQL par exemple par MongoDB, car pourquoi pas. Cela ne devrait pas affecter le coeur, parce que l'application fait toujours exactement la même chose: elle stocke vos articles de blog et ofc. leurs catégories et les renvoie à la demande. Dans ce cas, vous n'avez donc besoin que d'un adaptateur MondogDB, que vous pouvez utiliser à la place de votre adaptateur MySQL.

Que faire si vous voulez par exemple une API REST, et pas simplement une simple page HTML? Cela n'affecte toujours pas le cœur de votre application, vous avez donc besoin d'un autre adaptateur pour la communication REST.

Donc, à mon avis, vos adaptateurs devraient avoir des interfaces spécifiques définies dans le noyau de votre application et les ports devraient communiquer avec le noyau de l'application à l'aide de ces adaptateurs. Donc, à mon avis, les ports n'existent pas nécessairement en tant que classes, mais uniquement pour les adaptateurs et leur interface. Ce concept est logique, car le cœur de votre application ne sera pas étroitement couplé aux ports que vous souhaitez utiliser, mais uniquement aux interfaces de l'adaptateur que vous définissez. (Il existe de nombreuses architectures similaires. Semblable à l'architecture pure, l'architecture à l'oignon, qui utilise le même concept avec un vocabulaire différent.)

16
inf3rno

Quelqu'un à mon travail a fait une excellente présentation interne sur cette architecture. À la fin, à l'heure des questions, un autre collègue a demandé:

N'est-ce pas une architecture en couches avec un nom différent et dessiné différemment?

Et, pour être honnête, c'est vrai dans une large mesure. Pour de nombreuses applications, une architecture hexagonale sera structurée de manière identique à une architecture en couches, avec quelques détails spécifiques:

  • Il y a une discipline accrue dans la définition des interfaces entre chaque couche (les ports), plutôt que d'appeler impl à impl.
  • Le noyau (la logique métier) est considéré comme la couche la plus importante, toutes les autres couches (les adaptateurs) étant considérées comme quelque peu subordonnées.
  • L'accent mis sur la définition des interfaces du point de vue du noyau empêche le langage des adaptateurs de s'infiltrer dans le noyau. Par exemple, si vous mettez votre persistance (par exemple, Hibernate) dans un adaptateur, vous ne devriez pas avoir de classes @Entity dans votre noyau.

Vous pouvez voir que, même en faisant toutes ces choses, il ne reste qu'une architecture en couches, les limites entre couches étant assez strictes et l'accent étant mis sur la couche centrale.

Donc, pour répondre spécifiquement à la question, vous pouvez comprendre les ports et les adaptateurs en reconnaissant que les ports sont les interfaces entrant et sortant du cœur et que les adaptateurs ne sont que les couches d’implémentation qui ne sont pas le cœur.

6
Graham Lea

De mon point de vue, après avoir lu l'article original et regarder quelques-unes des conférences d'Alistair Cockurn ("Alistair dans l'Hexagone"), je pense que la bonne approche est ce que vous appelleriez "tout à l'intérieur", c'est-à-dire dans les communications entrantes et sortantes, les ports sont "à l'intérieur" des adaptateurs. Les adaptateurs se situent entre les acteurs externes qui interagissent avec l'application et les ports. Les ports appartiennent à l'application.

Pour la communication entrante, l'acteur (acteur de pilote) déclenche la communication à l'aide d'un adaptateur de pilote. Cet adaptateur appelle un port de pilote de l'application, lui demandant de faire quelque chose.

Pour les communications sortantes, l'application déclenche la communication avec un acteur entraîné en définissant et en appelant un port piloté. Ce port est un contrat (généralement une interface) de ce dont l'application a besoin en termes d'objet. Ce port est implémenté par un adaptateur communiquant avec l'acteur externe.

Les dépendances doivent être:

Driver Actor -> Driver Adapter -> Hexagon <- Adaptateur Driven <- Acteur Driven

Les ports appartiennent à l'hexagone:

Les ports de pilote sont les API proposées par Hexagon aux adaptateurs de pilote.

Les ports Driven sont les SPI nécessaires à Hexagon, implémentés par Driven Adapters.

J'ai également eu beaucoup de mal avec cette phrase que vous mentionnez et qui apparaît dans l'article original:

"Lorsque les événements arrivent du monde extérieur sur un port, un adaptateur spécifique à la technologie le convertit en appel de procédure utilisable ou en message et le transmet à l'application."

Il dit que les ports de pilote sont "en dehors" des adaptateurs de pilote. Mais en lisant tout l'article et en regardant les discussions, je pense que ce n'est pas ainsi. Ce que la phrase appelle "port", est simplement l'interaction entre l'acteur de pilote externe et l'adaptateur. Le "port" doit correspondre à l'interaction entre l'adaptateur et l'application ("... le transmet à l'application").

0
choquero70