You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 07:12:13