Hexagonale Architektur in PHP: sauberer Code ohne Schmerz

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Ihr Code ist eine Festung, keine Spaghetti

\n\n

Haben Sie schon einmal ein Projekt von Laravel auf Symfony oder von MySQL auf PostgreSQL umgeschrieben? Wenn ja, kennen Sie den Schmerz: Controller, die von SQL-Abfragen durchdrungen sind, Modelle, die alles über HTTP-Anfragen wissen, und Geschäftslogik, die beim Framework-Wechsel stirbt. Kommt Ihnen das bekannt vor?

\n\n

Willkommen in der Welt der Hexagonalen Architektur in PHP (auch bekannt als Ports & Adapters). Das ist kein modischer Buzzword, sondern ein Rettungsanker für Projekte, die lange und glücklich leben wollen. In diesem Artikel zeigen wir, wie Sie eine PHP-Anwendung bauen, die keine Angst vor Veränderungen hat: Datenbankwechsel, Umstieg auf einen anderen Cache oder eine neue API – all das wird zur routinemäßigen Adapter-Anpassung, nicht zum Umschreiben der halben Codebasis.

\n\n

Was ist hexagonale Architektur eigentlich?

\n\n

Stellen Sie sich vor, Ihre Anwendung ist ein Kern (Core), der nicht weiß, woher die Daten kommen: aus dem Web, der Konsole, einem Telegram-Bot oder Tests. Um diesen Kern herum liegen Ports (Schnittstellen), und außen befinden sich Adapter (Implementierungen). Hexagonale Architektur in PHP ermöglicht es, die Geschäftslogik vom Infrastrukturcode zu trennen. Sie schreiben die Regeln einmal und verwenden sie überall.

\n\n

Die Schlüsselprinzipien:

\n
    \n
  • Dependency Inversion – Code der oberen Ebene hängt nicht von der unteren ab. Es ist genau umgekehrt.
  • \n
  • Geschäftslogik ist isoliert – kein Framework dringt in Ihre Use Cases ein.
  • \n
  • Testbarkeit – Sie können den Kern ohne Datenbank und HTTP testen.
  • \n
\n\n

Im Gegensatz zur Clean Architecture von Robert Martin ist Hexagonal flexibler: Sie schreibt keine bestimmte Anzahl von Schichten vor, sondern sagt einfach: „Innen ist die Domäne, außen die Infrastruktur, verbindet sie über Schnittstellen“.

\n\n

Projektstruktur: Vom Chaos zur Ordnung

\n\n

Kommen wir gleich zur Sache. Hier ist eine typische Ordnerstruktur für hexagonale Architektur in PHP:

\n\n
src/\n├── Domain/\n│   ├── Entity/\n│   │   └── Order.php\n│   ├── Repository/\n│   │   └── OrderRepositoryInterface.php\n│   └── Service/\n│       └── OrderService.php\n├── Application/\n│   ├── UseCase/\n│   │   └── CreateOrderUseCase.php\n│   └── Port/\n│       ├── Input/\n│       │   └── CreateOrderInputPort.php\n│       └── Output/\n│           └── OrderOutputPort.php\n├── Infrastructure/\n│   ├── Persistence/\n│   │   └── DoctrineOrderRepository.php\n│   └── Http/\n│       └── OrderController.php\n└── Framework/\n    └── Symfony/\n        └── Kernel.php
\n\n

Haben Sie bemerkt? Domain und Application wissen nichts von Doctrine, Symfony oder MySQL. Sie arbeiten nur mit Schnittstellen. Die Adapter leben in Infrastructure und werden über Dependency Injection eingebunden.

\n\n

Schritt 1: Den Kern definieren – Domain

\n\n

Beginnen wir mit der Bestell-Entität. Keine ORM-Annotationen, keine Setter, die Invarianten verletzen:

\n\n
<?php\n// src/Domain/Entity/Order.php\nnamespace App\\Domain\\Entity;\n\nclass Order\n{\n    public function __construct(\n        private readonly string $id,\n        private readonly string $productName,\n        private float $totalAmount,\n        private string $status = 'pending'\n    ) {}\n\n    public function approve(): void\n    {\n        if ($this->status !== 'pending') {\n            throw new \\DomainException('Only pending orders can be approved.');\n        }\n        $this->status = 'approved';\n    }\n\n    public function getId(): string { return $this->id; }\n    public function getProductName(): string { return $this->productName; }\n    public function getTotalAmount(): float { return $this->totalAmount; }\n    public function getStatus(): string { return $this->status; }\n}
\n\n

Und hier ist ein Port (Interface) für das Repository. Beachten Sie: Es weiß nichts von Doctrine oder PDO:

\n\n
<?php\n// src/Domain/Repository/OrderRepositoryInterface.php\nnamespace App\\Domain\\Repository;\n\nuse App\\                

Blogs

Buchempfehlungen