Wildwuchs eindämmen mit ArchUnit

Wildwuchs eindämmen mit ArchUnit

Das Entwicklungsteam hat sich auf Namenskonventionen für Klassen und Pakete geeinigt. Und letztens fiel beim Code-Review wieder die eine Klasse auf, die öfter falsch benutzt wird und damit zu Fehlern führt.

Solange dies nur auf einer Wiki-Seite steht, sind automatische Prüfungen nicht möglich. Werkzeuge, die aus diesen Regeln Dashboards erzeugen, sind auf den ersten Blick schön anzuschauen, geraten jedoch bald in Vergessenheit.

Die Bibliothek ArchUnit erlaubt es dem Entwicklungsteam Regeln als Java-Code zu schreiben und sie wie andere Unit Tests auszuführen. Entwickler können sie jederzeit lokal vor dem Commit ausführen. Der Continuous Integration Server führt sie zusammen mit den anderen Unit-Tests aus. Schlagen die Tests fehl, so weist eine verständliche Meldung auf das Problem hin.

Dieser Vortrag zeigt wie Architektur- und Best-Practice-Regeln als ArchUnit Tests geschrieben werden. Er vergleicht ArchUnit mit anderen Werkzeugen und zeigt Vor- und Nachteile auf.

5f528a3f6814d28b583f31842e3e8d9e?s=128

Alexander Schwartz

February 20, 2020
Tweet

Transcript

  1. 2.

    Principal IT Consultant @ msg Travel & Logistics © msg

    | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 15+ Jahre Java & Web 7 Jahre PL/SQL 7 Jahre IT-Consulting 3,5 Jahre Online-Banking 700+ Geocaches @ahus1de 7 Jahre Absatzfinanzierung
  2. 3.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  3. 4.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  4. 5.

    Konventionen und Erfahrungen Es war einmal… © msg | Februar

    2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Nach vielen Stunden von Vermutungen und Nachforschungen: Trotz der Annotation, warum liefert findById() immer noch die Entität? @Modifying @Query("delete from Ship s where s.type = ?1") void deleteInBulkByType(String type); Lösung: die Annotation macht gar nichts, solange die Properties gesetzt werden: @Modifying(clearAutomatically = true, flushAutomatically = true)
  5. 6.

    Konventionen und Erfahrungen Post Mortem Analyse © msg | Februar

    2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Frage: Wie bereiten wir uns vor, damit zukünftig niemand mehr in diese Falle tappt? Möglichkeiten: 1. Im Wiki aufschreiben (bei den Namens-Konventionen und Package-Vorgaben) 2. Prüfung automatisieren (und die anderen Sachen im Wiki gleich mit) 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.
  6. 7.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  7. 8.

    ArchUnit Intro ArchUnit Überblick © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 1. Bibliothek zum Java-Projekt hinzufügen 2. Definieren von Regeln:  Package-Struktur  Klassennamen  Abhängigkeiten  … 3. Ausführen von Regeln:  Standalone  JUnit 4  JUnit 5 Homepage: https://www.archunit.org/ Lizenz: Apache 2.0
  8. 9.

    ArchUnit Intro Wie es funktioniert © msg | Februar 2020

    | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Java Bytecode ArchUnit Liste mit Verstößen Rules JUnit
  9. 10.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  10. 11.

    Regel für Namen, Struktur und Konventionen Herausforderung 1: Klassennamen ©

    msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „Alle mit @Configuration annotierten Klassen sollen auf Configuration enden“ (Umgebung: 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 mit Code-Vervollständigung
  11. 12.

    Regel für Namen, Struktur und Konventionen Herausforderung 1: Klassennamen ©

    msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „Alle mit @Configuration annotierten Klassen sollen auf Configuration enden“ (Umgebung: 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)
  12. 13.

    Regel für Namen, Struktur und Konventionen 1. https://www.infoq.com/news/2014/10/ddd-onion-architecture Herausforderung 2:

    Abhängigkeiten zwischen Packages © msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „In einer Zwiebel-Architektur können äußere Schichten innere Schichten sehen, eine innere Schicht weiß nichts über die äußeren Schichten. Adapter sind unabhängig voneinander.“ Ziele (Auszug): • Unabhängige Testbarkeit von Domäne und Adaptern • Adapter können unabhängig hinzugefügt und versioniert werden Mittel: • Dependency Injection mit Interfaces in der Domäne • Jeder Teil in einem eigenen Java-Package Core Domain Application Adapter A Adapter B Adapter C Adapter Z
  13. 14.

    Regel für Namen, Struktur und Konventionen Herausforderung 2: Abhängigkeiten zwischen

    Packages © msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „In einer Zwiebel-Architektur können äußere Schichten innere Schichten sehen, eine innere Schicht weiß nichts über die äußeren Schichten. Adapter sind unabhängig voneinander.“ @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");
  14. 15.

    Regel für Namen, Struktur und Konventionen Herausforderung 3: Verbotene Klassen

    und Methoden © msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „Du sollst nicht java.util.logging benutzen!“ @ArchTest ArchRule shouldNotUseJavaUtilLogging = GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING .because("we use log4j2"); Projektspezifische Begründung hinterlegt Wiederverwenden einer vordefinierten Regel in ArchUnit
  15. 16.

    Regel für Namen, Struktur und Konventionen Herausforderung 4: Verwendung von

    Annotationen © msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz „Bei @Modifying sollen immer beide Properties gesetzt sein“ @ArchTest ArchRule modifyingAnnotationsShouldHavePropertiesSet = ArchRuleDefinition.members().that().areAnnotatedWith(Modifying.class) .should(HAVE_MODIFYING_PARAMETERS_SET) .because("so they will flush the persistence context ..."); Custom Condition
  16. 17.

    Regel für Namen, Struktur und Konventionen Herausforderung 4: Verwendung von

    Annotationen © msg | Februar 2020 | Wildwuchs eindämmen mit 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)); } } } };
  17. 18.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  18. 19.

    Vertrauen durch Kontrolle Regeln testen © msg | Februar 2020

    | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Mit einem JUnit Test prüfen, ob die Regel wirklich das findet, was sie finden soll: 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)"); } }
  19. 20.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  20. 21.

    Grenzen 1. https://jqassistant.org/ Wo es passt und wo es etwas

    anderes braucht © msg | Februar 2020 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz • Verstöße unterdrücken:  Datei mit regulären Ausdrücken  Selbstdefinierte Annotation um Ausnahmen zu markieren  „Einfrieren” von Verstößen in Legacy-Projekten (new in v0.11.0) • ArchUnit entnimmt alle Informationen dem Java Byte Code  Keine Informationen zu Java Generics (wegen Type Erasure)  Nur Annotationen mit RetentionPolicy von SOURCE  Kein Zugriff auf nicht-Java Dateien, Maven Dependency Information, Git Commit Verlauf, etc. (dafür wäre ggf. jQAssistant besser geeignet) • ArchUnit hat keine UI, um die bestehende Architektur zu visualisieren (dafür wäre ggf. jQAssistant besser geeignet)
  21. 22.

    Wildwuchs eindämmen mit ArchUnit © msg | Februar 2020 |

    Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz Konventionen und Erfahrungen 1 ArchUnit Intro 2 Regel für Namen, Struktur und Konventionen 3 Vertrauen durch Kontrolle 4 Grenzen 5 Zusammenfassung 6
  22. 23.

    Zusammenfassung ArchUnit © msg | Februar 2020 | Wildwuchs eindämmen

    mit ArchUnit | Alexander Schwartz • Erlaubt einem Team seine eigenen bewährten Praktiken und Architektur-Regeln zu formulieren und automatisch zu überprüfen • Entscheidungen sind in der Versionshistorie mit einer Begründung dokumentiert • Verstöße werden mit verständlichen Meldungen beschrieben • Regeln laufen als Teil des normalen Testlaufs. Bei Verstößen schlägt der Build fehl. • Entwickler können die Regeln lokal vor dem Check-In laufen lassen • Keine zusätzlichen Werkzeuge auf dem Server notwendig für Continuous Integration & besser als eine Wiki-Seite :-) https://www.archunit.org/
  23. 24.

    Links © msg | Februar 2020 | Wildwuchs eindämmen mit

    ArchUnit | Alexander Schwartz ArchUnit https://www.archunit.org/ Source Code & Slides https://www.ahus1.de/post/archunit-entropy @ahus1de
  24. 25.

    .consulting .solutions .partnership Alexander Schwartz Principal IT Consultant +49 171

    5625767 alexander.schwartz@msg.group @ahus1de msg systems ag Mergenthalerallee 73-75, 65760 Eschborn Deutschland www.msg.group