Zum Hauptinhalt springen

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:

  1. Implementiert die in der Domain-Schicht definierten Interfaces
  2. Enthält Framework-spezifischen Code (z. B. für Security, Forms, ORM)
  3. Kapselt die Anbindung an externe Systeme (z. B. APIs, Mail, Filesystem)
  4. Konfiguriert Symfony-spezifische Dinge wie Services, Events, Messenger
  5. 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.