Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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)

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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)

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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");

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

Rules for Naming, Structure and Conventions Challenge 4: Use of Annotations (2/2) © msg | October 2019 | Stopping Entropy with ArchUnit | Alexander Schwartz ArchCondition 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)); } } } };

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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)"); } }

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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/

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

.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