Modul 01 · Migration Deep Dive · Stufe 1 Fundament

Dependency Injection

Service Locator / manuelle Verdrahtung wird nachvollziehbar in Spring Constructor Injection überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.

Service LocatorPortConstructor Injection
4 → 4Produktionsdateien
49 → 48Java-Zeilen
1 → 1Testdateien
1 / 2Dependencies entfernt / neu
NIEDRIGRisiko · Score 1

Was sich konkret ändert

DimensionLegacyModernMigrationskonsequenz
ProgrammiermodellOverrideComponent, Override, Service, SpringBootApplicationAnnotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen.
Abhängigkeiten1 direkte Dependencies2 direkte Dependencies2 neu, 1 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen.
Öffentliche API4 erkannte Methoden3 erkannte MethodenMethoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag.
Datenmodellclass DefaultGreetingService, class DiLegacyApplication, class ServiceRegistry, interface GreetingServiceclass DiModernApplication, class GermanGreetingService, class GreetingRunner, interface GreetingPortFeldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet.
FehlerverhaltenLegacy-Exceptions und Rückgabewerteexplizitere Fach-/Framework-FehlerabbildungFehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden.
TestsBestands- und Golden-Master-TestsUnit-, Slice-, Contract- und IntegrationstestsDer Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt.
BetriebLegacy-Start/Lifecyclemodernes Packaging, Health und externe KonfigurationStartup, Lifecycle, Security Context und Remote-/Containerfehler separat testen.
RollbackLegacy-Artefakt bleibt unverändertModern-Artefakt getrennt deploybarKein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein.

Fachlicher Vertrag: unverändert zu erhalten

  • Die Grußfunktion liefert für denselben Namen denselben fachlichen Text.
  • Der fachliche Use Case kennt weder Spring noch einen globalen Registry-Zugriff.
  • Fehlende Implementierungen werden bereits beim Start beziehungsweise Konstruktoraufruf sichtbar.

Hauptrisiko und Testfokus

hidden dependencies, global state

constructor-level unit tests and context smoke test

Die Modernisierung gilt erst als abgeschlossen, wenn der fachliche Vertrag automatisiert belegt ist.

Verbindliches Code- und Rollenmapping

Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.

Legacy-Rolle / DateiModern-Rolle / DateiBedeutung
ServiceRegistry.javaSpring Context / Constructor InjectionVersteckte globale Auflösung wird durch explizite Verdrahtung ersetzt.
GreetingService.javaGreetingPort.javaFachlicher Servicevertrag wird als Port stabilisiert.
DefaultGreetingService.javaGermanGreetingService.javaKonkrete Implementierung bleibt fachlich gleich.
DiLegacyApplication.javaDiModernApplication.java + GreetingRunner.javaBootstrap und Use-Case-Ausführung werden getrennt.

Modern-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
DiModernApplication.javaclass DiModernApplicationSpringBootApplicationvoid main(String[] args)11
GermanGreetingService.javaclass GermanGreetingServiceOverride, ServiceString greet(String name)11
GreetingPort.javainterface GreetingPortPort (Hexagonal Architecture)7
GreetingRunner.javaclass GreetingRunnerComponent, Overridevoid run(String ... args)Constructor Injection19

Dependency- und Laufzeitmigration

StatusDependencyVersionScopePrüfung
ENTFERNTorg.junit.jupiter:junit-jupiterBOM/ParenttestLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
NEUorg.springframework.boot:spring-boot-starterBOM/ParentcompileNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
NEUorg.springframework.boot:spring-boot-starter-testBOM/ParenttestNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
Direkte POM-Daten ersetzen keinen erfolgreichen Online-Build mit transitiver SBOM- und CVE-Prüfung.

Vorher-/Nachher-Codebelege

Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.

Legacy
ServiceRegistry.java
Modern
Spring Context / Constructor Injection

Migrationsbedeutung: Versteckte globale Auflösung wird durch explizite Verdrahtung ersetzt.

Legacy-Code

projects/01-dependency-injection/legacy/src/main/java/at/aydin/lab/di/legacy/ServiceRegistry.java

package at.aydin.lab.di.legacy;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

// Design Pattern: Service Locator
// Zweck: Zentrale, manuelle Auflösung von Abhängigkeiten in Legacy-Anwendungen.
public final class ServiceRegistry {
    private static final Map<Class<?>, Object> SERVICES = new ConcurrentHashMap<>();
    private ServiceRegistry() {
    }

    public static <T> void register(Class<T> type, T implementation) {
        SERVICES.put(type, implementation);
    }

    public static <T> T resolve(Class<T> type) {
        Object service = SERVICES.get(type);
        if (service == null) {
            throw new IllegalStateException("Kein Service registriert: " + type.getName());
        }
        return type.cast(service);
    }
}

Modern-Code

projects/01-dependency-injection/modern/src/main/java/at/aydin/lab/di/modern/DiModernApplication.java

package at.aydin.lab.di.modern;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DiModernApplication {
    public static void main(String[] args) {
        SpringApplication.run(DiModernApplication.class, args);
    }
}
Technischen Unified Diff öffnen
--- ServiceRegistry.java
+++ DiModernApplication.java
@@ -1,24 +1,11 @@
-package at.aydin.lab.di.legacy;
+package at.aydin.lab.di.modern;
 
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 
-// Design Pattern: Service Locator
-// Zweck: Zentrale, manuelle Auflösung von Abhängigkeiten in Legacy-Anwendungen.
-public final class ServiceRegistry {
-    private static final Map<Class<?>, Object> SERVICES = new ConcurrentHashMap<>();
-    private ServiceRegistry() {
-    }
-
-    public static <T> void register(Class<T> type, T implementation) {
-        SERVICES.put(type, implementation);
-    }
-
-    public static <T> T resolve(Class<T> type) {
-        Object service = SERVICES.get(type);
-        if (service == null) {
-            throw new IllegalStateException("Kein Service registriert: " + type.getName());
-        }
-        return type.cast(service);
+@SpringBootApplication
+public class DiModernApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(DiModernApplication.class, args);
     }
 }
Legacy
GreetingService.java
Modern
GreetingPort.java

Migrationsbedeutung: Fachlicher Servicevertrag wird als Port stabilisiert.

Legacy-Code

projects/01-dependency-injection/legacy/src/main/java/at/aydin/lab/di/legacy/GreetingService.java

package at.aydin.lab.di.legacy;

public interface GreetingService {
    String greet(String name);
}

Modern-Code

projects/01-dependency-injection/modern/src/main/java/at/aydin/lab/di/modern/GreetingPort.java

package at.aydin.lab.di.modern;

// Design Pattern: Port (Hexagonal Architecture)
// Zweck: Die Anwendung hängt von einem fachlichen Vertrag statt einer konkreten Implementierung ab.
public interface GreetingPort {
    String greet(String name);
}
Technischen Unified Diff öffnen
--- GreetingService.java
+++ GreetingPort.java
@@ -1,5 +1,7 @@
-package at.aydin.lab.di.legacy;
+package at.aydin.lab.di.modern;
 
-public interface GreetingService {
+// Design Pattern: Port (Hexagonal Architecture)
+// Zweck: Die Anwendung hängt von einem fachlichen Vertrag statt einer konkreten Implementierung ab.
+public interface GreetingPort {
     String greet(String name);
 }
Legacy
DefaultGreetingService.java
Modern
GermanGreetingService.java

Migrationsbedeutung: Konkrete Implementierung bleibt fachlich gleich.

Legacy-Code

projects/01-dependency-injection/legacy/src/main/java/at/aydin/lab/di/legacy/DefaultGreetingService.java

package at.aydin.lab.di.legacy;

public final class DefaultGreetingService implements GreetingService {
    @Override
    public String greet(String name) {
        return "Willkommen, " + name + "!";
    }
}

Modern-Code

projects/01-dependency-injection/modern/src/main/java/at/aydin/lab/di/modern/GermanGreetingService.java

package at.aydin.lab.di.modern;

import org.springframework.stereotype.Service;

@Service
public class GermanGreetingService implements GreetingPort {
    @Override
    public String greet(String name) {
        return "Willkommen, " + name + "!";
    }
}
Technischen Unified Diff öffnen
--- DefaultGreetingService.java
+++ GermanGreetingService.java
@@ -1,6 +1,9 @@
-package at.aydin.lab.di.legacy;
+package at.aydin.lab.di.modern;
 
-public final class DefaultGreetingService implements GreetingService {
+import org.springframework.stereotype.Service;
+
+@Service
+public class GermanGreetingService implements GreetingPort {
     @Override
     public String greet(String name) {
         return "Willkommen, " + name + "!";
Legacy
DiLegacyApplication.java
Modern
DiModernApplication.java + GreetingRunner.java

Migrationsbedeutung: Bootstrap und Use-Case-Ausführung werden getrennt.

Legacy-Code

projects/01-dependency-injection/legacy/src/main/java/at/aydin/lab/di/legacy/DiLegacyApplication.java

package at.aydin.lab.di.legacy;

public final class DiLegacyApplication {
    private DiLegacyApplication() {
    }

    public static void main(String[] args) {
        ServiceRegistry.register(GreetingService.class, new DefaultGreetingService());
        GreetingService service = ServiceRegistry.resolve(GreetingService.class);
        System.out.println(service.greet("Aydin"));
    }
}

Modern-Code

projects/01-dependency-injection/modern/src/main/java/at/aydin/lab/di/modern/DiModernApplication.java

package at.aydin.lab.di.modern;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DiModernApplication {
    public static void main(String[] args) {
        SpringApplication.run(DiModernApplication.class, args);
    }
}

Modern-Code

projects/01-dependency-injection/modern/src/main/java/at/aydin/lab/di/modern/GreetingRunner.java

package at.aydin.lab.di.modern;

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class GreetingRunner implements CommandLineRunner {
    private final GreetingPort greetingPort;
    // Design Pattern: Constructor Injection
    // Zweck: Abhängigkeiten sind sichtbar, unveränderlich und leicht testbar.
    public GreetingRunner(GreetingPort greetingPort) {
        this.greetingPort = greetingPort;
    }

    @Override
    public void run(String ... args) {
        System.out.println(greetingPort.greet("Aydin"));
    }
}

Umsetzungsplan mit Qualitäts-Gates

  1. Arbeitspaket 1
    GreetingService als fachlichen Port festlegen und Aufrufer dagegen testen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  2. Arbeitspaket 2
    Direkte Zugriffe auf ServiceRegistry erfassen und durch Konstruktorparameter ersetzen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  3. Arbeitspaket 3
    DefaultGreetingService funktional in GermanGreetingService überführen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  4. Arbeitspaket 4
    GreetingRunner als Application Adapter einführen; keine Container-API in den Port aufnehmen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  5. Arbeitspaket 5
    Spring-Komponenten nur am äußeren Rand annotieren und Context-Smoke-Test ergänzen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  6. Arbeitspaket 6
    Globalen Registry-Zustand entfernen und Parallelbetrieb über Adapter ermöglichen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.

Konkreter Test- und Abnahmekatalog

IDEbenePrüfungerforderlicher Nachweis
01-DEPENDENCY-INJECTION-A01Integration/ContractKein Produktionscode ruft ServiceRegistry auf.Automatisierter Test und CI-Protokoll
01-DEPENDENCY-INJECTION-A02Integration/ContractGreetingPort lässt sich ohne Spring-Kontext unit-testen.Automatisierter Test und CI-Protokoll
01-DEPENDENCY-INJECTION-A03Integration/ContractAnwendung startet nur mit genau einer passenden Implementierung.Automatisierter Test und CI-Protokoll
01-DEPENDENCY-INJECTION-A04Integration/ContractLegacy- und Modern-Ausgabe sind für definierte Testfälle identisch.Automatisierter Test und CI-Protokoll
01-DEPENDENCY-INJECTION-A05Integration/ContractKein statischer veränderlicher Servicezustand bleibt zurück.Automatisierter Test und CI-Protokoll
01-DEPENDENCY-INJECTION-T01UnitFachlogik ohne Container oder externen Dienst testen.Unit-Test
01-DEPENDENCY-INJECTION-T02RegressionLegacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen.Vergleichsreport
01-DEPENDENCY-INJECTION-T03NegativeFehlerhafte, leere und grenzwertige Eingaben prüfen.Negativtest
01-DEPENDENCY-INJECTION-T04OperationsStart, Health, Shutdown und Konfigurationsfehler prüfen.Deployment-/Startprotokoll
01-DEPENDENCY-INJECTION-T05LifecycleContainer-/Service-Lifecycle, Startup-Fehler und Recovery prüfen.Runtime-Test
01-DEPENDENCY-INJECTION-F01Fokusconstructor-level unit tests and context smoke testModulspezifischer Testreport

Risikoregister des Moduls

RisikoAuswirkungGegenmaßnahmeGate
hidden dependencies, global stateNIEDRIGconstructor-level unit tests and context smoke testvor Cutover
Neue Framework-/Library-LaufzeitabhängigkeitenMITTELSBOM, Lizenz-, CVE- und transitive Dependency-Prüfung im Online-Buildvor Release
Legacy-Verhalten war implizit an entfernte Library gekoppeltMITTELContract-/Golden-Master-Tests und gezielte Fehlerpfadevor Abschaltung

Konfiguration und Ressourcen

Keine zusätzlichen Ressourcen.

Rollback und Koexistenz

Legacy-Aufrufer können vorübergehend über einen Adapter weiterhin die Registry nutzen; der neue Port bleibt dabei die einzige fachliche Schnittstelle.

Abbruchkriterien

  • Fachlicher Golden-Master weicht ab.
  • Daten-, Nachrichten- oder API-Kompatibilität ist ungeklärt.
  • Fehlerquote, Latenz oder Ressourcenverbrauch überschreiten das vereinbarte Limit.
  • Monitoring oder Rückfallpfad ist nicht funktionsfähig.

Definition of Done

Direkte Arbeitslinks

⌂ Cockpit