架构测试与依赖治理

共 19 题
📑 题目列表 19 题
#
★★★

1. ArchUnit(Java)的架构规则表达中依赖方向、包结构、注解使用、继承层级

ArchUnit 如何表达架构规则?依赖方向、包结构、注解使用、继承层级分别如何表达?

  • ArchUnit 的规则 DSL
  • 依赖方向、包结构、注解、继承层级的表达
  • 架构规则的可执行测试

ArchUnit 是 Java 的架构测试库,用流式 DSL 表达可自动执行的架构规则。依赖方向:classes().that().resideInAPackage("..domain..").should().onlyDependOnClassesThat().resideInAPackage("..domain..") 可强制领域层不依赖基础设施层。包结构:classes().that().haveSimpleNameEndingWith("Controller").should().resideInAPackage("..controller..") 约束命名与包对应。注解使用:classes().that().areAnnotatedWith("@Service").should(). ... 检查注解的适用范围。继承层级:classes().that().implement(SomeInterface.class).should(). ... 约束实现类。规则以 @ArchTest 标注的静态字段声明,由 JUnit 运行,违反即测试失败。

ArchUnit 把"架构约束"变成可执行的单元测试,在 CI 中守护架构。它基于字节码分析,能在不运行应用的情况下检查依赖方向、包结构、注解与继承关系,是架构守护的自动化手段。

@ArchTest
static final ArchRule domain_should_not_depend_on_infrastructure =
    classes().that().resideInAPackage("..domain..")
        .should().onlyDependOnClassesThat()
        .resideInAnyPackage("..domain..", "java..", "org..");
#
★★★

2. BOM(Bill of Materials)与 platform dependency 的版本协调

BOM(Bill of Materials)与 platform dependency 如何协调版本?

  • BOM 的概念
  • platform dependency(Spring Boot parents)
  • 版本协调机制

BOM(Bill of Materials)是一个只声明依赖版本(不含依赖本身)的聚合 POM,用于统一管理一组依赖的版本,避免各模块各自指定版本导致冲突。用 dependencyManagement 声明 BOM(如通过 <scope>import</scope> 导入),下游模块声明依赖时可不写版本,由 BOM 统一管理。platform dependency(如 Spring Boot 的 parent POM / spring-boot-dependencies)是典型的 BOM,它把 Spring 生态相关库的版本统一协调。使用方式:Spring Boot 项目继承 spring-boot-starter-parent 或用 <scope>import</scope> 导入 spring-boot-dependencies,再按需覆盖个别版本。BOM 协调版本保证依赖一致性、可升级、可管理。

BOM 解决"多依赖版本各自为政"的问题。通过统一管理版本,保证依赖兼容、可追踪、可整体升级。Spring Boot 的 platform BOM 是一站式协调 Spring 生态的权威做法。

#
★★★

3. Dependency-Cruiser(JavaScript)的依赖图规则中禁止循环依赖、限制层级

Dependency-Cruiser(JavaScript)如何定义依赖图规则?禁止循环依赖、限制层级如何实现?

  • Dependency-Cruiser 的规则配置
  • 循环依赖检测
  • 层级限制

Dependency-Cruiser 是 JavaScript/TypeScript 项目的依赖图分析工具,通过 .dependency-cruiser.js 配置文件中的 forbidden 规则定义依赖约束。禁止循环依赖:内置规则 no-circular 检测与禁止模块间循环依赖。限制层级:用 no-orphansno-deprecated-core 等,以及 from/to 匹配(如 from: { path: '^src/domain' }to: { pathNot: '^src/domain' })禁止领域层依赖展示层,限制依赖方向与层级。也可以配置 cycleseverity 为 error 阻止 CI。规则在 CI 中运行,违反即失败。它支持 paths、pathNot、dependsOn 等条件,表达精细化依赖规则。

Dependency-Cruiser 把"依赖图约束"变成可执行的配置,禁止循环依赖、限制层级方向。它分析源码依赖并生成可视化依赖图,作为前端/全栈项目的架构守护工具,与 Java 的 ArchUnit 对应。

#
★★★

4. 架构测试的「误报治理」中 ArchUnit/Dependency-Cruiser 规则如何分层(基建豁免、显式例外清单、按模块裁剪),避免规则爆炸后无人维护被整体禁用?

架构测试的"误报治理"如何做?ArchUnit/Dependency-Cruiser 规则如何分层(基建豁免、显式例外清单、按模块裁剪),避免规则爆炸后无人维护被整体禁用?

  • 架构测试误报的治理
  • 分层豁免策略
  • 避免规则爆炸被禁用

架构测试若误报过多会失去信任,最终被整体禁用。误报治理要分层:其一,基建豁免(infrastructure exemption)——对基础设施类(如工具类、框架集成、配置)的合理违规做出豁免,如忽略 java..org..、DTO 库;其二,显式例外清单(explicit exception list)——对确需破例的少数类用 ignoreDependencyfreeze 显式记录例外,而不是放宽整条规则;其三,按模块裁剪(scoping)——规则按模块/包裁剪,不同模块应用不同规则力度,避免全局规则误伤。关键是"例外要显式、有理由、可审计",让规则可维护、误报可控,从而长期被信任并保留。

误报是架构测试的天敌。分层豁免(基建豁免 + 显式例外 + 按模块裁剪)让规则精准、例外可控,避免因误报过多而整体禁用。规则应"宁可少而准,不可多而滥",维护成本与价值平衡。

#
★★★

5. 架构测试作为适应度函数中如何将架构约束转化为可自动验证的适应度函数,在演进式架构中持续守护边界?

架构测试如何作为适应度函数(fitness function)?如何将架构约束转化为可自动验证的适应度函数,在演进式架构中持续守护边界?

  • 适应度函数的概念
  • 架构约束的自动化验证
  • 演进式架构的守护

在演进式架构(Evolutionary Architecture)中,适应度函数(fitness function)是"对某项架构特性/约束的可客观度量的评判指标",架构测试是最典型的适应度函数之一。把架构约束转化为适应度函数:用 ArchUnit/Dependency-Cruiser 等工具把"领域层不依赖基础设施""无循环依赖""依赖方向正确"等约束写成可自动执行的测试,任何违反即失败,从而持续守护架构边界。适应度函数应客观、可自动运行、快速、在 CI 中执行,随架构演进持续验证。它能防止"架构漂移"——让每次变更都验证架构约束是否被破坏,实现"架构即代码、持续守护"。

适应度函数把"架构约束"从文档变成可自动验证的测试,是演进式架构的核心机制。它让架构约束在持续集成中不断被检验,架构演进时边界不被破坏,实现可演进的、可持续守护的架构。

#
★★

6. Maven enforcer + dependencyConvergence 的版本对齐强制

Maven enforcer + dependencyConvergence 如何强制版本对齐?

  • Maven Enforcer 插件
  • dependencyConvergence 规则
  • 版本对齐强制

Maven Enforcer 插件通过规则(rules)在构建时强制项目约束,其中 dependencyConvergence 规则检测依赖树中同一依赖出现多个不同版本的情况,若发生则构建失败(默认 fail),从而强制版本对齐。它确保依赖树中每个 groupId:artifactId 只有一个版本,避免"传递依赖版本冲突"导致的不确定性。使用:在 pom 中配置 maven-enforcer-plugin 的 dependencyConvergence 规则,可在 CI 中强制所有依赖版本收敛。若需允许特定冲突,可用 excludesdependencyManagement 显式锁定版本。该规则强制"依赖版本唯一",提升可复现性与稳定性。

dependencyConvergence 是"依赖版本冲突"的强制防线。它把"同一依赖多版本"视为错误,在构建时阻止,保证依赖树收敛。配合 dependencyManagement 锁定版本,实现版本对齐的自动化强制。

#
★★

7. 依赖收敛(dependency convergence)的版本冲突解决策略

依赖收敛(dependency convergence)的版本冲突如何解决?

  • 依赖收敛的含义
  • 版本冲突的解决策略
  • 依赖管理的原则

依赖收敛(dependency convergence)要求依赖树中同一依赖只有一个版本。版本冲突解决策略:其一,用 dependencyManagement 显式声明统一版本,覆盖所有传递依赖版本;其二,调整依赖自身的版本以匹配统一版本;其三,用排除(exclude)排除不需要的传递依赖;其四,对确需不同版本的冲突,需评估是否真实冲突(类库还是可共存),必要时用不同 groupId 或 shade/relocate 隔离。核心原则是"依赖越少越收敛、版本越明确越好":优先收敛到较新且兼容的版本,用 dependencyManagement 集中管理,减少冗余传递依赖。收敛失败时用 enforcer 报错并人工决策。

版本冲突解决的关键是"统一、明确、最小化"。用 dependencyManagement 集中声明版本、排除冗余传递依赖、必要时收敛到兼容版本,避免同一依赖多版本导致的运行时的不确定性。

#
★★

8. ArchUnit 的 cycle detection(循环依赖检测)的边界

ArchUnit 的 cycle detection(循环依赖检测)的边界是什么?

  • 循环依赖检测
  • 检测的粒度与边界
  • 无环依赖的约束

ArchUnit 用于检测循环依赖的规则是 slices().matching("..").should().beFreeOfCycles(),它把包/模块切成"切片"(slice),检测切片之间是否存在循环依赖(A 依赖 B、B 又依赖 A)。边界在于:检测的粒度由切片(slice)定义决定——按包、按模块、按层切分;规则只保证"切片之间无环",切片内部依赖不强约束;检测针对"依赖方向"形成的环,而非数据库/运行时环。无环依赖(acyclic dependency)是架构可维护性的关键:循环依赖破坏分层、难测试、难独立演进。ArchUnit 的 cycle 检测把这些边界固化为测试,违反即失败。

循环依赖检测的边界由切片定义,粒度可调(包/模块/层)。其价值是强制依赖无环、分层清晰,防止修改地雷。ArchUnit 让"无环"成为可自动验证的约束。

#
★★

9. ArchUnit 的 frozen rules(冻结规则)的演进控制

ArchUnit 的 frozen rules(冻结规则)如何用于演进控制?

  • frozen rules 的概念
  • 演进控制
  • 冻结规则的管理

ArchUnit 的 frozen rules(冻结规则)允许把"当前已存在的违规"冻结(store as frozen),规则运行时会忽略这些已冻结的违规,只对新增违规报错。这用于演进控制:当架构重构需要渐进式进行时,先冻结现有违规(记录违规清单),再逐步清理,期间新引入的同类违规会被拦截,防止"违规继续蔓延"。冻结规则有 FreezingArchRule 包装,可把现有违规存储为"白名单",后续清理后从冻结列表移除。它的价值是"承认现状但阻止恶化",让大规模架构治理可以渐进推进,避免一次性清理的巨大成本。

frozen rules 是"渐进式治理"的工具:冻结现有违规、只阻断新增违规,让团队在不中断业务的前提下逐步清理技术债。它把"现状"与"恶化"分开管理,是演进式架构治理的实用手段。

#
★★

10. ArchUnit 的 layered architecture(分层架构)的强制规则

ArchUnit 的 layered architecture(分层架构)强制规则如何配置?

  • 分层架构规则
  • layer 的定义与依赖
  • 强制分层约束

ArchUnit 提供 layeredArchitecture() 定义分层架构规则:layeredArchitecture().consideringAllDependencies().layer("Controller").definedBy("..controller..").layer("Service").definedBy("..service..").layer("Repository").definedBy("..repository.."),然后用 whereLayer("Controller").mayNotBeAccessedByAnyLayer()whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") 等声明各层依赖方向。它强制"上层依赖下层、禁止反向依赖、禁止跨层依赖",把分层架构约束固化为测试,违反即失败。还可配合 onlyDependOnClassesThat 增强约束。分层规则让"依赖单向"成为可自动验证的架构约束。

layeredArchitecture 把"分层 + 依赖方向"表达为可执行规则,强制 Controller -> Service -> Repository 的单向依赖,防止跨层与反向依赖。它是最常用的 ArchUnit 规则之一,守护分层架构的边界。

#
★★

11. 依赖治理与模块边界的编译期强制中 Java module / npm workspaces / Bazel 等模块化手段如何在编译期锁定领域依赖方向,与架构测试形成互补?

依赖治理与模块边界的编译期强制如何实现?Java module / npm workspaces / Bazel 等如何在编译期锁定领域依赖方向,与架构测试形成互补?

  • 编译期依赖强制
  • Java module、npm workspaces、Bazel
  • 与架构测试的互补

编译期强制依赖边界是比架构测试更严格的手段:架构测试在测试阶段检测,编译期强制在编译阶段就阻止非法依赖。Java 用 JPMS(Java Module)的 requires 声明模块依赖,模块未声明则无法访问,从而在编译期锁定依赖方向;npm workspaces / Bazel 用模块/目标的依赖图声明,构建系统只允许声明的依赖,未声明则编译失败。这些手段把"依赖方向"内化到构建系统,形成强约束。与架构测试互补:编译期强制是从"能否编译"层面硬性拦截,架构测试是"语义/命名/注解"层面的软性校验;两者结合,编译期保证模块边界,架构测试补充规则化的约束(如命名、分层、冻结),形成"硬强制 + 软校验"的完整守护。

编译期强制是"边界即构建"的硬约束,架构测试是"规则可表达"的软校验。JPMS/Bazel 等在编译期锁定依赖,架构测试补充语义规则,互补提升依赖治理的强度与灵活性。

#
★★

12. 依赖升级的架构回归中升级框架或库后如何用架构测试、契约测试与编译检查发现破坏性变更?

依赖升级后如何发现架构回归?升级框架或库后如何用架构测试、契约测试与编译检查发现破坏性变更?

  • 依赖升级的回归风险
  • 架构测试、契约测试、编译检查
  • 升级的验证流程

升级框架或库后可能产生破坏性变更,需通过多层验证发现:编译检查(编译期)——升级后代码能否编译通过,能否发现 API 签名变化、移除的方法;架构测试——升级是否破坏架构约束(如依赖方向、分层、包结构),用 ArchUnit/Dependency-Cruiser 重跑规则;契约测试——升级是否破坏对外契约(API/消息),用 Pact/Spring Cloud Contract 验证消费者契约;单元/集成测试——功能回归。升级流程:升级依赖 → 编译 → 跑单元/集成测试 → 跑架构测试 → 跑契约测试 → 修复破坏性变更 → 验证。关键是把架构测试与契约测试纳入 CI,升级时自动触发,及时发现破坏性变更。

依赖升级的回归由"编译 + 架构测试 + 契约测试 + 功能测试"多层覆盖。编译发现 API 破坏,架构测试发现依赖/边界破坏,契约测试发现协议破坏,功能测试发现行为破坏。形成升级的自动验证网。

#
★★

13. 架构测试的运行分层与 CI 策略中哪些规则每次提交执行、哪些合并前执行,如何控制执行成本?

架构测试的运行分层与 CI 策略如何设计?哪些规则每次提交执行、哪些合并前执行,如何控制执行成本?

  • 架构测试的分层
  • 提交级与合并级规则
  • 执行成本控制

架构测试按执行成本与反馈速度分层:快速规则(如编译、命名、基础依赖方向)在每次提交(pre-commit/CI quick)执行,反馈快、成本低;慢速/重规则(如全量依赖图、循环检测、跨模块分析、契约测试)在合并前(merge/PR)执行,成本高但覆盖全。控制成本:尽量快(增量分析、只测变更模块)、分层缓存(未变模块不重跑)、把慢规则放到合并门禁而非每次提交、按模块裁剪测试范围。策略:让"便宜的规则高频跑、贵的规则低频/合并前跑",平衡反馈速度与成本。架构测试也应纳入 CI 门禁,违规即阻塞合并。

架构测试分层的核心是"快规则高频、慢规则低频"。通过分层与增量分析控制成本,让每次提交快速反馈、合并前全量验证。CI 门禁让架构约束成为合并的硬条件。

#

14. 依赖方向(domain 不应依赖 infrastructure)的强制

domain 不应依赖 infrastructure 的依赖方向如何强制?

  • 依赖方向约束
  • 依赖倒置
  • 架构测试强制

"domain 不应依赖 infrastructure"是 DDD 的核心依赖方向,通过架构测试强制:用 ArchUnit 声明 classes().that().resideInAPackage("..domain..").should().onlyDependOnClassesThat().resideInAnyPackage("..domain..", "java.."),即领域层只能依赖领域层与 JDK,不得依赖基础设施层(JPA、Spring 实现、Mapper 等)。实现上,领域层通过接口(Repository 接口)表达依赖,基础设施层实现接口,依赖方向反转(基础设施依赖领域)。架构测试把该方向固化为规则,任何违反(领域代码 import 基础设施类)即测试失败,保证依赖整洁。

依赖方向是分层架构与 DDD 的生命线。架构测试用"领域层只能依赖领域层 + JDK"的规则强制方向,防止领域层被基础设施"污染"。它把依赖倒置落实到可自动验证的约束。

@ArchTest
static final ArchRule domain_must_not_depend_on_infrastructure =
    classes().that().resideInAPackage("..domain..")
        .should().onlyDependOnClassesThat()
        .resideInAnyPackage("..domain..", "java..", "javax..");
#

15. 依赖矩阵与组件边界中如何用架构测试工具维护组件间的依赖规则,禁止反向依赖与循环依赖的自动化手段有哪些?

如何用架构测试工具维护组件间的依赖矩阵与边界?禁止反向依赖与循环依赖的自动化手段有哪些?

  • 依赖矩阵与组件边界
  • 禁止反向依赖
  • 禁止循环依赖的自动化

依赖矩阵与组件边界描述组件(模块/层)之间的依赖关系,可用架构测试工具维护。禁止反向依赖:用 ArchUnit onlyDependOnClassesThat / resideIn 规则声明"组件 A 只能依赖组件 B",违反即失败;用 Dependency-Cruiser 的 from/to 声明依赖方向。禁止循环依赖:用 ArchUnit 的 slices().matching("..").should().beFreeOfCycles() 或 Dependency-Cruiser 的 no-circular 规则,自动化检测并禁止组件间循环。这些手段把"依赖矩阵"变成可执行规则,任何组件间出现反向或循环依赖即测试失败,在 CI 中守护组件边界。

依赖矩阵的自动化靠"声明式规则 + 检测工具"。ArchUnit 用简便的依赖规则表达方向,slices/cycle 检测循环;Dependency-Cruiser 用配置表达前端依赖。二者把"依赖方向正确、无环"固化为可自动验证的约束。

#

16. 架构决策的自动化验证?

架构决策如何自动化验证?

  • 架构决策记录(ADR)
  • 决策的自动化验证
  • 决策与实现一致

架构决策(如"领域层不依赖基础设施""使用 CQRS")可通过架构测试/适应度函数自动化验证,确保实现与决策一致。方式:把架构决策的关键约束写成可执行的架构测试(ArchUnit、Dependency-Cruiser、契约测试),纳入 CI 门禁,任何违反即失败,从而把这些决策"固化"为可自动验证的规则。配合 ADR(架构决策记录)记录决策的背景与理由,架构测试作为决策的执行项。这样"决策 → 实现 → 验证"闭环,架构决策不再只是文档,而是被持续守护的适应度函数。

架构决策的自动化验证即"把决策变成可执行的适应度函数"。ADR 记录决策,架构测试验证决策,CI 守护决策,使架构决策可落地、可验证、可审计,防止决策与实践脱节。

#

17. 依赖治理工具中依赖图与循环检测?

依赖治理工具有哪些?依赖图与循环检测如何实现?

  • 依赖治理工具
  • 依赖图生成
  • 循环检测

依赖治理工具包括:Java 的 ArchUnit(架构测试,含循环检测)、Maven 的 dependency:tree / enforcer(依赖树与收敛)、Jdeps/JDepend(依赖分析与循环检测);JavaScript/TypeScript 的 Dependency-Cruiser(依赖图 + 循环检测)、Madge(循环检测);通用有 Neo4j 等图分析。依赖图:工具解析源码/构建信息生成依赖关系图(可视化模块/类依赖)。循环检测:ArchUnit 的 beFreeOfCycles、Dependency-Cruiser 的 no-circular、Madge 的 --circular 检测依赖环。这些工具把依赖关系可视化并自动检测循环,配合 CI 守护依赖治理。

依赖治理工具的核心是"依赖图可视化 + 循环检测 + 规则约束"。ArchUnit 与 Dependency-Cruiser 分别守护 Java 与前端,Maven 工具治理版本,形成完整的依赖治理工具链。

#

18. 架构测试的 CI 集成?

架构测试如何集成到 CI?

  • 架构测试的 CI 集成
  • 门禁与反馈
  • 分层执行

架构测试集成到 CI 的方式:作为 CI 的一个测试阶段,在构建/测试流水线中运行(如 Maven 的 mvn test 跑 ArchUnit 测试、dependent-cruiser 命令跑前端规则),作为合并门禁——一旦架构测试失败,CI 失败,阻塞合并/发布。集成要点:快规则在提交/PR 阶段运行,慢规则在合并前运行;架构测试结果纳入 CI 报告,失败时给出具体违规信息便于修复;通过固定规则文件(ArchRule、dependency-cruiser 配置)确保规则随代码版本化。架构测试作为适应度函数,在 CI 中持续守护架构边界,防止架构漂移。

架构测试的 CI 集成让"架构约束"成为合并的硬条件。分层运行控制成本,失败即阻塞,规则随代码版本化,实现架构的持续守护与早期反馈。

#

19. 架构测试的定制化扩展中内置 DSL 不足以表达规则时,如何用 AST 或静态分析编写一次性检查并纳入门禁?

当内置 DSL 不足以表达规则时,如何用 AST 或静态分析编写一次性检查并纳入门禁?

  • 内置 DSL 的局限
  • AST/静态分析
  • 自定义检查与门禁

当 ArchUnit/Dependency-Cruiser 的内置 DSL 不足以表达特定规则(如"禁止使用某种 API 模式""检查特定注解组合""统计特定代码结构")时,可编写自定义检查:用 AST(抽象语法树)/静态分析工具(Java 的 JavaParser、Checker Framework、Error Prone;前端的 ESLint 自定义规则、TypeScript AST/ts-morph)解析源码,遍历 AST 检测特定模式,判断是否违反规则。把分析逻辑写成一次性脚本/自定义规则,输出违规清单,再接入门禁:在 CI 中运行该脚本,有违规即失败(自定义适应度函数)。这样"内置 DSL 未覆盖的规则"也能自动验证并纳入门禁,扩展架构守护的能力边界。

内置 DSL 覆盖常见规则,但特定/复杂的规则需自定义。AST 或静态分析提供底层能力,可编写任意语义的检查,作为一次性脚本接入 CI 门禁,是架构测试的定制化扩展,保证"规则总能被自动验证"。