知识卡片

组件架构中控制流方向与依赖方向的分离,及灵活部署选项

结构图卡

内容

基于[[视频网站案例四个角色驱动的用例分析与抽象用例]]的角色和用例,可以画出初步组件架构图:系统按视图、展示器、交互器、控制器四类组件划分,同时按对应的系统角色分组,双实线代表架构边界,每个组件对应一个潜在的jar/dll文件;其中目录视图(Catalog View)和目录展示器(Catalog Presenter)是应对抽象用例”查看目录”的具体做法——被设计成抽象类,各角色对应的组件则是它们的派生类。是否真要拆成这么多独立jar文件交付?答案是”是,又不全是”:编译构建环境确实该按组件分开、以便单独构建,但交付时完全可以灵活组合——可以按视图/展示器/交互器/控制器/工具类拆成5个jar文件分别部署被修改的组件,也可以把视图和展示器合并进一个jar、交互器控制器工具类各自独立,甚至更简单地把视图展示器合成一个jar、其余全部合成另一个jar——具体怎么分组可以随系统演进、根据实际变更情况调整。图中控制流的方向是从右向左:输入发生在控制器端,经交互器处理后交给展示器格式化,最后由视图展示结果;但源码依赖箭头并不跟着控制流走,大部分箭头反而是从左向右的——这正是依赖关系原则的体现:所有跨边界的依赖都指向同一个方向,即指向包含更高级策略的组件。图中”使用”关系(开放箭头)和控制流方向一致,而”继承”关系(闭合箭头)方向与之相反,这是开闭原则的应用——通过调整依赖关系,让底层细节的变更不会波及高层策略组件。整套架构实现了两个维度的隔离:一是按SRP对不同角色做隔离(对应不同的变更原因),二是依赖关系原则带来的隔离(对应不同的变更速率,取决于组件所在层级)——这种代码组织方式让部署时能灵活选择,随时把组件合并交付,也能在需求变化时灵活拆分调整。

结构图

flowchart LR
    Controller[控制器: 输入端] -->|控制流| Interactor[交互器]
    Interactor -->|控制流| Presenter[展示器]
    Presenter -->|控制流| View[视图: 输出端]
    Presenter -.源码依赖:指向高层策略.-> Interactor
    Controller -.源码依赖:指向高层策略.-> Interactor
    CatalogView[目录视图: 抽象类] -.继承,方向与控制流相反.-> ViewImpl[各角色的具体视图]
    CatalogPresenter[目录展示器: 抽象类] -.继承,方向与控制流相反.-> PresenterImpl[各角色的具体展示器]

参考来源

- 位置:《架构整洁之道》第33章《案例分析:视频销售网站》"组件架构""依赖关系管理""本章小结"(源文件:_epub-src/text/part0015_split_003.html) - 结论依据:原文描述组件按视图/展示器/交互器/控制器划分并对应jar/dll文件、目录视图与目录展示器作为抽象类被各角色具体组件继承,说明交付时可灵活合并部署,并明确控制流从右向左而源码依赖从左向右指向高层策略、"使用"与"继承"关系方向相反,最终总结为SRP角色隔离与依赖关系原则两个维度的隔离,直接支撑本卡片的结构图与解释。 - 原始内容:这里将系统划分成视图、展示器、交互器以及控制器这几个组件,同时也按照对应的系统角色进行了分组……所有跨越边界的依赖关系都应该是同一个方向,而且都指向包含更高级策略的组件……这两个维度的隔离都是为了将不同变更原因和不同变更速率的组件分隔开来。