0Pricing
PHP Academy · Lektion

Dependency Inversion in der Praxis

Verbinden Sie Adapter mithilfe der Dependency Inversion mit dem Kern.

Dependency Inversion in der Praxis ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Inversion und Injection

Oft werden zwei unterschiedliche Konzepte miteinander verwechselt. Dependency Injection ist eine Technik: Sie übergeben Mitarbeiter, anstatt sie zu erzeugen. Dependency Inversion (das D in SOLID) ist ein Prinzip: Sowohl die übergeordnete Fachlogik als auch technische Details hängen von einer Abstraktion ab, und diese Abstraktion gehört dem übergeordneten Modul. In dieser Lektion geht es darum, das zweite Konzept umzusetzen — konkrete Adapter mit einem Kern zu verdrahten, der die Schnittstellen definiert.

Das Prinzip präzise formuliert

DIP besagt:

  • Übergeordnete Module sollten nicht von untergeordneten Modulen abhängen. Beide sollten von Abstraktionen abhängen.
  • Abstraktionen sollten nicht von Details abhängen. Details hängen von Abstraktionen ab.

Der subtile Punkt: Die Schnittstelle gehört dem Consumer, nicht dem Implementierer. Ihre Domäne deklariert PaymentGateway; der Stripe-Adapter richtet sich danach — nicht umgekehrt.

Die Abstraktion im Kern definieren

Platzieren Sie die Schnittstelle neben dem Code, der sie benötigt, und formulieren Sie sie in Begriffen der Domäne. Es dürfen keine Stripe-Typen nach außen dringen.

<?php
// src/Billing/Application/Port/PaymentGateway.php
interface PaymentGateway
{
    public function charge(Money $amount, CardToken $token): ChargeId;
}

final class Money {
    public function __construct(public readonly int $cents, public readonly string $currency) {}
}
final class CardToken { public function __construct(public readonly string $value) {} }
final class ChargeId { public function __construct(public readonly string $value) {} }

Einen Adapter daran ausrichten

Der Infrastrukturadapter implementiert die Schnittstelle des Kerns und übersetzt sie in das SDK des Anbieters. Der Abhängigkeitspfeil zeigt vom Adapter (Detail) zur Abstraktion (Fachlogik) — die Inversion ist erreicht.

<?php
final class StripePaymentGateway implements PaymentGateway
{
    public function __construct(private \Stripe\StripeClient $stripe) {}

    public function charge(Money $amount, CardToken $token): ChargeId {
        $intent = $this->stripe->paymentIntents->create([
            'amount'   => $amount->cents,
            'currency' => strtolower($amount->currency),
            'payment_method' => $token->value,
            'confirm'  => true,
        ]);
        return new ChargeId($intent->id);
    }
}

Konstruktor-Injection als Standard

Injizieren Sie Abhängigkeiten über den Konstruktor und typisieren Sie sie mit der Abstraktion. Dadurch werden Abhängigkeiten explizit und unveränderlich und können nicht vergessen werden. Vermeiden Sie Setter- und Property-Injection für erforderliche Mitarbeiter — damit können halb aufgebaute Objekte entstehen.

<?php
final class CheckoutService
{
    public function __construct(
        private PaymentGateway $payments,   // abstraction, not StripeClient
        private OrderRepository $orders,
    ) {}

    public function pay(OrderId $id, CardToken $token): ChargeId {
        $order  = $this->orders->get($id);
        $charge = $this->payments->charge($order->total(), $token);
        $order->markPaid($charge);
        $this->orders->save($order);
        return $charge;
    }
}

Der Composition Root

Die gesamte konkrete Verdrahtung erfolgt genau an einer Stelle — im Composition Root — und so nah wie möglich an main()/dem Einstiegspunkt. Nirgends sonst wird Infrastruktur selbst instanziiert. Dies ist die einzige Stelle, die von der Existenz von Stripe weiß.

<?php
// public/index.php — composition root
$stripe   = new \Stripe\StripeClient(getenv('STRIPE_SECRET'));
$gateway  = new StripePaymentGateway($stripe);
$orders   = new PdoOrderRepository(new PDO(getenv('DB_DSN')));
$checkout = new CheckoutService($gateway, $orders);

// Everything below depends only on abstractions
$controller = new CheckoutController($checkout);

Verdrahtung mit einem DI-Container

Bei nicht trivialen Anwendungen automatisiert ein Container (PHP-DI, Symfony) die Verdrahtung. Der entscheidende Schritt ist die Zuordnung von Schnittstellen zu Implementierungen. Autowiring löst Konstruktoren anhand ihrer Typen auf; Sie müssen nur die Zuordnung von Schnittstelle zu Klasse deklarieren.

<?php
use function DI\autowire;
use function DI\get;

return [
    PaymentGateway::class => autowire(StripePaymentGateway::class),
    OrderRepository::class => autowire(PdoOrderRepository::class),
    \Stripe\StripeClient::class => fn() => new \Stripe\StripeClient(getenv('STRIPE_SECRET')),
    PDO::class => fn() => new PDO(getenv('DB_DSN')),
];

Das Austauschen von Adaptern beweist es

Da der Kern nur von PaymentGateway abhängt, lässt sich der Anbieter wechseln oder offline testen, indem Sie die Verdrahtung an einer einzigen Stelle ändern. Hier sehen Sie einen Fake für einen Unit-Test — der CheckoutService bleibt unverändert und importiert Stripe niemals.

<?php
final class FakeGateway implements PaymentGateway {
    public array $charges = [];
    public function charge(Money $a, CardToken $t): ChargeId {
        $this->charges[] = $a;
        return new ChargeId('ch_test_' . count($this->charges));
    }
}

$fake = new FakeGateway();
$id = $fake->charge(new Money(2500, 'EUR'), new CardToken('tok_visa'));
echo $id->value, ' charges=', count($fake->charges), PHP_EOL; // ch_test_1 charges=1

Die Service-Locator-Falle vermeiden

Den Container selbst zu injizieren und Abhängigkeiten innerhalb von Methoden abzurufen, ist das Service-Locator-Anti-Pattern. Es verbirgt Abhängigkeiten, verhindert die Typprüfung und koppelt Ihren Code erneut an den Container. Injizieren Sie explizit, was Sie benötigen.

<?php
// ANTI-PATTERN: hidden dependencies, container leaks everywhere
final class BadCheckout {
    public function __construct(private ContainerInterface $c) {}
    public function pay($id, $token) {
        $gateway = $this->c->get(PaymentGateway::class); // hidden!
        // ...
    }
}

Wo der Container auftauchen darf

Der Container ist genau in einer Schicht zulässig: im Composition Root und in der Framework-Anbindung darum herum (z. B. in einer Controller-Factory). Ihre Domänen- und Anwendungsklassen müssen unabhängig vom Container bleiben — sie erhalten einfache Objekte über Konstruktoren und könnten von Hand instanziiert werden. Ein guter Test lautet: Könnten Sie die gesamte Anwendung in einer einzigen PHP-Datei ohne Container verdrahten? Wenn ja, sind Ihre Abhängigkeiten ehrlich.

Lazy Wiring ohne Service Locator

Manchmal ist eine Abhängigkeit aufwendig zu erstellen oder wird nur bedingt benötigt. Widerstehen Sie der Versuchung, den Container zu injizieren — injizieren Sie stattdessen eine Factory-Closure. Die Abhängigkeit bleibt explizit und typisiert, während ihre Erstellung bis zur tatsächlichen Verwendung aufgeschoben wird.

<?php
final class ReportService
{
    /** @param Closure():PaymentGateway $gatewayFactory */
    public function __construct(private Closure $gatewayFactory) {}

    public function refundIfNeeded(bool $needed): void {
        if (!$needed) return;
        $gateway = ($this->gatewayFactory)(); // built only when required
        // $gateway->charge(...) etc.
    }
}

// Composition root supplies the factory, not the container
$svc = new ReportService(fn() => new StripePaymentGateway($stripe ?? null));
echo 'lazy dependency wired', PHP_EOL;

Kurzprüfung

Wem sollte die Schnittstelle PaymentGateway gehören?

Zusammenfassung

Sie haben Adapter richtig mit dem Kern verbunden:

  • Inversion ist nicht gleich Injection: Injection ist der Mechanismus, Inversion bedeutet, dass der Consumer die Abstraktion besitzt.
  • Der Kern definiert die Schnittstelle; Infrastrukturadapter richten sich danach.
  • Verwenden Sie Konstruktor-Injection von Abstraktionen; nehmen Sie die gesamte konkrete Verdrahtung in einem einzigen Composition Root vor (oder in einer Container-Konfiguration, die Schnittstellen Klassen zuordnet).
  • Das Austauschen von Adaptern und der Einsatz von Fakes in Tests wird zu einer Änderung an einer einzigen Stelle.
  • Vermeiden Sie das Service-Locator-Anti-Pattern — halten Sie den Container ausschließlich am Rand.

Häufig gestellte Fragen

Ist die Lektion „Dependency Inversion in der Praxis“ kostenlos?

Ja — der vollständige Text von „Dependency Inversion in der Praxis“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Dependency Inversion in der Praxis“?

Verbinden Sie Adapter mithilfe der Dependency Inversion mit dem Kern. Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um PHP Academy zu starten?

Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Dependency Inversion in der Praxis“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?

Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Von Layered Architecture zur Clean Architecture
  2. Ports und Adapter erklärt
  3. Use Cases und Application Services
  4. Dependency Inversion in der Praxis
← Zurück zu PHP Academy