知识卡片

跨越架构边界:用"输出端"接口反转控制流与源码依赖的方向

结构图卡

内容

跨越[[整洁架构综合多种架构思想的五个共同特征与依赖关系规则]]描述的架构边界时,控制流和源码依赖的方向往往不一致:以控制器、展示器与用例的通信为例,控制流从控制器出发、穿过用例、最后执行到展示器代码,但源码依赖的方向却必须始终指向内——都朝着用例。这个矛盾靠[[抽象工厂模式与依赖反转名字的由来控制流与源码依赖方向相反]]所属的依赖反转原则解决:假设某段用例代码需要调用展示器,绝不能直接调用(这样会违反依赖规则——内层代码不能引用外层声明),正确做法是让业务逻辑代码调用一个定义在内层的接口(即”用例输出端”),再让展示器去实现这个接口——通过接口和继承关系的调整,源码依赖就能被限制在只朝正确的方向跨越边界。这个手法可以用来跨越系统里所有的架构边界:借助动态多态,把源码依赖方向和控制流方向反转开来,不管控制流原本朝哪个方向走,都能让源码依赖始终遵守”只指向内层”的规则。跨边界传递的数据同样有讲究:应该尽量用简单的结构体或可传输数据对象,或直接通过函数调用参数传递,也可以放进哈希表或组装成某个对象——核心要求是这个跨边界对象要有独立、简单的数据结构,绝不能投机取巧直接传递业务实体对象或数据库记录对象(比如很多数据库框架返回的”行结构体”这类便于查询的结果对象,就不该跨边界往内层传,因为这等于让内层代码引用了外层代码,直接违反依赖规则)——跨边界传输永远要采用内层最方便使用的形式。

结构图

sequenceDiagram
    participant Controller as 控制器(外层)
    participant UseCase as 用例(内层)
    participant OutputPort as 用例输出端接口(内层)
    participant Presenter as 展示器(外层)
    Controller->>UseCase: 控制流: 调用用例
    UseCase->>OutputPort: 控制流: 调用输出端接口
    Note over OutputPort,Presenter: 源码依赖: Presenter实现OutputPort<br/>方向指向内层,与控制流方向相反
    Presenter-->>OutputPort: 实现接口

参考来源

- 位置:《架构整洁之道》第22章《整洁架构》"跨越边界""哪些数据会跨越边界"(源文件:_epub-src/text/part0014_split_007.html) - 结论依据:原文说明控制流从控制器经用例到展示器、但源码依赖必须指向用例内层,用"用例输出端"接口由展示器实现来解决这个矛盾,并说明跨边界数据必须是独立简单的结构、不能直接传递业务实体或数据库行结构体,直接支撑本卡片的结构图与解释。 - 原始内容:我们需要让业务逻辑代码调用一个内层接口(图22.1中的"用例输出端"),并让展示器来负责实现这个接口……不要投机取巧地直接传递业务实体或数据库记录对象……当我们进行跨边界传输时,一定要采用内层最方便使用的形式。