CUT: Richtig schneiden (5C-Design, Teil 2)

By 23. April 2019 Juni 11th, 2019 Allgemein, Inhaltliches

Je unabhängiger ein Baustein ist, desto einfacher wird er zu handhaben sein. Schneide Bausteine so, dass sie sich möglichst gut voneinander abgrenzen.

  1. Software-Entwurf: Ein Blick zurück und nach vorn
  2. CUT: Richtig schneiden
  3. CONCEAL: Verbergen
  4. CONTRACT: Schnittstelle festlegen
  5. CONNECT: Verbinden
  6. CONSTRUCT: Aufbauen
  7. It´s a Wrap: Zusammenfassung

Um Komplexität dauerhaft beherrschbar zu machen, gibt es kaum eine Alternative dazu, diese in einzelne kleinere Teile zu zerlegen. Dafür muss man sich zunächst einmal die Frage stellen, an welchen Grenzen diese Abgrenzung denn am besten funktioniert. Wie findet man diese? Im Endeffekt wird der Programmcode der Lösung ein Abbild der fachlichen Anforderungen sein. Die möglichen Grenzen müssten demnach bereits in den Anforderungen auffindbar sein. Warum eine solche “fachliche Grenzziehung” (auch Vertikalität genannt) zu mehr Eigenständigkeit der Bausteine führt möchte ich anhand eines Beispiels verdeutlichen.

Schichten vs. Vertikale Architektur

Abbildung 1 zeigt ein nach klassischer Manier in technische Strukturen zerlegtes System. Daraus ergeben sich die dort abgebildeten Schichten, manchmal auch Layer genannt. Damit werden vorrangig die technischen Aspekte der Lösung betont. Dies hat den Vorteil, dass Entwickler passend zu ihren Stärken Strukturen im Code wiederfinden. Es werden außerdem querschnittliche Themen wie Persistenz gleichartig behandelt, was meist auch gewünscht ist. Auch das User-Interface wird für den Benutzer hier wohl ein durchgängig flüssiges Erlebnis darstellen. Aber: Sobald der Wunsch auftaucht, in irgendeinem der bestehenden Features ein weiteres Eingabe-Feld hinzuzufügen, zieht dies eine Kaskade an Änderungen in allen Layern nach sich. Man beginnt in der DB, zieht dies dann im DB-Access-Layer nach, danach in der Service-Schicht und zum Schluss im UI. Auch wird ein Ausfall einer niedrigeren Schicht sich immer auch auf alle Schichten darüber auswirken. Solch technische Strukturen sind demnach nicht wirklich eigenständig.

Abb. 1: Schichtenarchitektur

Die Alternative zu technischen Strukturen sind fachliche bzw. vertikale Strukturen (siehe Abb. 2). Hier wird besonders die Fachlichkeit betont. Das heißt übrigens nicht, dass man bei näherem Blick in eine dieser fachlichen Strukturen nicht wieder so etwas wie Layer finden wird. Trotzdem: Der Aspekt, der der obersten Abstraktionsebene Form gibt, wird immer etwas stärker betont werden als der sekundäre Aspekt innerhalb dieser primären Struktur.

Abb. 2: Vertikale / Fachliche Architektur

Die Grenzen der fachlichen Zerlegung

Tatsache ist allerdings, dass vertikale Abtrennung in manchen Fällen etwas besser funktionieren wird, und in anderen Fällen wiederum nicht so gut. Die Anforderungen eignen sich nämlich nicht immer in gleichem Maß für eine solche Aufteilung. Manchmal sind bereits die Business-Regeln, die der Fachexperte formuliert, so dermaßen miteinander vernetzt, dass eine effiziente Zerlegung schwierig wird. Werfen Sie dazu bitte einen Blick auf die Architektur, die in Abb. 3 dargestellt ist. Es handelt sich dabei um einen Web-Shop, welcher nach allen aktuellen Regeln der Kunst in fachliche Microservices zerlegt wurde, während die User-Interfaces nach wie vor als monolithische Layer implementiert sind.

Zunächst ist bemerkenswert, dass man ein solch monolithische Design des UI in Microservice-Architekturen häufig antrifft. Hier möchte ich kurz innehalten und die Frage stellen, warum dem eigentlich so ist. Warum klappt hier die fachliche Zerlegung oft nicht so gut wie im Backend? Die Ursache ist bei einer nicht-funktionalen (oder qualitativen) Anforderung zu finden. Wenn ich dem User ein durchgängiges Erleben des UIs ermöglichen möchte, so ist ein vertikaler Architekturstil an dieser Stelle nicht besonders gut geeignet. Daran sieht man, dass qualitative Anforderungen ebenfalls Einfluss auf die optimal mögliche Zerlegung eines Systems haben können.

Im Backend wiederum scheint die vertikale Modularisierung prima geklappt zu haben. Im Service “Kundenkonto” werden die Daten gespeichert, die ich vom Kunden benötige um ihn in der Plattform zu registrieren. Dabei wird die Validität seiner Angaben entweder über eine EMail oder eine SMS geprüft. Aus diesem Grund haben wir das Thema “Kommunikation” hier mit rein gepackt. Weiters gibt es Services für die “Bestellung”, das “Lager”, die “Bezahlung” und die “Lieferung”. Querschnittliche fachliche Themen wie die Daten des Kunden (Zahlungsweise, Lieferadresse, Rechnungsadresse etc.) haben wir ebenfalls auf diese Services aufgeteilt wie das Produkt an sich (Preis, Lagerstand, Beschreibung etc.).

Das Ziel war Logik und Daten jeweils nahe beieinander zu haben um eine Architektur mit hoher Kohäsion und niedriger Kopplung zu erreichen. Dies ist hier auch ganz gut gelungen, wie ich finde. Würde mir das jemand als Architektur zum Review vorlegen mit der Fragestellubg, ob dies gängigen Best-Practices folgt, hätte ich zumindest auf den ersten Blick nichts zu beanstanden.

Abb. 3: Die einwandfreie Microservice-Architektur unseres Web-Shops

Eine neue Anforderung

Ab sofort soll es in gewissen Fällen möglich sein, frische Schnittblumen bei jeder Bestellung mit auszuliefern. Dies kann entweder über Bezahlung möglich sein, oder ab jeweils einem Umsatz von 1000 Euro einmalig als Gratis-Angebot. Dies ist allerdings nur möglich bei Lieferdiensten, die dabei auch mitmachen. Diese müssen nämlich die Blumen immer frisch bei örtlichen Blumenhändlern abholen. Außerdem ist dies nur möglich, wenn es in der jeweiligen Gegend auch einen Blumenhändler gibt, der mit uns kooperiert. Angeboten wird dies außerdem nur weiblichen Kunden. Für männliche Kunden wird überlegt evtl. später ein After-Shave als Bonus den Paketen beizulegen. Allerdings nur solange auch welche auf Lager sind. In welchem der Bausteine würden Sie diese Änderung verorten? Es gäbe dafür diverse Strategien:

  • Man schreibt einen neuen Service “Geschenke”, der genau dieses Feature abbildet.
  • Man gibt das neue Feature irgendwo dazu, z.B. bei Payment, Bestellung oder Lieferung.
  • Man extrahiert aus den bestehenden Services einen neuen namens “Produkt”, der Daten und Logik der Produkte beinhaltet und dabei die Geschenke als Sonderfall behandelt.

Jede dieser Lösungen führt dazu, dass sich die Kopplung zwischen den Services im Vergleich zur Situation davor vergrößert. Dies allerdings nicht, weil keine gute Arbeit geleistet wurde, sondern weil dies die Anforderungen selbst nötig machten.

Fazit

Es gibt nicht nur qualitative Anforderungen, die eigentlich immer querschnittlicher Natur sind. Es gibt ebenfalls funktionale Anforderungen, welche auf gewisse Weise übergreifend sind. Sie vernetzen bestehende Anforderungen und sind nicht so gut als Modul isolierbar. Sie führen dazu, dass sich die Software in Summe nicht so gut modularisieren lässt. Die Aufgabe ist, eine Struktur zu finden, welche möglichst gut zu den fachlichen Anforderungen passt. Außerdem haben Sie idealerweise noch die Möglichkeit, auf Änderungen der Fachlichkeit zu reagieren, indem Sie im Zuge eines Refactorings eine Restrukturierung vornehmen. Abhängig von der Komplexität der Anforderungen selbst wird Ihnen das manchmal besser gelingen, und manchmal sind dem gewisse Grenzen gesetzt. Wenn Ihnen das gelungen ist, geht es weiter mit dem nächsten der 5 C´s, nämlich: “CONCEAL: Verbergen”.

Leave a Reply