# ADR001 – WP2-002 Composition Root

**Status 21:00:** Proposed – Review durch LA erforderlich
**Status 22:00:** completed and verified
**Datum:** 2026-07-19  
**Workpackage:** WP2-002  
**Bezug:** `ADR001-WP2-RUNTIME_v2.md`, WP2-001
**Note PL** Abgeschlossen ca. 2026-07-19 22:00

Feedback LA: ADR001-WP2-002-COMPOSITION-ROOT_LA_REVIEW_SUMMARY_.md

## 1. Kontext

Mit WP2-001 wurden die Grundlagen der Runtime Infrastructure eingeführt:

- `Configuration` lädt und validiert die vorhandenen PHP-Konfigurationen.
- `RuntimeContext` enthält ausschließlich immutable request-scoped data.
- `ApplicationServiceFactory` bildet den schmalen Vertrag für den Controller.
- `RuntimeFactory` erzeugt für jede Verarbeitungseinheit einen neuen tenantbezogenen `ApplicationService`.

Die konkrete Objektverdrahtung befindet sich derzeit weiterhin in
`public/reconcile.php`. Der Einstiegspunkt erstellt unter anderem:

- `ProviderRegistry` und Provider,
- `ReconciliationOrchestrator`,
- `RuntimeFactory`,
- `ApiKeyAuthenticator`,
- `ManifestBuilder`,
- Mapper und Renderer,
- `ReconciliationController`.

Dadurch enthält `public/reconcile.php` neben der technischen Behandlung des
HTTP-Einstiegs auch die vollständige Assembly des Objektgraphen. Diese
Vermischung erschwert Tests, weitere Einstiegspunkte und die kontrollierte
Weiterentwicklung der Infrastruktur.

## 2. Entscheidung

WP2-002 führt einen eigenständigen Composition Root für den
Reconciliation-HTTP-Einstiegspunkt ein.

Die konkrete Klasse erhält den Namen:

```text
App\Infrastructure\Composition\ReconciliationCompositionRoot
```

Der spezifische Name wird einem generischen `CompositionRoot` vorgezogen,
weil Reconcilix perspektivisch mehrere unabhängig verdrahtete Einstiegspunkte
besitzen kann, beispielsweise:

- OpenRefine Reconciliation API,
- CLI-Kommandos,
- Batch-Verarbeitung,
- Worker-Prozesse,
- Evaluation Export.

Jeder Einstiegspunkt darf einen eigenen, klar abgegrenzten Composition Root
besitzen. Ein zentraler universeller Container wird ausdrücklich nicht
eingeführt.

## 3. Zielarchitektur

```text
public/reconcile.php
        │
        ├── PHP-/Fehlerkonfiguration
        ├── Bootstrap laden
        ├── Configuration laden und validieren
        ├── optional: bestehendes Request-Debug-Logging
        │
        ▼
ReconciliationCompositionRoot
        │
        ├── ProviderRegistry
        ├── Provider
        ├── ReconciliationOrchestrator
        ├── RuntimeFactory
        ├── ApiKeyAuthenticator
        ├── ManifestBuilder
        ├── Request-/Response-Mapper
        ├── PreviewRenderer
        │
        ▼
ReconciliationController
        │
        ▼
handle()
```

Der Einstiegspunkt kennt nach der Umstellung nur noch:

- den Autoload-/Bootstrap-Pfad,
- den Konfigurationspfad,
- `Configuration`,
- `ReconciliationCompositionRoot`,
- den erzeugten `ReconciliationController`.

## 4. Verantwortlichkeiten

### 4.1 `public/reconcile.php`

`public/reconcile.php` bleibt der technische HTTP-Einstiegspunkt.

Er ist verantwortlich für:

- PHP-Fehler- und Logging-Einstellungen des Einstiegspunkts,
- Laden von `src/bootstrap.php`,
- Laden und Validieren der Konfiguration über `Configuration`,
- Erzeugen des `ReconciliationCompositionRoot`,
- Aufruf von `ReconciliationController::handle()`.

Das bereits vorhandene Request-Debug-Logging darf in WP2-002 unverändert im
Einstiegspunkt verbleiben. Es ist Request-I/O und keine Objektverdrahtung.
Eine spätere Überführung in eine dedizierte Logging-Komponente ist nicht
Bestandteil dieses Workpackages.

`public/reconcile.php` enthält nach WP2-002 keine direkte Erzeugung von
Providern, Registry, Orchestrator, Factory, Mappern, Renderer oder Controller-
Abhängigkeiten mehr.

### 4.2 `Configuration`

`Configuration` bleibt unverändert verantwortlich für:

- Laden,
- Validieren,
- Bereitstellen der Konfiguration.

Der Composition Root lädt keine Konfigurationsdateien selbst und enthält
keine Validierungslogik.

### 4.3 `ReconciliationCompositionRoot`

Der `ReconciliationCompositionRoot` ist ausschließlich verantwortlich für:

- Verwendung der bereits validierten `Configuration`,
- Auswahl konkreter Implementierungen,
- Erzeugung und Verdrahtung des Objektgraphen,
- Bereitstellung des vollständig konstruierten
  `ReconciliationController`.

Vorgesehene öffentliche API:

```php
final class ReconciliationCompositionRoot
{
    public function __construct(
        private readonly Configuration $configuration,
    ) {
    }

    public function createController(): ReconciliationController
    {
        // Assembly des Objektgraphen
    }
}
```

Interne private Methoden dürfen zur Lesbarkeit verwendet werden, zum Beispiel:

```php
private function createProviderRegistry(): ProviderRegistry;
private function createOrchestrator(ProviderRegistry $registry): ReconciliationOrchestrator;
private function createRuntimeFactory(ReconciliationOrchestrator $orchestrator): RuntimeFactory;
```

Diese Methoden bilden keine öffentlich nutzbare Factory API. Sie dienen nur
der strukturierten Assembly innerhalb des Composition Root.

### 4.4 `RuntimeFactory`

Die `RuntimeFactory` bleibt verantwortlich für die Erzeugung der
request-scoped bzw. tenantbezogenen Application Services.

Der Composition Root erzeugt genau eine `RuntimeFactory` für den HTTP-
Objektgraphen. Die `RuntimeFactory` erzeugt bei jedem Aufruf von `create()`:

- einen neuen `RuntimeContext`,
- einen neuen tenantbezogenen `LegacyCandidateDiscoveryAdapter`,
- einen neuen `ReconciliationApplicationService`.

Damit bleibt die in WP2-001 festgelegte Trennung bestehen:

```text
Composition Root   → erzeugt application-scoped Infrastruktur
RuntimeFactory     → erzeugt processing-unit-scoped Infrastruktur
RuntimeContext     → enthält immutable processing-unit-scoped data
```

### 4.5 `ReconciliationController`

Der Controller bleibt unverändert von Abstraktionen bzw. seinen expliziten
Konstruktorabhängigkeiten abhängig.

Insbesondere kennt er weiterhin nur:

```text
ApplicationServiceFactory
```

und nicht die konkrete `RuntimeFactory` oder den Composition Root.

## 5. Lebensdauer der Objekte

WP2-002 unterscheidet folgende Lebensdauern:

### Application-scoped innerhalb eines HTTP-Requests

Durch den Composition Root einmal erzeugt:

- `ProviderRegistry`,
- Provider,
- `ReconciliationOrchestrator`,
- `RuntimeFactory`,
- `ApiKeyAuthenticator`,
- `ManifestBuilder`,
- Mapper,
- `PreviewRenderer`,
- `ReconciliationController`.

Da PHP-FPM bzw. der klassische PHP-Webrequest den Prozesskontext pro Request
begrenzt, entspricht dies derzeit faktisch request-lokaler Assembly.

### Processing-unit-scoped

Durch `RuntimeFactory::create(TenantContext $tenant)` für eine authentifizierte
Verarbeitungseinheit erzeugt:

- `RuntimeContext`,
- tenantbezogene Adapter,
- `ReconciliationApplicationService`.

Für zukünftige Long-running Processes gilt weiterhin: Tenant-bezogene
Objekte dürfen nicht zwischen Verarbeitungseinheiten wiederverwendet werden.

## 6. Konkreter Objektgraph

Der Composition Root verdrahtet den bestehenden Objektgraphen ohne fachliche
Änderung:

```text
ProviderRegistry
 ├── XtreeProvider
 ├── ReconciliationStoreProvider
 ├── MockExternalProvider("wikidata")
 ├── MockExternalProvider("gbif")
 └── MockExternalProvider("gnd")

ReconciliationOrchestrator
 ├── ProviderRegistry
 ├── DefaultRanker
 └── provider configuration

RuntimeFactory
 ├── ReconciliationOrchestrator
 ├── vocabulary configuration
 ├── graphStatusUri
 ├── nodeStatusUri
 └── matchingStrategyUri

ReconciliationController
 ├── ApiKeyAuthenticator
 ├── ManifestBuilder
 ├── ApplicationServiceFactory → RuntimeFactory
 ├── OpenRefineRequestMapper
 ├── OpenRefineResponseMapper
 ├── PreviewRenderer
 ├── vocabulary configuration
 ├── defaultLimit
 └── maxLimit
```

Die vorhandenen Status- und Strategie-URIs bleiben in WP2-002 unverändert.
Ihre spätere Überführung in Konfiguration oder definierte Identifikatoren ist
eine mögliche Roadmap-/Ideas-Frage, aber ausdrücklich nicht Teil dieses
Workpackages.

## 7. Abgrenzung

WP2-002 verändert nicht:

- öffentliche OpenRefine-Verträge,
- Authentifizierung,
- Tenant-Modell,
- Provider-Verhalten,
- Vocabulary-Auflösung,
- Candidate Discovery,
- Ranking,
- Domain Model,
- RuntimeContext,
- fachliche Reconciliation-Logik,
- Manifest- oder Preview-Ausgabe.

WP2-002 führt nicht ein:

- DI-Container,
- Service Locator,
- Framework,
- globale Singleton-Registry,
- generische Container-API,
- neue Konfigurationsformate,
- neue Provider oder Features.

## 8. Teststrategie

WP2-002 ergänzt einen fokussierten Test für den Composition Root.

Vorgesehener Test:

```text
tests/Composition/ReconciliationCompositionRootTest.php
```

Der Test prüft mindestens:

1. Der Composition Root kann mit einer gültigen `Configuration` erzeugt
   werden.
2. `createController()` liefert einen `ReconciliationController`.
3. Der Objektgraph kann ohne Zugriff auf HTTP-Superglobals aufgebaut werden.
4. Die vorhandenen WP1- und WP2-001-Tests bleiben unverändert erfolgreich.

Ein vollständiger HTTP-End-to-End-Test ist weiterhin Aufgabe der Integration
durch PL mit OpenRefine bzw. dem realen Endpoint.

## 9. Geplante Änderungen im Workpackage

### Neue Dateien

```text
src/Infrastructure/Composition/ReconciliationCompositionRoot.php
tests/Composition/ReconciliationCompositionRootTest.php
docs/workpackages/WP2/README_WP2-002.md
```

### Geänderte Dateien

```text
public/reconcile.php
```

Änderungen an weiteren Dateien sind nur zulässig, wenn sie für die bestehende
Verdrahtung technisch zwingend erforderlich sind. Solche Änderungen müssen im
Workpackage-README ausdrücklich dokumentiert werden.

## 10. Akzeptanzkriterien

WP2-002 ist abgeschlossen, wenn:

- `public/reconcile.php` keine konkrete Objektverdrahtung mehr enthält,
- der vollständige HTTP-Objektgraph durch
  `ReconciliationCompositionRoot` erzeugt wird,
- der Composition Root ausschließlich validierte `Configuration` verwendet,
- der Controller weiterhin nur vom `ApplicationServiceFactory`-Vertrag
  abhängt,
- keine Geschäfts-, Request- oder Konfigurationslogik in den Composition Root
  verschoben wurde,
- der neue Composition-Root-Test erfolgreich läuft,
- alle vorhandenen Tests weiterhin erfolgreich laufen,
- der OpenRefine-End-to-End-Test nach Integration unverändert erfolgreich ist.

## 11. Konsequenzen

### Positive Konsequenzen

- `public/reconcile.php` wird klein und verständlich.
- Die konkrete Infrastrukturverdrahtung besitzt einen eindeutigen Ort.
- Weitere Einstiegspunkte können eigene Composition Roots erhalten.
- Der Objektgraph kann unabhängig vom HTTP-Einstiegspunkt getestet werden.
- Die Runtime-Architektur aus WP2-001 wird konsequent abgeschlossen.

### Bewusst akzeptierte Konsequenzen

- Der Composition Root kennt viele konkrete Klassen. Dies ist beabsichtigt,
  weil er der einzige Ort für konkrete Assembly ist.
- Die Klasse besitzt mehrere Erzeugungsschritte. Private Methoden dienen der
  Lesbarkeit, ohne daraus einen Service Locator oder generischen Container zu
  machen.
- Der Objektgraph wird zunächst bei jedem HTTP-Request neu aufgebaut. Dies
  entspricht der aktuellen PHP-Laufzeit und vermeidet globale Zustände.

## 12. Nicht entschiedene Ideen für Roadmap-Abstimmung

Die folgenden Beobachtungen werden nicht in WP2-002 umgesetzt und müssen vor
einer späteren Aufnahme mit der Roadmap abgestimmt werden:

- Überführung der Status- und Strategie-URIs in eine typisierte
  Konfiguration oder zentrale Identifikator-Definitionen.
- Dedizierte Logging-Komponente für Request-Debug-Logging.
- Eigene Composition Roots für CLI, Batch, Worker und Evaluation Export.
- Entfernung der derzeit registrierten `MockExternalProvider`, sobald die
  Roadmap deren weiteren Umgang festlegt.

Diese Punkte dürfen nach gemeinsamer Abstimmung in `docs/ideas/` dokumentiert
werden. `docs/ideas/` ersetzt weder Roadmap noch Workpackages.

## 13. Review-Fragen an LA

1. Ist ein spezifischer `ReconciliationCompositionRoot` einem generischen
   `CompositionRoot` vorzuziehen?
2. Ist die Trennung zwischen application-scoped Assembly im Composition Root
   und processing-unit-scoped Assembly in der `RuntimeFactory` eindeutig?
3. Ist es architektonisch korrekt, das bestehende Request-Debug-Logging vorerst
   im HTTP-Einstiegspunkt zu belassen?
4. Bleibt der Composition Root mit der vorgeschlagenen öffentlichen API frei
   von Service-Locator-Charakter?
5. Kann WP2-002 auf dieser Grundlage implementiert werden?
