Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stopping Entropy with ArchUnit

Stopping Entropy with ArchUnit

So your team has agreed on naming conventions for classes and how to structure classes in packages. Last week you found out that a specific class is error prone to use. Writing this down in a Wiki will not allow for automatic checks. Tools that produce dashboards look nice at first sight, but will fall into oblivion soon.

The library ArchUnit allows you to code your project’s rules as tests in pure Java code. They will execute like any other unit test. Developers can run them locally before they commit the code. When the tests fail, they provide a descriptive error message.

This talk shows how to code architecture rules and best practices as ArchUnit tests. It will compare ArchUnit with other tools to show limits and advantages of this approach.

Alexander Schwartz

October 17, 2019
Tweet

More Decks by Alexander Schwartz

Other Decks in Programming

Transcript

  1. .consulting .solutions .partnership
    Stopping Entropy with ArchUnit
    Alexander Schwartz, Principal IT Consultant
    BaselOne, Basel (CH), 2019-10-17

    View full-size slide

  2. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  3. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  4. Conventions and Discoveries
    Once upon a time…
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    This piece of code piece cost the team several hours of guessing and investigation:
    Even after adding the annotation, why does findById() still find the entity?
    @Modifying
    @Query("delete from Ship s where s.type = ?1")
    void deleteInBulkByType(String type);
    Solution: the annotation on its own doesn’t do anything before you add one or two properties:
    @Modifying(clearAutomatically = true, flushAutomatically = true)

    View full-size slide

  5. Conventions and Discoveries
    Post mortem analysis
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Question:
    How can we prepare ourselves that this problem traps no one else in the future?
    Options:
    1. Write it in the wiki
    (next to existing naming conventions and package usage guidelines)
    2. Automate the check
    (and try to automate what’s on the wiki page already)
    Lorem ipsum dolor sit amet, consetetur sadipscing
    elitr, sed diam nonumy eirmod tempor invidunt ut
    labore et dolore magna aliquyam erat, sed diam
    voluptua. At vero eos et accusam et justo duo
    dolores et ea rebum. Stet clita kasd gubergren, no
    sea takimata sanctus est Lorem ipsum dolor sit amet.
    Lorem ipsum dolor sit amet, consetetur sadipscing
    elitr, sed diam nonumy eirmod tempor invidunt ut
    labore et dolore magna aliquyam erat, sed diam
    voluptua. At vero eos et accusam et justo duo
    dolores et ea rebum. Stet clita kasd gubergren, no
    sea takimata sanctus est Lorem ipsum dolor sit amet.
    Lorem ipsum dolor sit amet, consetetur sadipscing
    elitr, sed diam nonumy eirmod tempor invidunt ut
    labore et dolore magna aliquyam erat, sed diam
    voluptua. At vero eos et accusam et justo duo
    dolores et ea rebum. Stet clita kasd gubergren, no
    sea takimata sanctus est Lorem ipsum dolor sit amet.

    View full-size slide

  6. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  7. ArchUnit Intro
    ArchUnit Overview
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    1. Add library to Java project
    2. Define rules:
     Package structure
     Class naming
     Dependencies
     …
    3. Execute rules:
     Standalone
     JUnit 4
     JUnit 5
    Homepage: https://www.archunit.org/
    License: Apache 2.0

    View full-size slide

  8. ArchUnit Intro
    How it works
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Java Bytecode
    ArchUnit
    List of Violations
    Rules
    JUnit

    View full-size slide

  9. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  10. Rules for Naming, Structure and Conventions
    Challenge 1: Naming conventions for classes
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “All classes that are annotated with @Configuration should have a class name ending with Configuration”
    (Environment: JUnit 5)
    @AnalyzeClasses(packages = "de.ahus1.archunit.naming",
    importOptions = ImportOption.DoNotIncludeTests.class)
    public class NamingArchUnitTest {
    @ArchTest
    public final ArchRule configurationsShouldBeNamedConfiguration =
    ArchRuleDefinition.classes().that().areAnnotatedWith(Configuration.class)
    .should().haveNameMatching(".*Configuration");
    }
    Fluent API w/
    auto-completion

    View full-size slide

  11. Rules for Naming, Structure and Conventions
    Challenge 1: Naming conventions for classes
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “All classes that are annotated with @Configuration should have a class name ending with Configuration”
    (Environment: JUnit 5)
    Architecture Violation [Priority: MEDIUM] –
    Rule 'classes that are annotated with @Configuration should have name matching
    '.*Configuration'' was violated (1 time):
    Class does not match '.*Configuration' in
    (SomeConfig.java:0)

    View full-size slide

  12. Rules for Naming, Structure and Conventions
    1. https://www.infoq.com/news/2014/10/ddd-onion-architecture
    Challenge 2: Package Dependencies
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “In an onion architecture an outer layer can see an inner layer but
    an inner layer has no knowledge of any outer layer.
    Adapters must not depend on each other.”
    Goals (excerpt):
    • Independent testability of domain and each adapter
    • Ability to add adapters and versioning of adapters as needed
    Means:
    • Use dependency injection with interfaces present in domain
    • Use separate packages for each part for structuring
    Core
    Domain
    Application
    Adapter A
    Adapter B
    Adapter C
    Adapter Z

    View full-size slide

  13. Rules for Naming, Structure and Conventions
    Challenge 2: Package Dependencies
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “In an onion architecture an outer layer can see an inner layer but an inner layer has no knowledge of any
    outer layer. Adapters must not depend on each other.”
    @ArchTest
    ArchRule adaptersShouldNotDependOnEachOther =
    SlicesRuleDefinition.slices().matching("..adapter.(*)..")
    .should().notDependOnEachOther();
    @ArchTest
    ArchRule onionArchitecture =
    Architectures.layeredArchitecture()
    .layer("adapter").definedBy("..adapter..")
    .layer("domain").definedBy("..domain..")
    .layer("application").definedBy("..application..")
    .whereLayer("adapter").mayNotBeAccessedByAnyLayer()
    .whereLayer("application").mayOnlyBeAccessedByLayers("adapter");

    View full-size slide

  14. Rules for Naming, Structure and Conventions
    Challenge 3: Forbidden Use Of Classes
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “You should not use java.util.logging!”
    @ArchTest
    ArchRule shouldNotUseJavaUtilLogging =
    GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING
    .because("we use log4j2");
    Add you own
    reason
    Re-use predefined
    rules in ArchUnit

    View full-size slide

  15. Rules for Naming, Structure and Conventions
    Challenge 4: Use of Annotations (1/2)
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    “Each use of @Modifying should have the properties set to actually flush the context.”
    @ArchTest
    ArchRule modifyingAnnotationsShouldHavePropertiesSet =
    ArchRuleDefinition.members().that().areAnnotatedWith(Modifying.class)
    .should(HAVE_MODIFYING_PARAMETERS_SET)
    .because("so they will flush the persistence context ...");
    Custom Condition

    View full-size slide

  16. Rules for Naming, Structure and Conventions
    Challenge 4: Use of Annotations (2/2)
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    ArchCondition super JavaMember> HAVE_MODIFYING_PARAMETERS_SET =
    new ArchCondition("have clearAutomatically/flushAutomatically set") {
    @Override
    public void check(JavaMember javaMember, ConditionEvents events) {
    Modifying annotationOfType = javaMember.getAnnotationOfType(Modifying.class);
    if (annotationOfType != null) {
    if (!annotationOfType.clearAutomatically()) {
    String message = String.format(
    "clearAutomatically is not set to true on %s", javaMember.getFullName());
    events.add(SimpleConditionEvent.violated(javaMember, message));
    }
    if (!annotationOfType.flushAutomatically()) {
    String message = String.format(
    "flushAutomatically is not set to true on %s", javaMember.getFullName());
    events.add(SimpleConditionEvent.violated(javaMember, message));
    }
    }
    }
    };

    View full-size slide

  17. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  18. Watching the Watchers
    Testing your rules
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Use a JUnit test to check that your rules actually spot the problems you want to track.
    public class TestingTheNamingTest {
    @Test
    public void shouldFindViolationsForModifying() {
    JavaClasses importedClasses =
    new ClassFileImporter().importPackages("de.ahus1.archunit.naming.violation");
    ArchRule rule = new NamingArchUnitTest().configurationsShouldBeNamedConfiguration;
    Assertions.assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> {
    rule.check(importedClasses);
    })
    .withMessageContaining("classes that are annotated with @Configuration should have")
    .withMessageContaining("(1 times)");
    }
    }

    View full-size slide

  19. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  20. Limitations
    1. https://jqassistant.org/
    Things to consider
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    • To suppress violations:
     specify a file with regular expressions
     add a self-defined annotation to mark exceptions in the rules
     “freeze” violations for legacy projects (new in v0.11.0)
    • ArchUnit will only see information available in the Java byte code
     no Java generics
    (due to type erasure)
     no annotations with RetentionPolicy set to SOURCE
     no access to non-Java files, Maven dependency information, Git commit history, etc.
    (consider jQAssistant for this)
    • ArchUnit doesn’t provide a UI to report the existing architecture
    (consider jQAssistant for this)

    View full-size slide

  21. Stopping Entropy with ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    Conventions and Discoveries
    1
    ArchUnit Intro
    2
    Rules for Naming, Structure and Conventions
    3
    Watching the Watchers
    4
    Limitations
    5
    Summary
    6

    View full-size slide

  22. Summary ArchUnit
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    • Allows a team to encode and enforce its own best practices and architecture rules
    • Decisions are documented with a reason in the code repository
    • Violation messages are descriptive and human-readable
    • Rules are checked as part of the usual test run and will fail the build
    • Developers run the validation locally before check-in
    • No additional server-side tools needed for continuous integration
    & more effective than a wiki page :-)
    https://www.archunit.org/

    View full-size slide

  23. Links
    © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz
    ArchUnit
    https://www.archunit.org/
    Source Code & Slides
    https://www.ahus1.de/post/archunit-entropy
    @ahus1de

    View full-size slide

  24. .consulting .solutions .partnership
    Alexander Schwartz
    Principal IT Consultant
    +49 171 5625767
    [email protected]
    @ahus1de
    msg systems ag
    Mergenthalerallee 73-75, 65760 Eschborn
    Deutschland
    www.msg.group

    View full-size slide