Übung »Wiederholung ST2«
Diese Übung enthält in einem kompakten Beispiel den gesamten Stoff von ST2.
- Dauer
- Ca. 480 min
Worum geht es?
In dieser Übung wiederholen Sie anhand eines knappen, kompakten Beispiels den gesamten Stoff von ST2. Die
Beispiel-Anwendung finden Sie in dem Repo
https://git.archi-lab.io/public-repos/st2-recap/st2-recap-assignment.
Bitte clonen Sie das. Das Repo hat auch ein paar einfache Unit-Tests (in der Klasse AllTests
). Bitte denken Sie
daran, auch diese Klasse mit zu pflegen, wenn Sie den Code ändern.
Ihre Aufgabe
Es ist ein kleines Beispiel (4 Klassen, 200 Zeilen Code insgesamt). Es geht um folgendes:
- Wir betrachten das Payment-Modul eines Webshops. Der Shop operiert in Deutschland, NL, Belgien, Österreich,
Dänemark und Schweden. Er unterstützt also die Währungen EUR, DKR und SEK.
- Es gibt Kunden, die Rechnungen (Invoice) bekommen, für bestellte Waren. Kunden haben nur Vor- und Nachname sowie
eine Email. Es gibt noch Firmenkunden, die nur einen Namen und eine Email haben.
- Umtausch auf Kulanz wird durch die Ausgabe von Gutscheinen (Voucher) geregelt. Diese sind personalisiert. Man kann
Bestellungen mit solchen Vouchers bezahlen. Man kann die Vouchers auch mehrmals einsetzen, so lange, bis der
Geldbetrag darauf verbraucht ist.
Sie sollen folgendes tun:
- Wenden Sie Clean-Code-Regeln und die SOLID-Prinzipien an, und machen Sie ein entsprechendes Refactoring.
- Implementieren Sie Domain Primitives.
- Identifizieren Sie Entities, Value Objects und Aggregates.
- Implementieren Sie dies mit JPA.
- Spezifizieren Sie ein REST-API.
- Implementieren Sie das REST-API.
Die nachfolgenden Aufgaben leiten Sie durch den Prozess.
Lösungen
Für die Zwischenschritte gibt es in den meisten Fällen
Beispiellösungen. Die Lösungen finden Sie alle im Repo
https://git.archi-lab.io/public-repos/st2-recap/st2-recap-solution.
Unten sind Deep Links auf dieses Repo, die Sie direkt zu den Lösungen führen.
E1) Clean Code & SOLID - Analyse der Probleme
In den Code sind absichtlich Verstöße gegen Clean-Code-Regeln und SOLID-Prinzipien eingebaut, und zwar (mindestens …)
die folgenden:
- Clean Code
- Meaningful names
- Keep your methods small
- Proper error handling
- SOLID
- Single Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
Nehmen Sie sich ein bisschen Zeit, um den Code zu verstehen und zu analysieren. Wenn Ihnen die obigen Regeln und
Prinzipien nichts mehr sagen, dann schauen Sie nochmal in die Videos oder die zugehörigen Scripte. Dann annotieren
Sie bitte alle Verstöße, die Sie finden, mit TODO
im Code, und schreiben Sie dazu, was Sie refactorn würden.
Beispiel 1:
// TODO CC - meaningful names
// "dob" ist kein sprechender Name, sondern eine unverständliche Abkürzung.
// Ich würde das Attribut zu "dateOfBirth" umbenennen.
Beispiel 2:
// TODO SOLID - open-closed-principle
// Das Attribut "isValid" ist zwar private, aber es hat einen Setter. Damit kann man die
// Gültigkeit des Objektzustands einfach so von außen setzen! Das sollte nicht sein.
// Ich würde das Attribut ganz entfernen, natürlich auch den Setter, und die "Gültigkeit"
// dann aus den anderen Objekt-Attributen berechnen.
Insgesamt kommen (wie schon oben gelistet) mindestens die folgenden Verstöße vor - jedes dieser Todos sollten
Sie also mindestens einmal verwenden.
// TODO CC - meaningful names
// TODO CC - keep your methods small
// TODO CC - proper error handling
// TODO SOLID - single-responsibility-principle
// TODO SOLID - open-closed-principle
// TODO SOLID - liskov-substitution-principle
Tipps
- Obwohl der Code kurz ist, liegt doch viel im Argen :-(. Am einfachsten ist es, die verschiedenen Verstöße
nacheinander abzuarbeiten. Also erst alle Verstöße gegen “Meaningful Names”, dann die gegen “Keep your methods
small”, etc.
- Manchmal verstoßen Codestellen auch gegen mehrere Regeln/Prinzipien auf einmal!
- Die Beispiel-Lösung kommt auf insgesamt 43 Todos aus Verstößen gegen Clean Code und SOLID. Die Zahl ist nicht
in Stein gemeißelt - hängt ja ein bisschen davon ab, was man wie oft wo anmerkt. Aber vielleicht hilft das als
Orientierung.
- Wichtig: Refactorn Sie noch nichts! Das kommt in den nächsten Schritten.
Lösung
Eine Beispiel-Lösung finden Sie im Lösungs-Repo in Folder E1.
Das Repo ist (möglicherweise) noch nicht sofort sichtbar, sondern wird erst im Laufe der Übung sichtbar geschaltet.
E2) Refactoring der Verstöße gegen “Meaningful Names”
Machen Sie jetzt als erstes einmal ein Refactoring der Benennung von Variablen und ggfs. Klassen, damit der Code ein
bisschen übersichtlicher wird. Am besten löschen Sie dann jeweils das Todo aus Ihrem Code. Tipp:
Wenn Sie IntelliJ verwenden, dann gibt es links in der Fußzeile den Button “TODO”, der alle Todos im gesamten Code
listet. Damit können Sie die Todos prima filtern.
Lösung
Eine Beispiel-Lösung finden Sie im Lösungs-Repo in Folder E2.
E3) Domain Primitives
Als nächsten Schritt im Refactoring empfehle ich die Domain Primitives. Mit diesen Domain Primitives löst man
nämlich auch eine ganze Anzahl von SOLID-Verstößen (“Single Responsibility” und “Open-Closed Principle”) auf.
Man kann im Code mindestens drei Domain Primitives finden. Implementieren Sie diese und sorgen Sie für ein
entsprechendes Refactoring im Code.
Tipps
- Lassen Sie die restlichen CC- und SOLID-Verstöße (die nicht über Domain Primitives zu lösen sind) erst einmal in
Ruhe - es ist nicht gut, wenn man zu viel auf einmal ändert.
- Die eine Ausnahme hiervon ist
Invoice.payPerVoucher(...)
- diese Methode sollten Sie unbedingt direkt mit
refactorn, weil man sonst das Potential der DPs nicht ausnutzt. Also: Verschieben Sie direkt auch Funktionalität
hin zu Voucher
, um dort einen Betrag abzubuchen.
- Achten Sie darauf, dass die Tests grün bleiben! Sie müssen mit den DPs auch in
AllTests
einiges ändern.
(Dass bestehende Unittests grün bleiben, ist im Übrigen ja genau die “Essenz” eines professionellen, also gut
abgesicherten Refactorings.)
- Nach der Einführung der DPs bleiben in der Beispiellösung noch 12 Todos übrig - nur so als Orientierung.
Lösung
Eine Beispiel-Lösung finden Sie im Lösungs-Repo in Folder E3.
E4) Auflösen der verbleibenden Clean-Code- und SOLID-Verstöße durch geeignetes Refactoring
Bringen Sie jetzt das Refactoring zu Ende, indem Sie die verbleibenden Verstöße im Code beheben.
Lösung
Eine Beispiel-Lösung finden Sie im Lösungs-Repo in Folder E4.
E5) Identifizieren Sie Aggregates, legen Sie Package-Strukturen an, und persistieren Sie alles mit JPA
- Erstellen Sie als Erstes ein logisches Datenmodell, in dem Sie Entities und Domain Primitives (als Value Objects)
erfassen. Attribute brauchen nicht dargestellt zu werden, Domain Primitives / VOs sollten als eigene Klassen
abgebildet werden.
- Ordnen Sie alles zu Aggregates an, indem Sie im Datenmodell die Aggregates umkringeln. Wie viele Aggregates
gibts es?
- Legen Sie gemäß der Konvention, die wir im Praktikum verwendet haben, Packages entlang der Aggregate-Grenzen
an. Ergänzen Sie Klassen, die Sie für eine Persistierung mit JPA brauchen, und machen Sie an den richtigen Stellen
die JPA-Annotationen für Entities und Value Objects.
- Legen Sie für jedes Aggregate auch schon mal einen Application Service an, auch wenn der vielleicht noch
leer ist. Schauen Sie, dass der Application Service im richtigen Sub-Package landet.
- Stellen Sie die Testklasse so um, dass die Objekte auch wirklich persistiert werden.
Lösung
Schritt 1 und 2 sieht man in diesem logischen Datenmodell:
E6) Spezifizieren und implementieren Sie REST-Endpoints
- Spezifizieren Sie die in der nachfolgenden Tabelle genannten REST-Endpoints.
- Implementieren Sie die REST-Endpoints. Achten Sie darauf, dass Sie DTOs bereitstellen und die Geschäftslogik
nicht in den Controllern anlegen, sondern in ApplicationServices und in Domänenklassen.
- Schreiben Sie je einen Unittest für jeden REST-Endpoint. Orientieren Sie sich dafür an den Testklassen aus
dem REST-Meilenstein M4 des Praktikums.
Aufgabe |
Verb |
Endpoint |
Lege Kunde neu an |
|
|
Hole Liste aller Kunden |
|
|
Zeige einen Kunden an |
|
|
Ändere die Email eines Kunden |
|
|
Lösche einen Kunden |
|
|
Finde alle Kunden mit einem bestimmten Nachname |
|
|
Lege eine Rechnung neu an |
|
|
Zeige eine bestimmte Rechnung an |
|
|
Suche alle Rechnungen für einen Kunden |
|
|
Lösche eine Rechnung |
|
|
Lege einen Voucher neu an |
|
|
Finde Vouchers, die für einen bestimmten Zweck eingelöst wurden |
|
|
Finde alle Vouchers einer Währung und >0 Geld drauf für einen Kunden |
|
|
Lösung
Hier sind die Lösungen jetzt mit in der Tabelle.
Aufgabe |
Verb |
Endpoint |
Lege Kunde neu an |
POST |
/customers |
Hole Liste aller Kunden |
GET |
/customers |
Zeige einen Kunden an |
GET |
/customers/{c-id} |
Ändere die Email eines Kunden |
PATCH |
/customers/{c-id} |
Lösche einen Kunden |
DELETE |
/customers/{c-id} |
Finde alle Kunden mit einem bestimmten Nachname |
GET |
/customers?lastName={lastNameString} |
Lege eine Rechnung neu an |
POST |
/invoices |
Zeige eine bestimmte Rechnung an |
GET |
/invoices/{i-id} |
Suche alle Rechnungen für einen Kunden |
GET |
/invoices?customerId={c-id} |
Lösche eine Rechnung |
DELETE |
/invoices/{i-id} |
Lege einen Voucher neu an |
POST |
/vouchers |
Finde Vouchers, die für einen bestimmten Zweck eingelöst wurden |
GET |
/vouchers?purpose={purposeString} |
Finde alle Vouchers einer Währung und >0 Geld drauf für einen Kunden |
GET |
/vouchers?customerId={c-id}¤cy={currencyString}&amountIsGreaterThan=0 |
Das ist aus Zeitgründen im Lösungsrepo noch nicht implementiert (vielleicht später mal).