$30 off During Our Annual Pro Sale. View Details »

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.

Alexander Schwartz

December 10, 2020
Tweet

More Decks by Alexander Schwartz

Other Decks in Programming

Transcript

  1. Wildwuchs eindämmen mit ArchUnit
    Alexander Schwartz, Principal IT Consultant
    JavaLand, 2021-03-17

    View Slide

  2. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 2
    Principal IT Consultant @ msg
    15+ Jahre
    Java & Web
    7 Jahre
    PL/SQL
    7 Jahre
    IT-Consulting
    3,5 Jahre
    Online-Banking
    700+
    Geocaches
    @ahus1de
    7 Jahre
    Absatzfinanzierung

    View Slide

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

    View Slide

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

    View Slide

  5. Nach vielen Stunden von Vermutungen und Nachforschungen:
    Trotz der Annotation, warum liefert findById() immer noch die Entität?
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 5
    Es war einmal…
    Konventionen und Erfahrungen
    @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)

    View Slide

  6. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz
    Konventionen und Erfahrungen
    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)
    6
    Post Mortem Analyse
    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 Slide

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

    View Slide

  8. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz
    ArchUnit Intro
    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
    ArchUnit Überblick

    View Slide

  9. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 9
    Wie es funktioniert
    ArchUnit Intro
    Java Bytecode
    ArchUnit
    Liste mit Verstößen
    Rules
    JUnit

    View Slide

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

    View Slide

  11. „Alle mit @Configuration annotierten Klassen sollen auf Configuration enden“
    (Umgebung: JUnit 5)
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 11
    Herausforderung 1: Klassennamen
    Regel für Namen, Struktur und Konventionen
    @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

    View Slide

  12. „Alle mit @Configuration annotierten Klassen sollen auf Configuration enden“
    (Umgebung: JUnit 5)
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 12
    Herausforderung 1: Klassennamen
    Regel für Namen, Struktur und Konventionen
    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 Slide

  13. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz
    Regel für Namen, Struktur und Konventionen
    „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
    13
    Herausforderung 2: Abhängigkeiten zwischen Packages
    https://www.infoq.com/news/2014/10/ddd-onion-architecture
    Core
    Domain
    Application
    Adapter A
    Adapter B
    Adapter C
    Adapter Z

    View Slide

  14. „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.“
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 14
    Herausforderung 2: Abhängigkeiten zwischen Packages
    Regel für Namen, Struktur und Konventionen
    @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 Slide

  15. „Du sollst nicht java.util.logging benutzen!“
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 15
    Herausforderung 3: Verbotene Klassen und Methoden
    Regel für Namen, Struktur und Konventionen
    @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

    View Slide

  16. „Bei @Modifying sollen immer beide Properties gesetzt sein“
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 18
    Herausforderung 4: Verwendung von Annotationen
    Regel für Namen, Struktur und Konventionen
    @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 Slide

  17. © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 19
    Herausforderung 4: Verwendung von Annotationen
    Regel für Namen, Struktur und Konventionen
    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 Slide

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

    View Slide

  19. Mit einem JUnit Test prüfen, ob die Regel wirklich das findet, was sie finden soll:
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 21
    Regeln testen
    Vertrauen durch Kontrolle
    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 Slide

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

    View Slide

  21. Verstöße unterdrücken für Legacy Code:
    • 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)
    • Keine 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)
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 23
    Wo es passt und wo es etwas anderes braucht
    Grenzen
    https://jqassistant.org/

    View Slide

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

    View Slide

  23. © msg | März 2021 | 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 :-)
    25
    Zusammenfassung ArchUnit
    https://www.archunit.org/

    View Slide

  24. ArchUnit
    https://www.archunit.org/
    Source Code & Slides
    https://www.ahus1.de/post/archunit-entropy
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 26
    Links
    @ahus1de

    View Slide

  25. msg systems ag
    Amelia-Mary-Earhart-Straße 14
    60549 Frankfurt am Main
    Germany
    value – inspired by people
    Contact
    Alexander Schwartz
    Principal IT Consultant
    +49 171 5625767
    [email protected]
    @ahus1de
    © msg | März 2021 | Wildwuchs eindämmen mit ArchUnit | Alexander Schwartz 27

    View Slide