测试@Service 的ArchUnit 规则仅依赖于自己包中的@Repository?

ArchUnit rule to test @Service only depends on @Repository in own package?

我想编写一个 ArchUnit 规则来验证服务 class(由 @Service Spring 注释注释)将仅使用其自己包中的存储库,不是来自其他包。

我目前有:

noClasses().that()
  .areAnnotatedWith(Service.class)
  .should()
  .dependOnClassesThat().areAnnotatedWith(Repository.class)
  .orShould().dependOnClassesThat().haveSimpleNameEndingWith("Repository")

这不好,因为它根本不允许对存储库有任何依赖。

或者,我可以这样做:

noClasses().that()
  .resideInAPackage("com.company.backend.user")
  .and()
  .areAnnotatedWith(Service.class)
  .should()
  .dependOnClassesThat(resideOutsideOfPackage("com.company.backend.user").and(annotatedWith(Repository.class)))
  .orShould().dependOnClassesThat(have(resideOutsideOfPackage("com.company.backend.user").and(simpleNameEndingWith("Repository"))))
  .as("Service classes should only use Repository class(es) from the own package");

但是我重复包名称 com.company.backend.user 3 次 我需要为每个包定义这个规则。

您需要定义依赖项的谓词并对其进行操作。

classes()
    .that()
    .areAnnotatedWith(Service.class)
    .should(ArchConditions.onlyHaveDependenciesWhere(RepositoryAccessesAreOnlyWithinPackage.PREDICATE));

谓词检查访问目标是否为存储库,如果是,它将验证目标是否位于与调用相同的包中 class:

private static class RepositoryAccessesAreOnlyWithinPackage
        extends DescribedPredicate<Dependency> {

    public static final DescribedPredicate<Dependency> PREDICATE = new RepositoryAccessesAreOnlyWithinPackage();

    public RepositoryAccessesAreOnlyWithinPackage() {
        super("repository accesses are only within the same package");
    }

    @Override
    public boolean apply(Dependency dependency) {
        if (isRepositoryAccess(dependency)) {
            return areClassesInTheSamePackage(dependency.getOriginClass(), dependency.getTargetClass());
        }

        return true;
    }

    private boolean isRepositoryAccess(Dependency dependency) {
        return dependency.getTargetClass().isAnnotatedWith(Repository.class);
    }

    private boolean areClassesInTheSamePackage(JavaClass originClass, JavaClass targetClass) {
        return originClass.getPackageName().equals(targetClass.getPackageName());
    }
}