Fachlicher Vertrag: unverändert zu erhalten
- Ergebnis, Reihenfolge, Rundung und Null-Semantik bleiben identisch.
- Streams erzeugen keine versteckten Seiteneffekte.
- Parallelisierung wird nur nach Messung aktiviert.
imperative Schleifen und anonyme Klassen wird nachvollziehbar in Stream Pipeline mit Records und Collectors überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.
| Dimension | Legacy | Modern | Migrationskonsequenz |
|---|---|---|---|
| Programmiermodell | imperativ / ohne Framework-Annotationen | explizite Java-Abstraktionen | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 1 direkte Dependencies | 1 direkte Dependencies | 0 neu, 0 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 1 erkannte Methoden | 2 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class LegacyOrderAnalytics, record Order | class OrderAnalytics, record Order | Feldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet. |
| Fehlerverhalten | Legacy-Exceptions und Rückgabewerte | explizitere Fach-/Framework-Fehlerabbildung | Fehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden. |
| Tests | Bestands- und Golden-Master-Tests | Unit-, Slice-, Contract- und Integrationstests | Der Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt. |
| Betrieb | Legacy-Start/Lifecycle | modernes Packaging, Health und externe Konfiguration | Umstellung über kleine Adapter und Golden-Master-Tests statt Big Bang. |
| Rollback | Legacy-Artefakt bleibt unverändert | Modern-Artefakt getrennt deploybar | Kein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein. |
ordering, short-circuit and null semantics
empty input, duplicates, precision and parallel-safety tests
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| LegacyOrderAnalytics.java | OrderAnalytics.java | Schleifen werden zu lesbarer Stream-Pipeline. |
| Order.java | Order.java | Fachliches Eingabeobjekt bleibt stabil. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| LegacyOrderAnalytics.java | class LegacyOrderAnalytics | — | List<String> paidCustomersOver(List<Order> orders, double limit) | Accumulator | 22 |
| Order.java | record Order | — | — | — | 4 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| Order.java | record Order | — | — | — | 4 |
| OrderAnalytics.java | class OrderAnalytics | — | List<String> paidCustomersOver(List<Order> orders, double limit) double totalRevenue(List<Order> orders) | Pipeline | 16 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| BEIBEHALTEN | org.junit.jupiter:junit-jupiter | BOM/Parent → BOM/Parent | test → test | Gemeinsame Dependency; Version und Scope im effektiven POM prüfen. |
Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.
Migrationsbedeutung: Schleifen werden zu lesbarer Stream-Pipeline.
package at.aydin.lab.streams.legacy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
// Design Pattern: Accumulator
// Zweck: Sammelt Ergebnisse imperativ in einer veränderlichen Collection.
public final class LegacyOrderAnalytics {
public List<String> paidCustomersOver(List<Order> orders, double limit) {
List<String> result = new ArrayList<>();
for (Order order : orders) {
if (order.paid()
&& order.total() > limit
&& !result.contains(order.customer())) {
result.add(order.customer());
}
}
Collections.sort(result);
return result;
}
}
projects/15-lambdas-streams/modern/src/main/java/at/aydin/lab/streams/modern/OrderAnalytics.java
package at.aydin.lab.streams.modern;
import java.util.*;
// Design Pattern: Pipeline
// Zweck: Filter-, Mapping- und Reduktionsschritte werden deklarativ zusammengesetzt.
public final class OrderAnalytics {
public List<String> paidCustomersOver(List<Order> orders, double limit) {
return orders.stream().filter(Order::paid).filter(o -> o.total() > limit).map(Order::customer).distinct()
.sorted().toList();
}
public double totalRevenue(List<Order> orders) {
return orders.stream().filter(Order::paid).mapToDouble(Order::total).sum();
}
}
--- LegacyOrderAnalytics.java
+++ OrderAnalytics.java
@@ -1,22 +1,16 @@
-package at.aydin.lab.streams.legacy;
+package at.aydin.lab.streams.modern;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
-// Design Pattern: Accumulator
-// Zweck: Sammelt Ergebnisse imperativ in einer veränderlichen Collection.
-public final class LegacyOrderAnalytics {
+// Design Pattern: Pipeline
+// Zweck: Filter-, Mapping- und Reduktionsschritte werden deklarativ zusammengesetzt.
+public final class OrderAnalytics {
public List<String> paidCustomersOver(List<Order> orders, double limit) {
- List<String> result = new ArrayList<>();
- for (Order order : orders) {
- if (order.paid()
- && order.total() > limit
- && !result.contains(order.customer())) {
- result.add(order.customer());
- }
- }
- Collections.sort(result);
- return result;
+ return orders.stream().filter(Order::paid).filter(o -> o.total() > limit).map(Order::customer).distinct()
+ .sorted().toList();
+ }
+
+ public double totalRevenue(List<Order> orders) {
+ return orders.stream().filter(Order::paid).mapToDouble(Order::total).sum();
}
}
Migrationsbedeutung: Fachliches Eingabeobjekt bleibt stabil.
projects/15-lambdas-streams/legacy/src/main/java/at/aydin/lab/streams/legacy/Order.java
package at.aydin.lab.streams.legacy;
public record Order(long id, String customer, double total, boolean paid) {
}
projects/15-lambdas-streams/modern/src/main/java/at/aydin/lab/streams/modern/Order.java
package at.aydin.lab.streams.modern;
public record Order(long id, String customer, double total, boolean paid) {
}
--- Order.java
+++ Order.java
@@ -1,4 +1,4 @@
-package at.aydin.lab.streams.legacy;
+package at.aydin.lab.streams.modern;
public record Order(long id, String customer, double total, boolean paid) {
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 15-LAMBDAS-STREAMS-A01 | Integration/Contract | Golden-Master-Ergebnisse sind identisch. | Automatisierter Test und CI-Protokoll |
| 15-LAMBDAS-STREAMS-A02 | Integration/Contract | Encounter Order ist dokumentiert und getestet. | Automatisierter Test und CI-Protokoll |
| 15-LAMBDAS-STREAMS-A03 | Integration/Contract | Keine Mutation externer Collections. | Automatisierter Test und CI-Protokoll |
| 15-LAMBDAS-STREAMS-A04 | Integration/Contract | Leere Eingaben liefern definiertes Ergebnis. | Automatisierter Test und CI-Protokoll |
| 15-LAMBDAS-STREAMS-A05 | Integration/Contract | Parallelstream wird nicht unbewusst verwendet. | Automatisierter Test und CI-Protokoll |
| 15-LAMBDAS-STREAMS-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 15-LAMBDAS-STREAMS-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 15-LAMBDAS-STREAMS-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 15-LAMBDAS-STREAMS-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 15-LAMBDAS-STREAMS-F01 | Fokus | empty input, duplicates, precision and parallel-safety tests | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| ordering, short-circuit and null semantics | NIEDRIG | empty input, duplicates, precision and parallel-safety tests | vor Cutover |
Keine zusätzlichen Ressourcen.
Beide Analytics-Implementierungen können hinter derselben Schnittstelle per Feature Flag verglichen werden.