周三下午三点,一个Android开发团队正在处理一次令人头疼的合并冲突。原因很简单:两个特性团队各自的实现模块竟然互相依赖,牵一发而动全身,任何改动都让CI管道亮起红灯。技术负责人叹了口气:“我们需要一条不会说谎的规则——特性实现模块之间就是不能互相引用。但谁来守着这条线?”答案就在一个独立的架构测试模块里,和一款名为Konture的Kotlin工具。

最好的第一个架构测试,通常不是那种画满层级的理想化设计图。它应该是团队已经坚信、一说就明白的边界规则。比如:特性实现模块严禁依赖兄弟特性实现模块。这条规则足够具体,容易解释,破坏时会立刻制造痛苦,并且能培养起正确的习惯——把真实的架构决策编码进测试,而不是追求一个完美的抽象图景。本指南将用Gradle Kotlin DSL和JUnit 5一步步搭建这个守门人。

打开网易新闻 查看精彩图片

先别急着在现有模块里塞架构测试,那只会让检查逻辑和生产依赖搅在一起。正确做法是新建一个独立的架构测试模块。它拥有项目级视野,却不会让任何生产模块染上测试依赖。CI集成也很干净:只需运行一个专门任务,想查结构的时候再查。在你的多模块项目里,被测模块的规模可能看起来类似这样::app:core:domain:core:data:feature:checkout:api:feature:checkout:impl:feature:profile:api:feature:profile:impl:shared:androidApp:iosApp。模块的名字不重要,规则才重要。之后的每条检查都要用项目真实的模块和包名。

第一步,把Konture引入版本目录。在libs.versions.toml里声明版本0.6.8,配置好插件和库坐标。接着在根项目的构建脚本里应用插件,它会生成模块布局元数据,这是后面编写模块感知规则的前提。第二步,在settings.gradle.kts里注册:konture-test模块,再创建它的build.gradle.kts:添加JVM Kotlin插件和Konture插件,然后将需要检查的所有项目模块(如:app:core:domain:feature:checkout:api等)通过testImplementation引入,并加上JUnit 5依赖。最后别忘了让测试任务使用JUnit平台。这一步只做一件事:让架构测试模块能够看见代码和构建信息,但不要往里面塞业务逻辑。

接下来就是写下第一条构建图规则。在测试源码里创建ArchitectureGuardrailsTest类,包里写上公司域名。第一个测试方法的名字直接说明意图:“特性实现模块不能依赖兄弟特性实现模块”。方法体内调用Konture.modules,根据项目的真实模块关系来定义禁止的依赖边。这样一来,当有人不小心把:feature:profile:impl引入:feature:checkout:impl时,测试就会立刻失败,就像一道自动闸门,把混乱挡在CI管道最前端。

关于要不要把所有架构原则都塞进测试,有一种声音认为越全面越好,层与层之间、模块组之间都要画出不可逾越的线。但在实际项目中,过度设计往往让团队陷入维护规则的泥潭。正方的理想很丰满,反方的务实更打动人:从一条团队已经达成共识、破坏成本又高的规则开始。它不是华丽的架构图,而是团队每天都在践行的边界。Konture正是让你用最少的仪式感,把这条边界变成硬约束。判断也在这里:先用一条规则养出习惯,再慢慢扩展套件,远比一开始就堆出几十条检查要有效。

Konture并不绑定测试运行器,同样的规则既可以在JUnit里跑,也能放在Kotest、TestBalloon或者任何Kotlin/JVM运行器里。你只需要记住三件事——模块隔离、真实规则、从一条开始——就能把它变成项目里最安静也最可信的那一层防护。下次CI变红时,你会感谢那个只写了十几行测试的下午。