【问题标题】:Ports and adapters / hexagonal architecture - clarification of terms and implementation端口和适配器/六边形架构 - 术语和实现的澄清
【发布时间】:2014-05-29 15:40:43
【问题描述】:

在阅读了有关端口和适配器架构的不同资料后,包括 Alistair Cockburn 的 original article,我仍然不确定术语“端口”和“适配器”的确切含义——尤其是在将这些概念映射到实现工件时.

几个来源(例如this post)暗示此架构模式中的端口是最外面的工件,然后是中间层中的适配器ports和核心的应用程序之间。

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

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

实际上对我来说,“全外”方法和“内外”方法都没有意义 - 我将 ports 视为始终放置在应用程序旁边的工件,无论方向如何的沟通。 Imo 这也与 portadapter 隐喻一致:例如。有一个带串口的设备,要连接另一个没有串口的设备,我需要一个适配器,从我的设备的角度适应入站和出站通信。

在实现此架构时,我会看到 ports 的定义,而不是作为我的应用程序的一部分,我会看到不同的 适配器我的应用程序的“外部”。例如。单个端口的实现可以由facade(由适配器调用以进行入站通信)和interface(由适配器实现以进行出站通信)组成。

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

更新:

找到了this article,和我的理解差不多。如果有某种共同协议,问题仍然存在。

【问题讨论】:

    标签: architecture hexagonal-architecture


    【解决方案1】:

    我认为这是一个非常简单的概念。你有应用程序核心,它不依赖于它之外的任何东西。例如,它不依赖于 HTTP 框架、数据库驱动程序、邮件框架等等……这个核心有一个非常具体的接口,取决于您的问题域,因此您的应用程序核心的代码只有在您的问题域发生变化时才应该更改.

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

    现在,如果您想用 MongoDB 替换 MySQL 数据库,为什么不呢?它不应该影响核心,因为应用程序仍然做同样的事情:它存储您的博客文章和 ofc。他们的类别并按需返回。因此,在这种情况下,您只需要一个 MondogDB 适配器,您可以使用它来代替您的 MySQL 适配器。

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

    所以在我看来,您的适配器应该在您的应用程序核心中定义特定的接口,并且端口应该使用这些适配器与应用程序核心进行通信。所以在我看来,端口不必作为类存在,只是适配器和它们的接口。这个概念是有道理的,因为您的应用程序核心不会与您要使用的端口紧密耦合,而只会与您定义的适配器接口紧密耦合。 (顺便说一下,有多种类似的架构。像干净的架构、洋葱架构,它们使用相同的概念但词汇不同。)

    【讨论】:

    • 感谢您的详细回答,但最后它仍然没有详细说明我原来的问题。仍然希望得到更多澄清。
    【解决方案2】:

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

    据我了解端口是您界面的一种表达方式

    港口:

    • 定义核心功能的公开(对于“传入”端口)
    • 定义核心对外部世界的看法(针对“传出”端口)

    适配器:

    • 位于组件外部(六边形)
    • 用于确保端口和目标之间的传输以满足与端口接口的约定的方式进行
    • 是你替换(使用依赖注入)来测试六边形

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

    该端口应包含在通信测试中。在这种情况下,“模拟”的是两个相邻六边形(或一个六边形和一个服务)的核心,并测试端口/适配器/适配器/端口组件。

    有关更多信息,您可以查看我和 James Gardner 在伦敦的 Skillsmatter Microservices meetup in July 2014 上关于六角微服务的讨论。

    【讨论】:

    • 给您的小建议:在添加指向您创建的资源的链接时,您可能希望将您的单词选择从“请查看我的演讲...”更改为“了解更多信息,您可以查看我在...上发表的演讲。”在这种情况下,您链接到的演讲似乎是该问题的主题,所以应该没问题。但是,如果您不小心,它可能会被视为垃圾邮件或自我推销。
    • 我同意前面的评论。很好的介绍顺便说一句! :-)
    【解决方案3】:

    我工作中的某个人对这个架构做了一个很棒的内部演示。最后,在提问时间,另一位同事问道:

    这不就是一个名称不同、绘制方式不同的分层架构吗?

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

    • 在定义每一层(端口)之间的接口时更加规范,而不是调用 impl 到 impl。
    • “核心”(业务逻辑)是最重要的层,而所有其他层(适配器)都被视为从属地位。
    • 从核心的角度定义接口的重点可防止适配器的语言泄漏到核心中。例如,如果您将持久性(例如 Hibernate)放入适配器中,那么您的核心中不应该有任何 @Entity 类。

    您可以看到,即使做了所有这些事情,它仍然只是一个分层架构,只是层与层之间的边界非常严格,并且侧重于中心层。

    因此,为了具体回答这个问题,您可以通过认识到端口是进出核心的接口来理解端口和适配器,而适配器只是不是核心的实现层。

    【讨论】:

      【解决方案4】:

      在我看来,在阅读了原始文章并观看了 Alistair Cockurn 的一些演讲(“Alistair in the Hexagone”)之后,我认为正确的方法是您所说的“全在里面”,即在入站和出站通信,端口在适配器“内部”。适配器位于与应用程序交互的外部参与者和端口之间。端口属于应用程序。

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

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

      依赖项应该是:

      Driver Actor --> Driver Adapter --> Hexagon

      Ports 属于 Hexagon:

      Driver Ports 是 Hexagon 提供给 Driver Adapters 的 API。

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

      你提到的这句话我也很挣扎,它出现在原始文章中:

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

      这是说驱动程序端口是“外部”驱动程序适配器。但是读整篇文章,看谈话,我认为不是这样的。这句话所说的“端口”,只是外部驱动actor和适配器之间的交互。 “端口”应该是适配器和应用程序之间的交互(“...将其传递给应用程序”)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多