Mein Blog-Post Schubladendenken - aber konstruktiv hat beschrieben, welchen Wert Stereotypen für eine gemeinsam verstandene Architektursprache im Projekt haben können. Darin wurde auch angesprochen, dass Stereotypen darüber hinaus auch verwendet werden können, um die Architekturrichtlinien im Projekt zu beschreiben. Sind diese Richtlinien einmal definiert, wollen wir auch deren Einhaltung überprüfen und sicherstellen - idealerweise automatisiert. Mit diesem Aspekt der Architecture Governance beschäftigt sich dieser Blog-Post.
Architecture Governance: Kontrolle ist besser …
Architecture Governance beinhaltet mindestens zwei bereits erwähnte Aspekte: die Definition und Dokumentation der Richtlinien und deren Überprüfung inklusive der Berichterstattung bei Verletzungen gegen die Richtlinien.
Die Definition muss dabei in einer Sprache erfolgen, welche sich auf die relevanten Architekturelemente bezieht und dabei aussagt, was erlaubt ist, und was nicht. Nicht überraschend bieten sich hier wiederum die definierten Stereotypen als Repräsentation dieser Architekturelemente an. So lässt sich basierend auf den Stereotypen aussagen, welche Abhängigkeiten erlaubt bzw. verboten sind, in welchen Schichten sich eine Ausprägung eines Stereotypen befinden darf und welchen Eigenschaften eine Ausprägung erfüllen muss. Solche Regeln sollten zudem nicht nur postuliert, sondern auch nachvollziehbar begründet werden.
Bei der Überprüfung der Richtlinien müssen die definierten Regeln dann gegen die effektive Umsetzung in der Code-Basis angewendet und Verletzungen festgestellt werden. Dabei ist es von Vorteil, wenn direkt die Beschriebung der Richtlinien selbst als Input für die Analyse und Bewertung verwendet werden können. Dadurch kann sichergestellt werden, dass keine Abweichungen zwischen der Beschreibung der Regeln und des Inputs für die Analyse entstehen. Zudem soll diese Überprüfung möglichst automatisiert, regelmässig bzw. zeitnah nach Änderungen durchgeführt werden.
jQAssistant
Für beiden Aspekte, sowohl die Definition der Richtlinien als auch deren Überprüfung, existieren eine Vielzahl von frei verfügbaren und kommerziellen Tools. In diesem Blog-Post wollen wir das Tool jQAssistant verwenden. jQAssistant ist open-source und kostenfrei verfügbar. Es kann in unterschiedliche Build-Tools (z.B. Maven oder Gradle) integriert werden. Dadurch kann die Analyse der Richtlinien direkt als Teil der Continuous Integration Pipeline durchgeführt und der Build bei Verletzungen abgebrochen werden.
jQAssistant scannt unterschiedliche Artefakte wie z.B. Java Code oder XML-Dateien und bildet die darin gefundenen Strukturen und Konzepte in einer Graphendatenbank ab, konkret in Neo4j. Regeln lassen sich nun als Abfragen gegen diese Strukturen im Graphen beschreiben (unter Verwendung der Neo4j-Abfragesprache “Cypher”). Liefert eine solche Abfrage ein Resultat, entspricht dies einer Verletzung der jeweiligen Regel.
Darüber hinaus können mit jQAssistant auch eigene höherwertige Konzepte definiert werden, die danach in Regeln verwendet werden können. Dieser Mechanismus kann nun eingesetzt werden, um die Stereotypen aus der Architektursprache als solche Konzept zu formalisieren und diese danach für die Beschreibung der Regeln zu verwenden. Somit sprechen die Regeln ebenfalls wieder die Architektursprache bzw. die Semantik der Stereotypen, wodurch Durchgängigkeit und Verständlichkeit erreicht werden kann.
Die Regeln selbst lassen sich in AsciiDoc oder XML beschreiben und so direkt in die Architekturdokumentation integrieren. Die geforderte Konsistenz zwischen Beschreibung der Regeln und des Inputs für die Analyse ermöglicht jQAssistant dadurch, dass die Architekturdokumentation selbst direkt als Input verwendet werden kann.
Ein einfaches, aber lauffähiges Beispiel ist auf https://github.com/cstettler/onion-jqassistant-sample verfügbar.
Stereotypen als Konzepte abbilden
Bevor ausdrucksstarke Regeln beschrieben werden können, müssen die höherwertigen
Konzepte definiert werden. jQAssistant bildet standardmässig bereits sehr viele
Elemente aus dem Java Code im Graphen ab, so auch Klassen und ihre jeweiligen
Annotationen (Annotationen müssen dabei natürlich mindestens @Retention(CLASS)
haben, damit sie im Java Byte Code enthalten sind). Wurden die Stereotypen wie
im letzten Blog-Post empfohlen als Annotationen umgesetzt, lässt sich damit nun
für jeden Stereotypen ein eigenes Konzept definieren, z.B für Aggregate:
Dieser Cypher-Ausdruck selektiert alle Klassen, die mit einer Annotation
versehen sind, welche den angegebenen voll-qualifizierten Namen trägt, und setzt
auf diesen Klassen das Label Aggregate
. Dieses Label kann danach in Regeln
verwendet werden.
Das Konzept beschreibt somit ein Element aus der Architektursprache (“Aggregate”) mittels Elementen aus der Implementationssprache (“Type”, “Annotation”).
Schichten beschreiben
Zusätzlich zu Stereotypen lassen sich auch die gewünschten Ordnungsstrukturen einer Architektur als Konzepte beschreiben. Falls die Umsetzung z.B. mittels einer Onion Architecture erfolgen soll, können die einzelnen Ringe der “Zwiebel” als Konzepte basierend auf Packages ausgedrückt werden:
Dieser Ausdruck selektiert alle Klassen in allen Packages (inkl. Sub-Packages),
welche den Domain-Ring repräsentieren, und setzt auf diesen Klassen das Label
DomainRing
.
Alles am richtigen Platz …
Mit Hilfe dieser beiden höherwertigen Konzepte lässt sich nun eine erste Regel beschreiben: Aggregate müssen sich im Domain-Ring befinden.
Diese Regel selektiert alle Knoten im Graphen, welche mit dem Konzept
Aggregate
markiert wurden, aber nicht mit dem Konzept DomainRing
, und
liefert deren voll-qualifizierten Namen zurück. Jeder Treffer entspricht also
einem Aggregat, welches im falschen Ring liegt.
Die Regel verwendet dabei in der Formulierung nur noch Begriffe aus der Architektursprache (“Aggregate”, “Domain-Ring”), aber nicht mehr aus der Implementationssprache (“Klasse” oder “Package”).
Die Integration von jQAssistant in den Build-Prozess erlaubt es nun, den Build abzubrechen, sobald eine solche Verletzung erkannt wurde. Alternativ lassen sich Verletzungen auch lediglich in einem Report ausweisen, ohne den Build selbst abzubrechen.
Ruf mich (nicht) auf …
Ähnlich wie die Überprüfung der Platzierung von Ausprägungen bestimmter Stereotypen im richtigen Ring lassen sich auch unerlaubte Abhängigkeiten zwischen Stereotypen beschreiben und erkennen.
Sind analog zum Konzept eines Aggregates auch Konzepte z.B. für Application Services definiert (wiederum basierend auf dem jeweiligen Stereotypen), kann exemplarisch folgende Regel verwendet werden um sicherzustellen, dass ein Aggregate nie einen Application Service aufruft (Aufrufe in die andere Richtung sind hingegen in einer Onion Architecture erlaubt und normal):
Mit diesem Ansatz können entsprechend auch weitere unerlaubte Abhängigkeiten zwischen Stereotypen beschrieben werden. Ebenfalls ist es möglich, geforderte Struktur-Eigenschaften der Ausprägungen von Stereotypen selbst zu beschreiben, z.B:
- jedes Aggregate muss eine Methode für den Zugriff auf die Aggregate-Id bieten, welche mit dem
AggregateId
-Stereotypen markiert ist (zur Referenzierung des Aggregates von anderen Aggregaten aus / von aussen) - Value Objects müssen unveränderbar sein (zur Vermeidung von ungewünschten Seiteneffekten)
- Aggregates dürfen nur unveränderbare Eigenschaften nach aussen geben (zur Sicherstellung der fachlichen Konsistenz durch das Aggregate selbst)
Dabei können sich Regeln auf andere abstützen, so dass die oben beschriebene dritte Regel die durch die zweite Regel sichergestellte Semantik eines Value Objects voraussetzen kann und somit die Zusicherung erhält, dass alle vom Aggregate nach aussen gegebenen Value Objects die Anforderung an Unveränderbarkeit erfüllen.
Fazit
Dank der Abbildung von Stereotypen über Annotationen im Code und der Unterstützung durch höherwertige Konzepte von jQAssistant lassen sich Architekturregeln prägnant in der gemeinsam verstandenen Architektursprache definieren und automatisiert überprüfen. Dabei können diese Regeln direkt innerhalb der Architekturdokumentation beschrieben und begründet werden.
Durch die Integration in den Build-Prozess können Verletzungen dieser Architekturregeln bereits während der Entwicklung aufgedeckt und somit aufwändige Änderungen im Review-Prozess vermieden werden.