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. 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
  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
  3. 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)
  4. 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.
  5. 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
  6. 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
  7. ArchUnit Intro How it works © msg | October 2019

    | Stopping Entropy with ArchUnit | Alexander Schwartz Java Bytecode ArchUnit List of Violations Rules JUnit
  8. 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
  9. 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
  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) Architecture Violation [Priority: MEDIUM] – Rule 'classes that are annotated with @Configuration should have name matching '.*Configuration'' was violated (1 time): Class <de.ahus1.archunit.naming.SomeConfig> does not match '.*Configuration' in (SomeConfig.java:0)
  11. 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
  12. 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");
  13. 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
  14. 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
  15. 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<JavaMember>("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)); } } } };
  16. 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
  17. 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)"); } }
  18. 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
  19. 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)
  20. 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
  21. 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/
  22. 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
  23. .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