wake-up-neo.com

Ports und Adapter/Sechseck-Architektur - Begriffsklärung und Implementierung

Nachdem ich verschiedene Quellen über die Architektur von Ports & Adapters gelesen hatte, einschließlich Alistair Cockburns ursprünglicher Artikel , bin ich immer noch nicht sicher, welche Bedeutung die Begriffe "Port" und "Adapter" haben, insbesondere wenn es darum geht, diese Konzepte auf Implementierungsartefakte abzubilden .

Mehrere Quellen (z. B. this post ) deuten an, dass die ports in diesem Architekturmuster ganz außen Artefakte sind, gefolgt von den Adaptern in der Zwischenschicht, die zwischen den ports und die Anwendung ist das Herzstück.

In Cockburns Originalartikel erscheinen jedoch die ports sowohl außen als auch innerhalb des adapters , abhängig von der Kommunikationsrichtung:

  • Eingehende Kommunikation: " Wenn Ereignisse von außen an einem Port eintreffen, konvertiert ein Technologiespezifischer Adapter diesen in einen Aufruf oder eine Nachricht für eine verwendbare Prozedur und übergibt ihn an die Anwendung. "
  • Ausgehende Kommunikation: " Wenn die Anwendung etwas zu versenden hat, sendet sie es über einen Port an einen Adapter, der die entsprechenden Signale erzeugt, die von der empfangenden Technologie (menschlich oder automatisiert) benötigt werden. "

Eigentlich macht für mich weder der Ansatz "alles außerhalb" noch der Ansatz "innen und außen" Sinn - ich würde die ports als Artefakte sehen, die unabhängig von der Kommunikationsrichtung immer neben der Anwendung platziert werden. Imo wäre dies auch konsistent mit den Metaphern port und adapter : z. Wenn Sie ein Gerät mit einer seriellen Schnittstelle haben, benötigen Sie zum Anschließen eines anderen Geräts ohne serielle Schnittstelle einen Adapter, der die eingehende und ausgehende Kommunikation aus der Sicht meines Geräts anpasst.

Bei der Implementierung dieser Architektur würde ich die Definition der ports eher als Teil meiner Anwendung sehen, wo ich die verschiedenen adapters als "außerhalb" von mir sehen würde Anwendung. Z.B. Eine Implementierung eines einzelnen port könnte aus einer facade (die von Adaptern für die eingehende Kommunikation aufgerufen werden soll) und einer interface (die von Adaptern für die ausgehende Kommunikation implementiert werden) bestehen.

Was ist die richtige Bedeutung der Begriffe port und adapter und wie können diese Konzepte Implementierungsartefakten zugeordnet werden?

AKTUALISIEREN:

Fand diesen Artikel der meinem Verständnis ähnelt. Es bleibt die Frage, ob es eine Art gemeinsamer Vereinbarung gibt.

24
lost

inf3rno gab eine gute Antwort, die die ursprüngliche Frage klarstellt, es kann jedoch nützlich sein, einige andere Verwendungszwecke für Ports und Adapter hervorzuheben.

Nach meinem Verständnis ist der Port ist Ausdruck Ihrer Schnittstelle .

Der Hafen:

  • Definiert die Offenlegung der Funktionalität des Kerns (für eingehende Ports).
  • Definiert die Sicht des Kerns auf die Außenwelt (für ausgehende Ports).

Der Adapter:

  • Befindet sich außerhalb der Komponente (Sechseck)
  • Wird verwendet, um sicherzustellen, dass der Transport zwischen Port und Ziel auf eine Weise erfolgt, die den Vertrag mit der Schnittstelle des Ports erfüllt
  • Ist es, was Sie ersetzen (mit Abhängigkeitsinjektion), um das Sechseck zu testen

Der Port sollte den Adapter akzeptieren und sicherstellen, dass der Adapter die Schnittstelle implementiert. Dann sollte es lediglich die entsprechenden Methoden/Funktionen auf dem Adapter aufrufen.

Der Port sollte in die Kommunikationstests einbezogen werden. In diesem Fall werden die Kerne zweier benachbarter Sechsecke (oder eines Sechsecks und eines Dienstes) "verspottet" und die Baugruppe Port/Adapter/Adapter/Port getestet.

Weitere Informationen finden Sie in den Vorträgen über Hexagonal Microservices, die James Gardner und ich beim Londoner Skillsmatter Microservices Meetup im Juli 2014 gehalten haben.

20
Vlad Mettler

Ich denke es ist ein ziemlich einfaches Konzept. Sie haben den Anwendungskern, der nicht von etwas außerhalb abhängt. Beispielsweise hängt es nicht von HTTP-Frameworks, Datenbanktreibern, Mailing-Frameworks usw. ab. Dieser Core verfügt je nach Problemdomäne über eine sehr spezifische Schnittstelle. Daher sollte sich der Code des Anwendungskerns nur ändern, wenn sich Ihre Problemdomäne ändert .

Sie haben beispielsweise Blogbeiträge und möchten Kategorien hinzufügen. Die Kategorien sollten also durch das gesamte System fließen, von der HTTP-Kommunikation durch Schreiben in die Datenbanken und umgekehrt durch Lesen.

Was ist, wenn Sie Ihre MySQL-Datenbank zum Beispiel durch MongoDB ersetzen möchten? Warum nicht? Es sollte sich nicht auf den Kern auswirken, da die Anwendung immer noch dasselbe tut: Es speichert Ihre Blogbeiträge und ofc. ihre Kategorien und gibt sie bei Bedarf zurück. Sie benötigen also in diesem Fall nur einen MondogDB-Adapter, den Sie anstelle Ihres MySQL-Adapters verwenden können.

Was ist, wenn Sie beispielsweise eine REST -API wünschen und nicht nur eine einfache HTML-Seite? Es hat immer noch keinen Einfluss auf Ihren Anwendungskern. Sie benötigen also einen weiteren Adapter für die Kommunikation mit REST.

Meiner Meinung nach sollten Ihre Adapter über spezifische Schnittstellen in Ihrem Anwendungskern verfügen, und die Ports sollten über diese Adapter mit dem Anwendungskern kommunizieren. Meiner Meinung nach gibt es die Ports nicht als Klassen, nur die Adapter und ihre Schnittstelle. Dieses Konzept ist sinnvoll, da der Anwendungskern nicht eng mit den Ports verbunden ist, die Sie verwenden möchten, sondern nur mit den von Ihnen definierten Adapterschnittstellen. (Es gibt übrigens mehrere ähnliche Architekturen. Wie saubere Architektur, Zwiebelarchitektur, die dasselbe Konzept mit einem anderen Vokabular verwendet.)

16
inf3rno

Jemand bei meiner Arbeit hat eine großartige interne Präsentation dieser Architektur gemacht. Am Ende der Fragestunde fragte ein anderer Kollege:

Ist das nicht nur eine geschichtete Architektur mit einem anderen Namen und einer anderen Zeichnung?

Um ehrlich zu sein, das stimmt weitgehend. Für viele Anwendungen wird eine sechseckige Architektur mit einigen spezifischen Details identisch zu einer mehrschichtigen Architektur strukturiert:

  • Bei der Definition von Schnittstellen zwischen den einzelnen Ebenen (den Ports) wird mehr Disziplin als beim Aufruf von impl to impl verwendet.
  • Der Schwerpunkt liegt auf "dem Kern" (Geschäftslogik) als der wichtigsten Schicht, wobei alle anderen Schichten (die Adapter) als etwas untergeordnet betrachtet werden.
  • Der Fokus auf die Definition von Schnittstellen aus der Perspektive des Kerns verhindert, dass die Sprache der Adapter in den Kern eindringt. Wenn Sie zum Beispiel Ihre Persistenz (z. B. Ruhezustand) in einen Adapter stecken, sollten Sie keine @Entity-Klassen in Ihrem Core haben.

Sie können sehen, dass es auch bei all diesen Dingen immer noch eine mehrschichtige Architektur ist, nur wenn die Grenzen zwischen den Ebenen ziemlich streng sind und der Fokus auf der zentralen Ebene liegt.

Um die Frage konkret zu beantworten, können Sie Ports und Adapater verstehen, indem Sie erkennen, dass Ports die Schnittstellen zum Core und aus dem Core sind, und Adapter nur die Implementierungsebenen sind, die nicht den Core bilden.

6
Graham Lea

Aus meiner Sicht denke ich, nachdem ich den Originalartikel gelesen und einige Gespräche von Alistair Cockurn ("Alistair in the Hexagone") gesehen habe, denke ich, dass der richtige Ansatz der ist, den Sie als "alles im Inneren" bezeichnen würden, dh sowohl in der eingehenden als auch in der ausgehenden Kommunikation. Die Ports befinden sich "in" den Adaptern. Die Adapter befinden sich zwischen den externen Akteuren, die mit der App interagieren, und den Ports. Ports gehören zur App.

Bei der Inbound-Kommunikation löst der Actor (Driver Actor) die Kommunikation über einen Treiberadapter aus. Dieser Adapter ruft einen Treiberport der App auf und fordert die App auf, etwas zu tun.

Für die ausgehende Kommunikation löst die App die Kommunikation mit einem gesteuerten Akteur aus, indem sie einen gesteuerten Port definiert und anruft. Dieser Port ist ein Vertrag (in der Regel eine Schnittstelle) über das, was die App in Bezug auf den Zweck benötigt. Dieser Port wird von einem Adapter implementiert, der mit dem externen Akteur kommuniziert.

Abhängigkeiten sollten sein:

Treiberdarsteller -> Treiberadapter -> Sechskant <- angetriebener Adapter <- angetriebener Darsteller

Ports gehört zum Hexagon:

Treiberanschlüsse sind die von Hexagon zu Treiberadaptern angebotene API.

Angetriebene Ports sind die vom Hexagon benötigten SPI, die von Driven Adapters implementiert werden.

Ich habe auch viel mit diesem Satz, den Sie erwähnen, zu kämpfen, der im Originalartikel steht:

"Wenn Ereignisse von außen an einem Port ankommen, konvertiert ein Technologiespezifischer Adapter diesen in einen Aufruf oder eine Nachricht für eine verwendbare Prozedur und leitet sie an die Anwendung weiter."

Es heißt, dass Treiberports "externe" Treiberadapter sind. Aber wenn ich den ganzen Artikel lese und die Gespräche anschaue, denke ich, dass es nicht so ist. Was der Satz "Port" nennt, ist einfach die Interaktion zwischen dem externen Treiberakteur und dem Adapter. Der "Port" sollte die Interaktion zwischen dem Adapter und der App sein ("... übergibt ihn an die Anwendung").

0
choquero70