In der agilen Softwareentwicklung zählt die Qualität der Architektur. Sie beeinflusst Wartbarkeit, Erweiterbarkeit und Performance langfristig. Doch wie lässt sich sicherstellen, dass alle Teams sich an Architekturvorgaben halten? Die Lösung: Automatisierte Architekturtests mit ArchUnit.
Warum ist Architekturqualität für dich entscheidend?
Die ISO 25010 nennt viele Qualitätsmerkmale – darunter Änderbarkeit, Wiederverwendbarkeit und die Modularität. Sobald dein Code gegen definierte Regeln verstößt, wird er schwer verständlich und kaum wartbar.
Typische Herausforderungen:
- Fehlinterpretationen von Architekturdiagrammen bei der Umsetzung
- Unbekannte oder manuell dokumentierte Regeln
- Inkonsistente Implementierungen über Teams oder Microservices hinweg
Folgen hiervon sind Abweichungen zwischen der geplanten und tatsächlichen Architektur sowie eine schlechte Wartbarkeit und Erweiterbarkeit.
ArchUnit: Architektur testen
ArchUnit ist ein Open-Source-Framework für Java (auch Forks für .NET, TypeScript und Python sind verfügbar). Es erlaubt dir, Architekturregeln direkt im Code zu formulieren – in Form von Unit Tests.
Deine Vorteile:
- Lesbare Regeln durch Fluent API („englische Sätze“ ermöglichen es selbst dem PO, Architekt oder Consultant die Regeln zu verstehen und vorzuformulieren)
- Automatisiert durch CI/CD ausführbar
- Architekturregeln versioniert über Git pflegen
Typische ArchUnit-Regeln – mit Beispielen
ArchUnit bietet eine Vielzahl an Möglichkeiten zur automatisierten Prüfung von Architekturvorgaben im Code. Die folgenden Beispiele zeigen exemplarisch einige typische Use Cases – stellvertretend für viele weitere, die je nach Projekt individuell ergänzt und eingesetzt werden können.
Beispiel 1: Layer Checks
Durch die Layer Checks wird sichergestellt, dass die Zugriffe zwischen den Schichten korrekt eingehalten sind.

Codebeispiel
layeredArchitecture()
.consideringAllDependencies()
.layer("Controller").definedBy("..controller..")
.layer("Service").definedBy("..service..")
.layer("Persistence").definedBy("..persistence..")
.whereLayer("Controller").mayNotBeAccessedByAnyLayer()
.whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
.whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service")
Beispiel 2: Cycle Checks
Durch die Cycle Checks wird sichergestellt, dass Module klar voneinander entkoppelt und ohne zyklische Abhängigkeiten strukturiert sind.

Codebeispiel:
slices().matching("com.myapp.(*)..").should().beFreeOfCycles()
Beispiel 3: Inheritance Checks
Durch die Inheritance Checks wird sichergestellt, dass Klassen, die bestimmte Interfaces implementieren, konsistente und konforme Namenskonventionen einhalten.

Codebeispiel:
classes().that().implement(Connection.class)
.should().haveSimpleNameEndingWith("Connection")
Beispiel 4: Class Dependency Checks
Durch die Class Dependency Checks wird sichergestellt, dass Klassen nur von den erlaubten anderen Klassen abhängig sind und unerwünschte Abhängigkeiten vermieden werden.

Codebeispiel:
classes().that().haveNameMatching(".*Bar")
.should().onlyHaveDependentClassesThat().haveSimpleName("Bar")
Praxistipps für dein Projekt
Diese Prinzipien haben sich aus unserer Erfahrung bewährt:
- Frühzeitig Regeln definieren
- Basisklassen für wiederverwendbare Strukturen wie Architekturregeln nutzen
- Bestehende PlantUML Diagramme referenzieren zum Abgleich der geplanten mit der tatsächlichen Implementierung
- Ausnahmefälle gezielt über Whitelisting erlauben
- Schnellstart mit GeneralCodingRules
Fazit: Architekturtests mit ArchUnit lohnen sich
ArchUnit ist leichtgewichtig, CI/CD-fähig und individuell erweiterbar. Es lässt sich schnell in bestehende Pipelines integrieren – und sorgt für nachhaltige Architekturqualität.
Gerade bei komplexen Projekten ist ArchUnit ein echter Gewinn. Anstelle händisch dokumentierte Regeln im Kopf behalten zu müssen, werden die definierten Regeln im Code automatisiert geprüft und während der Implementierung berücksichtigt.1
Dieser Beitrag wurde gemeinsam von Linda und Marcel geschrieben.

Marcel Sommer
Mehr Blogbeiträge von Marcel findest du hier.



