问题
After reading different sources about the Ports & Adapters architecture including Alistair Cockburn's original article I am still not sure about the definite meaning of the terms "port" and "adapter" - especially when it comes to mapping these concepts to implementation artifacts.
Several sources (e. g. this post) imply that the ports in this architecture pattern are artifacts at the very outside, followed by the adapters in the intermediate layer that translate between the ports and the application that is at the heart.
However, in Cockburn's original article the ports appear at the outside as well as on the inside of the adapter layer dependent on the direction of communication:
- Inbound communication: "As events arrive from the outside world at a port, a technology-specific adapter converts it into a usable procedure call or message and passes it to the application."
- Outbound communication: "When the application has something to send out, it sends it out through a port to an adapter, which creates the appropriate signals needed by the receiving technology (human or automated)."
Actually for me neither the "all outside" approach nor the "inside and outside" approach make sense - I would see the ports as artifacts that are always placed next to the application regardless of the direction of communication. Imo this would also be consistent with the port and adapter metaphors: E. g. having a device with a serial port, to connect another device without a serial port to this I'd need an adapter that adapts inbound and outbound communication from the viewpoint of my device.
Coming to the implementation of this architecture I'd see the definition of the ports rather as a part of my application where I'd see the different adapters as being "outside" of my application. E. g. an implementation of a single port could consist of a facade
(to be called by adapters for inbound communication) and an interface
(to be implemented by adapters for outbound communication).
What is the correct meaning of the terms port and adapter and how can these concepts be mapped to implementation artifacts?
UPDATE:
Found this article which resembles my understanding. The question remains if there is some kind of common agreement.
回答1:
inf3rno gave a good answer which clarifies the original question, but it may be useful to highlight some other uses for ports and adapters.
According to my understanding the port is an expression of your interface.
The port:
- Defines the exposure of the core's functionality (for 'incoming' ports)
- Defines the core's view of the outside world (for 'outgoing' ports)
The adapter:
- Is located outside the component (hexagon)
- Is used to ensure that the transport between port and the target happens in a way that satisfies the contract with the port's interface
- Is what you replace (using dependency injection) to test the hexagon
The port should accept the adapter and make sure that the adapter implements the interface. Then it should merely call the appropriate methods/functions on the adapter.
The port should be included in communication testing. In that case, what is 'mocked out' is the cores of two neighbouring hexagons (or a hexagon and a service) and test the port/adapter/adapter/port assembly.
For more information you can view the talk about Hexagonal Microservices that James Gardner and I gave at London's Skillsmatter Microservices meetup in July 2014.
回答2:
I think it is pretty simple concept. You have the application core, which does not depend on anything outside of it. For example it does not depend on HTTP frameworks, database drivers, mailing frameworks, and so on... This core has a very specific interface depending on your problem domain, so the code of your application core should change only if your problem domain changes.
For example you have blog posts and you want to add categories to them. So the categories should flow through the entire system, from the HTTP communication to the databases by writing and vice-versa by reading.
Now what if you want to replace your MySQL database for example with MongoDB, because why not. It should not affect the core, because the application does still the exact same thing: it stores your blog posts and ofc. their categories and returns them on demand. So what you need is only a MondogDB adapter in this case, which you can use instead of your MySQL adapter.
What if you want for example a REST API, and not just a simple HTML page? It still does not affect your application core, so what you need is another adapter for REST communication.
So in my opinion your adapters should have specific interfaces defined in your application core, and the ports should communicate with the application core using these adapters. So in my opinion the ports do not necessary exist as classes, just the adapters, and their interface. This concept makes sense, because your application core won't be tightly coupled to the ports you want to use, just to the adapter interfaces you define. (There are multiple similar architectures by the way. Like clean architecture, onion architecture, which use the same concept with a different vocabulary.)
回答3:
Someone at my work did a great internal presentation on this architecture. At the end, during question time, another colleague asked:
Isn't this just a layered architecture with a different name and drawn differently?
And, to be honest, that's true to a large extent. For many applications, a hexagonal architecture will be structured identically to a layered architecture, with a few specific details:
- There is increased discipline in defining interfaces between each layer (the ports), rather than calling impl to impl.
- There is a focus on "the core" (business logic) as being the most important layer, with all other layers (the adapters) being viewed as somewhat subservient.
- The focus on defining interfaces from the perspective of the core prevents the language of the adapters from leaking into the core. For example, if you're putting your persistence (e.g. Hibernate) into an adapter, then you should not have any @Entity classes in your core.
You can see that, even when doing all these things, it's still just a layered architecture, just with the boundaries between layers being quite strict and a focus on the central layer.
So, to specifically answer the question, you can understand ports and adapaters by recognising that ports are the interfaces into and out of the core, and adapters are just the implementation layers that are not the core.
回答4:
From my point of view, after reading the original article and watch some talks by Alistair Cockurn ("Alistair in the Hexagone"), I think the correct approach is what you would call "all inside", i.e. in both inbound and outbound communication, the ports are "inside" the adapters. The adapters are between the external actors that interact with the app, and the the ports. Ports belong to the app.
For inbound communication, the actor (driver actor) triggers the communication using a driver adapter. This adapter calls a driver port of the app, requesting the app to do something.
For outbound communication, the app triggers the communication with a driven actor by defining and calling a driven port. This port is a contract (usually an interface) of what the app needs in terms of the purpose. This port is implemented by an adapter, that communicates with the external actor.
Dependencies should be:
Driver Actor --> Driver Adapter --> Hexagon <-- Driven Adapter <-- Driven Actor
Ports belongs to the Hexagon:
Driver Ports are the API offered by the Hexagon to Driver Adapters.
Driven Ports are the SPI needed by the Hexagon, implemented by Driven Adapters.
I struggled a lot too with this sentence you mention, which appear in the original article:
"As events arrive from the outside world at a port, a technology-specific adapter converts it into a usable procedure call or message and passes it to the application."
It is saying that driver ports are "outside" driver adapters. But reading the whole article and watching the talks, I think that it isn't that way. What the sentence calls "port", is simply the interaction between the external driver actor and the adapter. The "port" should be the interaction between the adapter and the app ("...passes it to the application").
来源:https://stackoverflow.com/questions/23081105/ports-and-adapters-hexagonal-architecture-clarification-of-terms-and-impleme