Softwarearchitektur Qualitätssicherung
Einführung
Die Softwarearchitektur ist eine wichtige Voraussetzung für die Verständlichkeit und Veränderlichkeit von Code, sowie für die Einhaltung von Software Qualitätszielen. Drei Ziele der Softwarearchitektur sind Wartbarkeit, Austauschbarkeit und Erweiterbarkeit. Durch die Einführung von Mustern und Konventionen können die Ziele erreicht werden. Diese werden vom gesamten Entwicklungsteam definiert.
Doch wie kann jetzt sichergestellt werden, dass die Muster und Konventionen eingehalten werden?
Oft werden dafür in regelmäßigen Abständen aufwändige Code Reviews durchgeführt. Im Zeitraum zwischen den Reviews können sich jedoch viele technische Schulden ansammeln. Die Behebung dieser Verstöße kann dann viel Zeit in Anspruch nehmen.
Tools wie ArchUnit und jQAssistant helfen solche Konventionsverstöße schon bei der Entwicklung zu vermeiden. Im nachfolgenden werden diese Tools miteinander verglichen und eine Empfehlung ausgesprochen.
jQAssistant
jQAssistant analysiert den Code und speichert Daten darüber in einer Neo4j-Graphdatenbank. Als Knoten werden dabei unter anderem Dateien, Klassen, Interfaces, Packages, Felder, Methoden und Annotationen angelegt. Verbindungen werden durch Schlüsselwörter wie CONTAINS, DEPENDS_ON, INVOKES, DECLARES, IMPLEMENTS und RETURNS abgebildet. Durch Plugins kann der Graph nochmals erweitert werden. Zum Beispiel um Code-Coverage, Informationen aus der Git-Historie, JUnit-Testergebnisse und vieles mehr. Außerdem kann jQAssistant durch eigene Plugins oder Regeln erweitert werden.
Beispiel Regeln:
- Namenskonventionen prüfen z.B. EJBs, JPA Entities, Test Klassen, Packages etc.
- Abhängigkeiten zwischen Modulen überprüfen
- API und Implementation Packages trennen
- Probleme identifizieren z.B. zyklische Abhängigkeiten, Tests ohne Assertions etc.
- Vererbungshierarchien anzeigen
- Kennzahlen analysieren: Anzahl-Methoden in einer Klasse
Um einen Eindruck vom zu analysierenden bzw. zu validierenden System zu verschaffen, kann ein Neo4j-Server samt GUI gestartet werden. Dann können über den Browser, Abfragen gegen die Datenbank ausgeführt werden:
Einrichtung
Kommandozeile
- jQAssistant herunterladen und entpacken
- Es können verschiedene Tasks ausgeführt werden (Details: hier )
Beispiel:
Ordner oder Dateien scannen:
- Windows:
.\jqassistant.cmd scan -f folderpath/file
- Linux:
./jqassistant.sh scan -f folderpath/file
Maven
- jQAssistant-Plugin zur pom.xml hinzufügen (https://jqassistant.org/get-started/)
- Innerhalb des Moduls ein neues Verzeichnis „jqassistant“ mit einem
rules.xml
anlegen (dort können Regeln definieren werden). - Ein Maven Build führt die jQAssistant Prüfung aus.
- Wenn gegen eine Regel verstoßen wird, bricht der Build
Definition der Regeln
Regeln werden als Queries in Cypher definiert (in XML oder AsciiDoc Files). Es können aber auch Skriptsprachen wie JavaScript, Ruby oder Groovy verwendet werden.
Beispiel mit XML und Cypher:
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="my-rules:TestClassName"> <requiresConcept refId="junit4:TestClass" /> <description>All JUnit test classes must have a name with suffix "Test".</description> <cypher><![CDATA[ MATCH (t:Junit4:Test:Class) WHERE NOT t.name =~ ".*Test" RETURN t AS InvalidTestClass ]]></cypher> </constraint> <group id="default"> <includeConstraint refId="my-rules:TestClassName" /> </group> </jqa:jqassistant-rules> |
ArchUnit
ArchUnit ermöglicht es den Java Code und seine Strukturen mit jedem Java-Unit-Testframework zu testen. Mit ArchUnit können Regeln für die statischen Eigenschaften der Architektur wie den folgenden implementieren:
- Prüfungen der Package Abhängigkeit
- Überprüfung der Abhängigkeiten von Klassen
- Vererbungprüfungen
- Annotationsprüfungen
- Layerprüfungen
- Zyklusprüfungen
Einrichtung
Die ArchUnit Dependency zur Parent pom.xml hinzufügen (https://www.archunit.org/userguide/html/000_Index.html)
Definition der Regeln
Die „Regeln“ werden in Form von UnitTests erstellt:
@Test public void Services_should_only_be_accessed_by_Controllers() { JavaClasses importedClasses = new ClassFileImporter().importPackages("com.mycompany.myapp"); ArchRule myRule = classes() .that().resideInAPackage("..service..") .should().onlyBeAccessed().byAnyPackage("..controller..", "..service.."); myRule.check(importedClasses); } |
Annotationen reduziert den Code noch auf die Regeldefinition:
@AnalyzeClasses(packages = "com.mycompany.myapp") public class MyArchitectureTest { @ArchTest public static final ArchRule myRule = classes() .that().resideInAPackage("..service..") .should().onlyBeAccessed().byAnyPackage("..controller..", "..service.."); } |
Fazit
Sowohl jQAssistant als auch ArchUnit sind OpenSource. ArchUnit ist mit Apache License V2 und jQAssistant GPLv3 lizenziert.
Mit ArchUnit lässt sich die statische Architektur von Java-Projekten auf entwicklerfreundliche Art und Weise automatisiert testen. Es benötigt hierzu keine weitere Infrastruktur und kann als Library in jedes beliebige Java-Projekt eingebunden werden. Zudem bietet ArchUnit eine mächtige API, um eigene Regeln zu definieren.
jQAssistant ist etwas schwergewichtiger und man muss sich zuerst die Skriptsprache Cypher aneignen. Jedoch bietet die Neo4j Datenbank mit den Informationen zur Systemarchitektur viele weitere Möglichkeiten. Zum Beispiel nach verschiedenen Abhängigkeiten zu suchen und diese dann gezielt aufbrechen. Außerdem ist ein automatisiertes Reporting von Strukturen in sicherheitsrelevanten Umfeldern möglich.
Ich würde ArchUnit für jedes professionelle Java Projekt empfehlen. Die Integration ist sehr schnell durchgeführt und die wichtigsten Regeln sind schnell definiert. Bei größeren Projekten und Monolithen würde ich eher auf jQAssistant setzen. Hier ist auch eine manuelle Analyse mittels der Neo4j Datenbank möglich.
Quellen:
https://blogs.oracle.com/javamagazine/unit-test-your-architecture-with-archunit
https://www.archunit.org
https://jqassistant.org
https://blog.seibert-media.net/blog/2018/01/12/werkzeuge-zur-architektur-und-code-validierung-jqassistant
https://blog.jdriven.com/2018/10/testing-the-architecture-archunit-in-practice/