使用ArchUnit约束子包仅引用自身与common包的适配方案
自动适配新增feature包的ArchUnit架构测试方案
问题背景
项目包结构为org.myapp下包含common、feature1、feature2等子包,需要通过ArchUnit测试确保:每个feature包内的类仅能引用同feature包的类、外部类或common包的类。
当前使用layeredArchitecture实现的测试需要硬编码feature包名,新增feature包时必须手动修改测试代码,现有实现代码如下:
@ArchTest public static final ArchRule layer_arquitecture = layeredArchitecture().consideringAllDependencies() .layer("common").definedBy("org.myapp.common..") .layer("feature1").definedBy("org.myapp.feature1..") .layer("feature2").definedBy("org.myapp.feature2..") .whereLayer("feature1").mayNotBeAccessedByAnyLayer() .whereLayer("feature2").mayNotBeAccessedByAnyLayer();
解决方案
方案一:动态构建分层架构规则
通过扫描所有类动态识别feature包,自动为每个feature包添加分层规则,无需硬编码包名:
@ArchTest public static void dynamicFeatureLayerRule(JavaClasses classes) { // 初始化分层架构,定义common层 LayeredArchitecture architecture = layeredArchitecture() .consideringAllDependencies() .layer("common").definedBy("org.myapp.common.."); // 从扫描到的类中提取所有feature包名称 Set<String> featureLayerNames = classes.stream() .map(javaClass -> javaClass.getPackageName()) .filter(packageName -> packageName.startsWith("org.myapp.feature")) // 提取feature包的短名称(如从org.myapp.feature1.utils得到feature1) .map(packageName -> packageName.split("\\.")[3]) .collect(Collectors.toSet()); // 为每个feature包添加分层及访问规则 for (String layerName : featureLayerNames) { String packagePattern = String.format("org.myapp.%s..", layerName); architecture.layer(layerName).definedBy(packagePattern); // 禁止其他feature层访问当前feature层,确保feature之间隔离 architecture.whereLayer(layerName).mayNotBeAccessedByAnyLayer(); // 允许当前feature层访问common层 architecture.whereLayer(layerName).mayAccessLayer("common"); } // 执行架构检查 architecture.check(classes); }
方案二:自定义依赖隔离规则
直接通过依赖规则约束feature包的访问范围,无需维护分层列表,更简洁灵活:
@ArchTest public static final ArchRule featureIsolationRule = classes() .that().resideInAPackage("org.myapp.feature..") .should().onlyDependOnClassesThat( // 允许依赖自身所属的feature包及其子包 DescribedPredicate.describe("same feature package", javaClass -> { String classPackage = javaClass.getPackageName(); // 提取当前类的feature根包(如org.myapp.feature1) String featureRootPackage = classPackage.substring(0, classPackage.indexOf(".feature.") + 9) + classPackage.split("\\.")[3]; return classPackage.startsWith(featureRootPackage); }) // 允许依赖common包及其子包 .or(resideInAPackage("org.myapp.common..")) // 允许依赖项目外部的类 .or(resideOutsideOfPackage("org.myapp..")) );
方案说明
- 方案一基于分层架构模型,保留了分层语义,适合需要明确分层的场景,新增feature包时自动识别并添加规则
- 方案二直接针对依赖关系做约束,逻辑更直观,无需处理分层配置,同样支持自动适配新增feature包
内容的提问来源于stack exchange,提问作者Hugo Palma
相关产品推荐
相关产品推荐

