Ports und Adapter erklärt
Isolieren Sie den Kern mit Ports und austauschbaren Adaptern.
Ports und Adapter erklärt ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 2 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.
Die Hexagon-Idee
Ports & Adapters – Alistair Cockburns Hexagonal Architecture – stellt Ihre Anwendung als Hexagon dar. Im Inneren befindet sich reine Geschäftslogik. Jede Interaktion mit der Außenwelt (HTTP, Datenbank, Queue, Uhr, E-Mail) überschreitet einen Port, und jeder Port wird durch einen oder mehrere Adapter bedient. Die Form hat keine bevorzugte Ober- oder Unterseite: Benutzeroberfläche und Datenbank sind symmetrisch und beide lediglich Adapter.
Ports sind Interfaces
Ein Port ist ein Interface, das dem Anwendungskern gehört und einen Bedarf oder eine Fähigkeit in Begriffen der Domäne ausdrückt. Er darf keine Infrastrukturbegriffe nach außen geben – kein PDOStatement, kein GuzzleResponse, kein Eloquent.
<?php
// Driven (outbound) port: the core needs to persist users
interface UserRepository
{
public function byId(UserId $id): ?User;
public function save(User $user): void;
}Driving- und Driven-Ports
Es gibt zwei Varianten:
- Driving-Ports (primäre, eingehende Ports) – die API, die von der Außenwelt aufgerufen wird, um die Anwendung anzusteuern. In der Regel handelt es sich um Ihre Use-Case-Interfaces.
- Driven-Ports (sekundäre, ausgehende Ports) – Interfaces, die die Anwendung aufruft, um die Außenwelt zu erreichen: Repositories, Mailer und Uhren.
Driving-Adapter rufen den Kern auf; der Kern kommuniziert über Driven-Adapter nach außen.
<?php
// Driving (inbound) port — the public capability of the core
interface RegisterUser
{
public function handle(string $email, string $plainPassword): UserId;
}Der Kern implementiert Driving-Ports
Der Use Case implementiert einen Driving-Port und hängt von Driven-Ports ab. Beachten Sie: Er akzeptiert PasswordHasher und Clock als injizierte Ports – sogar Zeit und Hashing werden abstrahiert, damit der Kern deterministisch und testbar bleibt.
<?php
final class RegisterUserService implements RegisterUser
{
public function __construct(
private UserRepository $users,
private PasswordHasher $hasher,
private Clock $clock,
) {}
public function handle(string $email, string $plain): UserId {
if ($this->users->byEmail($email)) {
throw new EmailAlreadyTaken($email);
}
$user = User::register(
$email,
$this->hasher->hash($plain),
$this->clock->now()
);
$this->users->save($user);
return $user->id();
}
}Ein Driven-Adapter
Ein Driven-Adapter implementiert einen Driven-Port mithilfe einer konkreten Technologie. Hier erfüllt ein PDO-Adapter UserRepository. Sie können ihn durch Doctrine, Redis oder einen HTTP-API-Client ersetzen, ohne den Kern anzutasten.
<?php
final class PdoUserRepository implements UserRepository
{
public function __construct(private PDO $pdo) {}
public function byId(UserId $id): ?User {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([(string) $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? User::fromRow($row) : null;
}
public function save(User $user): void {
// INSERT ... ON CONFLICT UPDATE
}
}Ein Driving-Adapter
Ein Driving-Adapter übersetzt einen externen Auslöser in einen Aufruf eines Driving-Ports. Ein HTTP-Controller, ein CLI-Befehl oder ein Nachrichtenkonsument – alle sind austauschbare Driving-Adapter für denselben Use Case.
<?php
// CLI driving adapter
final class RegisterUserCommand
{
public function __construct(private RegisterUser $register) {}
public function run(array $argv): int {
[$email, $password] = array_slice($argv, 1);
$id = $this->register->handle($email, $password);
fwrite(STDOUT, "Created user $id\n");
return 0;
}
}In-Memory-Adapter für Tests
Der größte Vorteil: Jeder Driven-Port erhält ein schnelles Fake. Tests führen den echten Use Case mit In-Memory-Adaptern, deterministischen Uhren und einem No-op-Hasher aus.
<?php
final class FixedClock implements Clock {
public function __construct(private DateTimeImmutable $t) {}
public function now(): DateTimeImmutable { return $this->t; }
}
final class PlainHasher implements PasswordHasher {
public function hash(string $p): string { return 'h:' . $p; }
}
$service = new RegisterUserService(
new InMemoryUsers(),
new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01'))
);
echo 'wired OK', PHP_EOL;Adapter übersetzen, sie entscheiden nie
Ein häufiger Fehler besteht darin, Geschäftsregeln in Adapter einfließen zu lassen. Als Faustregel gilt: Ein Adapter übersetzt ausschließlich Datenformate und Protokolle. Wenn Sie in einem Controller oder Repository ein if zu Preisen, Berechtigungen oder Status finden, gehört es in den Kern.
- Zuordnung JSON ↔ DTO: Adapter
- Hydrierung SQL ↔ Entity: Adapter
- „VIP erhält 10 % Rabatt“: Kern
Ein Port, viele Adapter
Ports ermöglichen das Austauschen und sogar die parallele Verwendung von Adaptern. Ein NotificationPort kann gemeinsam verwendete Adapter für E-Mail, SMS und Slack haben. Der Kern ruft eine Methode auf; die Verdrahtung entscheidet, wie viele Kanäle antworten.
<?php
interface Notifier { public function send(string $to, string $msg): void; }
final class CompositeNotifier implements Notifier {
/** @param Notifier[] $channels */
public function __construct(private array $channels) {}
public function send(string $to, string $msg): void {
foreach ($this->channels as $c) $c->send($to, $msg);
}
}
$notifier = new CompositeNotifier([new EmailNotifier(), new SmsNotifier()]);
echo 'composed', PHP_EOL;Wo sich das Hexagon in Ordner abbildet
Ein pragmatischer PHP-Aufbau für einen Bounded Context:
Domain/— Entitäten, Wertobjekte, DomänendiensteApplication/Port/In/— Schnittstellen der Driving-Ports (Use Cases)Application/Port/Out/— Schnittstellen der Driven-Ports (Repositories, Uhr)Application/— Implementierungen der Use CasesInfrastructure/Adapter/In/— Controller, CLI, ConsumerInfrastructure/Adapter/Out/— PDO-/Doctrine-/HTTP-Adapter
Der Composition Root (Konfiguration des DI-Containers) verbindet In- und Out-Adapter mit den Ports.
Das gesamte Hexagon testen
Über Unit-Tests hinaus ermöglichen Ports schnelle Akzeptanztests, die die Anwendung über ihren primären Port ansteuern und über In-Memory-Sekundäradapter prüfen. Damit wird ein vollständiger Use Case ohne HTTP oder Datenbank abgedeckt. Dieselbe Testsuite kann später mit echten Adaptern als Integrationstests ausgeführt werden. So erhalten Sie eine geschichtete Teststrategie, ohne Tests neu schreiben zu müssen.
<?php
// Acceptance test: real use case, fake driven adapters, no I/O
$users = new InMemoryUsers();
$service = new RegisterUserService($users, new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01')));
$id = $service->handle('dev@coddykit.com', 'pw');
assert($users->byId($id) !== null);
echo 'acceptance: user persisted via in-memory adapter', PHP_EOL;Kurzprüfung
Welche Aussage über Ports und Adapter ist korrekt?
Zusammenfassung
Ports & Adapters kapselt den Kern hinter Schnittstellen:
- Ports sind Schnittstellen in der Domänensprache, die dem Kern gehören.
- Driving-Ports werden von eingehenden Adaptern aufgerufen; Driven-Ports werden vom Kern aufgerufen und von ausgehenden Adaptern bereitgestellt.
- Adapter übersetzen ausschließlich Protokolle und Formate — sie treffen niemals Geschäftsentscheidungen.
- Ein Port unterstützt viele Adapter (Fakes für Tests, zusammengesetzte Adapter für Fan-out).
- Der Composition Root verbindet alles; das Hexagon bleibt frei von Frameworks.
Häufig gestellte Fragen
Ist die Lektion „Ports und Adapter erklärt“ kostenlos?
Ja — der vollständige Text von „Ports und Adapter erklärt“ 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 „Ports und Adapter erklärt“?
Isolieren Sie den Kern mit Ports und austauschbaren Adaptern. 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 2 von 4.
Wie lange dauert die Lektion „Ports und Adapter erklärt“?
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
- Von Layered Architecture zur Clean Architecture
- Ports und Adapter erklärt
- Use Cases und Application Services
- Dependency Inversion in der Praxis