依赖方向决定变化怎样传播

说明源码、运行时和数据依赖并不相同,以及依赖方向如何影响替换、测试和发布。

依赖方向决定变化怎样传播

模块之间必须协作,但依赖指向会决定谁需要了解谁、哪一侧变化会扩散,以及组件能否独立测试和替换。讨论依赖方向时,必须区分源码、运行时、数据与部署关系;只画调用箭头容易遗漏真正约束系统演进的联系。

源码依赖规定编译知识

模块 A 导入模块 B 的类型或函数时,A 在源码层依赖 B。B 的公开契约变化可能要求 A 重新编译和修改。将稳定领域接口放在调用方可控制的边界,并让具体实现适配接口,可以避免高层规则直接依赖易变基础设施。

依赖反转不等于每个类都需要接口。只有当替换、隔离测试或所有权边界带来真实价值时,引入抽象才有意义。无差别增加接口只会扩大导航和维护成本。

运行时调用方向可能不同

源码中实现依赖接口,运行时却可能由高层模块调用该实现。事件和回调还会使控制流反向发生。架构图应分别说明编译依赖与运行时交互,否则“谁调用谁”无法解释组件能否独立构建。

远程调用增加超时、重试和部分失败。调用方向不仅影响代码,也决定故障传播路径。同步链越长,上游请求越容易被下游延迟拖累;异步消息降低时间耦合,却增加交付、顺序和幂等问题。

数据依赖常被接口图隐藏

多个模块直接读写同一张表时,即使没有源码调用,也共享字段语义、事务和迁移顺序。一个模块改变列含义,其他模块可能静默出错。共享文件、缓存键和消息格式也具有类似依赖。

明确数据所有者、只通过稳定接口或版本化事件交换,可以限制这种传播。但复制数据会引入一致性延迟和修复责任,不能把它描述成无成本解耦。

循环依赖阻碍独立演进

A 依赖 B、B 又依赖 A 时,双方难以单独理解、测试和发布。循环可能出现在包导入、服务调用或数据库迁移中。常见处理方式是提取真正共享的稳定概念、重新分配职责,或通过事件打破同步控制环。

把公共代码全部放进“共享模块”并不能自动消除问题。共享模块若不断吸收业务规则,会成为所有模块共同依赖的变化中心。共享内容应小而稳定,并有明确所有权。

稳定方向来自变化概率

更容易变化的细节通常应依赖较稳定的策略与契约,而不是让核心规则绑定具体框架、数据库或供应商。稳定不是永不变化,而是变化原因更少、兼容责任更清晰。

依赖规则可以通过构建检查和架构测试执行,但工具只识别显式关系。评审还要检查数据、部署和组织依赖。可靠的方向设计会说明允许的依赖、例外理由和迁移路径,让变化传播可预测,而不是追求一张没有箭头的图。

资料依据