端口和适配器/六边形体系结构 - 术语和实现的说明

时间:2014-04-15 10:31:32

标签: architecture hexagonal-architecture

阅读有关Ports& amp;适应器架构包括Alistair Cockburn的original article我仍然不确定术语的明确含义" port"和"适配器" - 特别是在将这些概念映射到实现工件时。

多个来源(例如this post)暗示此架构模式中的端口是外部的工件,后面是中间层中的适配器端口应用程序之间进行转换。

但是,在Cockburn的原始文章中, ports 出现在 adapter 层的外部和内部,具体取决于通信方向:

  • 入站通信:" 当事件从外部世界到达端口时,特定于技术的适配器会将其转换为可用的过程调用或消息,并将其传递给应用程序。& #34;
  • 出站通信:" 当应用程序有发送内容时,它会通过端口将其发送到适配器,从而创建接收技术所需的适当信号(人工或自动)。 / EM>"

实际上对我而言,"所有外部"内部和外部"内部和外部"方法是有意义的 - 我会将 ports 视为工件旁边的工件,无论通信方向如何。 Imo这也与 port adapter 隐喻一致:E。g。如果设备带有串口,要将没有串口的其他设备连接到此设备,我需要一个能够从我的设备角度调整入站和出站通信的适配器。

为了实现这个架构,我会看到 ports 的定义,而不是我的应用程序的一部分,我会看到不同的适配器 as" outside"我的申请E. g。单个端口的实现可以包含facade(由适配器调用以进行入站通信)和interface(由适配器实现,用于出站通信)。

术语 port adapter 的正确含义是什么?这些概念如何映射到实现工件?

更新:

找到this article,类似于我的理解。如果存在某种共同协议,问题仍然存在。

4 个答案:

答案 0 :(得分:20)

inf3rno给出了一个很好的答案,澄清了原始问题,但强调端口和适配器的其他用途可能很有用。

根据我的理解端口是您界面的表达

端口:

  • 定义核心功能的曝光(针对'传入'端口)
  • 定义外部世界的核心视图(用于'传出'端口)

适配器:

  • 位于组件外部(六边形)
  • 用于确保端口和目标之间的传输以满足与端口接口的合同的方式发生
  • 您替换(使用依赖注入)来测试六边形

端口应该接受适配器并确保适配器实现接口。然后它应该只在适配器上调用适当的方法/函数。

端口应包含在通信测试中。在那种情况下,什么被嘲笑'是两个相邻六边形(或六边形和一个服务)的核心,并测试端口/适配器/适配器/端口组件。

有关更多信息,您可以查看詹姆斯·加德纳和我在伦敦的Skillsmatter微服务meetup in July 2014中提供的六角形微服务的讨论。

答案 1 :(得分:17)

我认为这是非常简单的概念。你有应用程序核心,它不依赖于它之外的任何东西。例如,它不依赖于HTTP框架,数据库驱动程序,邮件框架等等......根据您的问题域,此核心具有非常特定的界面,因此只有在您的问题域发生更改时,应用程序核心的代码才会更改

例如,您有博客文章,并且您想要为其添加类别。因此,类别应该流经整个系统,从HTTP通信到数据库通过写入,反之亦然,通过阅读。

现在如果你想用MongoDB替换你的MySQL数据库怎么办,因为为什么不呢。它不应该影响核心,因为应用程序仍然完全相同:它存储您的博客帖子和ofc。他们的类别并按需返回。所以你需要的只是这种情况下的MondogDB适配器,你可以使用它来代替你的MySQL适配器。

如果您想要一个REST API,而不仅仅是一个简单的HTML页面,该怎么办?它仍然不会影响您的应用程序核心,因此您需要的是另一个用于REST通信的适配器。

因此,在我看来,您的适配器应该具有在应用程序核心中定义的特定接口,并且端口应该使用这些适配器与应用程序核心通信。所以在我看来,端口不必作为类,只是适配器及其接口存在。这个概念很有意义,因为您的应用程序核心不会与您想要使用的端口紧密耦合,只需要与您定义的适配器接口紧密耦合。 (顺便提一下,有许多类似的架构。就像干净的架构,洋葱架构,它使用相同的概念和不同的词汇。)

答案 2 :(得分:8)

我工作的人在这个架构上做了很好的内部演示。最后,在提问期间,另一位同事问道:

  

这不仅仅是一个具有不同名称并以不同方式绘制的分层架构吗?

而且,说实话,这在很大程度上是正确的。对于许多应用程序,六边形体系结构的结构与分层体系结构相同,只有一些具体细节:

  • 在定义每个层(端口)之间的接口方面有更多的纪律,而不是将impl调用到impl。
  • 关注“核心”(业务逻辑)是最重要的层,所有其他层(适配器)被视为有些屈服。
  • 从核心角度定义接口的重点是防止适配器的语言泄漏到核心。例如,如果您将持久性(例如Hibernate)放入适配器中,那么您的核心中不应该有任何@Entity类。

你可以看到,即使做了所有这些事情,它仍然只是一个分层的架构,只是层次之间的界限非常严格,而且集中在中心层。

因此,要专门回答这个问题,您可以通过识别端口是进出核心的接口来理解端口和适配器,而适配器只是不是核心的实现层。

答案 3 :(得分:0)

从我的观点来看,在阅读原文并观看Alistair Cockurn(" Alistair在Hexagone")中的一些演讲之后,我认为正确的方法是你会称之为"所有在"内,即在入站和出站通信中,端口是"内部"适配器。适配器位于与应用程序交互的外部actor和端口之间。端口属于该应用程序。

对于入站通信,actor(驱动程序actor)使用驱动程序适配器触发通信。此适配器调用应用程序的驱动程序端口,请求应用程序执行某些操作。

对于出站通信,应用程序通过定义和调用驱动端口来触发与驱动的actor的通信。此端口是应用程序在目的方面所需的合同(通常是接口)。此端口由适配器实现,该适配器与外部actor进行通信。

依赖性应该是:

司机演员 - >驱动适配器 - > Hexagon< - 驱动适配器< - 驱动的演员

Ports属于Hexagon:

驱动程序端口是Hexagon向驱动程序适配器提供的API。

Driven Ports是Hexagon所需的SPI,由Driven Adapters实现。

我提到的这句话也很困难,这句话出现在原始文章中:

"当事件从外部世界到达端口时,特定于技术的适配器会将其转换为可用的过程调用或消息,并将其传递给应用程序。"

据说驱动程序端口在外面"驱动适配器。但阅读整篇文章并观看会谈,我认为不是这样。句子调用" port",只是外部驱动程序actor和适配器之间的交互。 "端口"应该是适配器和应用程序之间的交互(" ...将它传递给应用程序")。