Sebastian Sprenger
Sebastian Sprenger

Reputation: 125

How to prevent certain packages from using spring with ArchUnit?

If I wanted to keep a certain Java package free of 3rd party dependencies with ArchUnit, how would I do it?

More specifically I am looking at keeping my domain model in a hexagonal architecture free from spring code. I specified some rules which I believe ought to prevent the model from using spring. However, I am able to use spring annotations like @Component and @Bean without causing a violation.

What I tried so far is

layeredArchitecture().
  layer("domain").definedBy(DOMAIN_LAYER).
  layer("application").definedBy(APPLICATION_LAYER).
  layer("primary-adapters").definedBy(PRIMARY_ADAPTERS).
  layer("secondary-adapters").definedBy(SECONDARY_ADAPTERS).
  layer("spring").definedBy("org.springframework..")
  whereLayer("spring").mayOnlyBeAccessedByLayers("primary-adapters", "secondary-adapters", "application").
  because("Domain should be kept spring-free").
  check(CLASSES);

As well as

noClasses().that().resideInAPackage(DOMAIN_LAYER).
should().dependOnClassesThat().resideInAPackage("org.springframework..").
check(CLASSES);

noClasses().that().resideInAPackage(DOMAIN_LAYER).
should().accessClassesThat().resideInAPackage("org.springframework..").
check(CLASSES);

Here a code example which executes the tests just fine, although com.example.app.domain.Factory is importing org.springframework....

Upvotes: 2

Views: 1767

Answers (2)

fan
fan

Reputation: 2364

You can also go with name matching. So you don't have to write a custom DescribedPredicate.

 ApplicationCoreMustNotDependOnFrameworks = noClasses()
       .that().resideInAnyPackage(DOMAIN_LAYER)
       .should().dependOnClassesThat().haveNameMatching("org.springframework.")
       .orShould().dependOnClassesThat().haveNameMatching("javax.persistence.*")
       .because("Domain should be free from Frameworks");

in my case, I wanted an exception to that rule. I.e., instead of excluding completely Spring, I wanted to accept the classes in the event package (EventHanlder) so you can replace "org.springframework" with "org.springframework(?!.*event).*" which is a regular expression

Upvotes: 1

aggredi
aggredi

Reputation: 426

You can use DescribedPredicate:

void domainSpring() {
    DescribedPredicate<JavaAnnotation> springAnnotationPredicate = new DescribedPredicate<JavaAnnotation>("Spring filter") {
        @Override
        public boolean apply(JavaAnnotation input) {
            return input.getType().getPackageName().startsWith("org.springframework");
        }
    };
    classes().that().resideInAPackage(DOMAIN_LAYER).should()
            .notBeAnnotatedWith(springAnnotationPredicate).check(CLASSES);

}

Upvotes: 2

Related Questions