Infrastruktur-Schicht
Technische Details und Anbindung an die Außenwelt.
Die Infrastruktur-Schicht in einer DDD-Symfony-Anwendung
Die Infrastruktur-Schicht ist die äußerste Schicht einer DDD-Anwendung. Sie enthält alle technischen Details, Framework-spezifischen Implementierungen und Integrationen mit externen Systemen. Diese Schicht implementiert die in der Domain-Schicht definierten Interfaces und stellt die Verbindung zur Außenwelt her.
Struktur der Infrastruktur-Schicht
src/
└── Infrastructure/
├── Repository/ # Repository-Implementierungen
├── Persistence/ # Doctrine-Mappings und Konfiguration
│ ├── Doctrine/ # Doctrine-spezifischer Code
│ └── Migration/ # Datenbank-Migrationen
├── Security/ # Sicherheitskomponenten
├── Api/ # API-Integrationen
├── Messaging/ # Message Bus, Queue-Integrationen
├── EventListener/ # Symfony Event Listener
└── Service/ # Infrastruktur-Dienste
Repository-Implementierungen
In der Infrastruktur-Schicht werden die Repository-Interfaces aus der Domain-Schicht konkret umgesetzt.
// src/Infrastructure/Repository/DoctrineProductRepository.php
use App\Domain\Model\Product;
use App\Domain\Repository\ProductRepository;
use Doctrine\ORM\EntityManagerInterface;
class DoctrineProductRepository implements ProductRepository
{
public function __construct(private EntityManagerInterface $em) {}
public function findById(string $id): ?Product
{
return $this->em->find(Product::class, $id);
}
public function save(Product $product): void
{
$this->em->persist($product);
$this->em->flush();
}
}
Mapper für Entitäten
Wenn die Datenbankstruktur nicht direkt dem Domain-Modell entspricht, können Mapper zwischen Infrastruktur-Entitäten und Domänenmodellen vermitteln.
// src/Infrastructure/Persistence/ProductMapper.php
class ProductMapper
{
public static function mapToDomain(array $row): Product
{
return new Product(
$row['id'],
$row['name'],
new Price($row['price_in_cents'])
);
}
}
Doctrine-Entitäten
Infrastruktur-Entitäten mit Annotations oder PHP-Attributen für Doctrine:
// src/Infrastructure/Persistence/Doctrine/ProductEntity.php
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'product')]
class ProductEntity
{
#[ORM\Id]
#[ORM\Column(type: 'string')]
public string $id;
#[ORM\Column(type: 'string')]
public string $name;
#[ORM\Column(name: 'price_in_cents', type: 'integer')]
public int $priceInCents;
}
Symfony Messenger als Command Bus und Event Dispatcher
Die Infrastruktur-Schicht konfiguriert und verwendet Symfony Messenger:
# config/packages/messenger.yaml
framework:
messenger:
routing:
'App\Application\Command\CreateProduct': sync
Symfony Forms
Formulare werden in der Infrastruktur-Schicht erstellt und verarbeitet:
// src/Infrastructure/Form/ProductType.php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class)
->add('price', TextType::class);
}
}
Symfony Security
Authenticatoren, Voter und Sicherheitsregeln leben in der Infrastruktur-Schicht.
// src/Infrastructure/Security/ProductVoter.php
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class ProductVoter extends Voter
{
protected function supports(string $attribute, $subject): bool
{
return $attribute === 'EDIT_PRODUCT';
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
return true; // Logik nach Bedarf
}
}
API-Integrationen
Zugriffe auf externe Dienste (REST, SOAP, GraphQL, etc.) werden hier gekapselt:
// src/Infrastructure/Api/ProductApiClient.php
use Symfony\Contracts\HttpClient\HttpClientInterface;
class ProductApiClient
{
public function __construct(private HttpClientInterface $client) {}
public function fetchProductData(string $externalId): array
{
$response = $this->client->request('GET', 'https://api.example.com/products/' . $externalId);
return $response->toArray();
}
}
Dependency Injection Konfiguration
In der services.yaml
wird geregelt, welche Implementierung zu welchem Interface gehört:
# config/services.yaml
services:
App\Domain\Repository\ProductRepository:
alias: App\Infrastructure\Repository\DoctrineProductRepository
Zusammenfassung
Die Infrastruktur-Schicht:
- Implementiert die in der Domain-Schicht definierten Interfaces
- Enthält Framework-spezifischen Code (z. B. für Security, Forms, ORM)
- Kapselt die Anbindung an externe Systeme (z. B. APIs, Mail, Filesystem)
- Konfiguriert Symfony-spezifische Dinge wie Services, Events, Messenger
- Verbindet die technische Welt mit den fachlichen Anforderungen
Die Infrastruktur-Schicht ist die einzige Schicht, die von konkreten Technologien oder Frameworks abhängig ist. Durch die klare Trennung zur Domain kannst du diese technischen Details jederzeit austauschen, ohne die Kernlogik zu verändern.