# WP3-001-003 – Implementation Plan  
## Candidate Discovery Adapter Contract and Registry

- **Status:** Proposed for PL approval
- **Version:** 0.1
- **Scope:** WP3 – Discovery Framework Foundation
- **Basis:** aktueller Projektstand `reconcile_ADR002.WP2_20260801_2.zip`
- **Related:** WP3-001-003 – Candidate Discovery Adapter Contract and Registry v0.2

---

# 1. Ziel

WP3-001-003 führt den gemeinsamen technischen Vertrag für Candidate Discovery Adapter sowie die zugehörige Adapter Registry ein.

Nach Abschluss des Teilpakets können technische Discovery-Implementierungen

- denselben Contract implementieren,
- unter einem eindeutigen `adapterKey` registriert werden,
- und unabhängig von konkreten Adapterklassen aufgelöst werden.

Das Teilpaket führt noch keinen produktiven Adapter und keine Runtime-Integration ein.

---

# 2. Analyse des aktuellen Projektstands

Im aktuellen Projekt existieren bereits:

- `CandidateDiscoveryGateway`
- `ReconciliationCommand`
- `DiscoveredCandidate`
- `LegacyCandidateDiscoveryAdapter`
- `DiscoveryRoute`
- `DiscoveryConfigurationRegistry`

Der bestehende Application Port verwendet bereits folgenden Vertrag:

```php
/** @return list<DiscoveredCandidate> */
public function discover(ReconciliationCommand $command): array;
```

Der neue `CandidateDiscoveryAdapter` soll denselben fachlichen Ein- und Rückgabetyp verwenden. Dadurch können der spätere Router und der Compatibility Adapter ohne zusätzliche Mapping-Schicht arbeiten.

`CandidateItem` wird weiterhin erst im `ReconciliationApplicationService` innerhalb des `ReconciliationResult` erzeugt.

---

# 3. Neue Produktionsdateien

## 3.1 CandidateDiscoveryAdapter

**Pfad**

```text
src/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapter.php
```

**Namespace**

```php
App\Infrastructure\Discovery\Adapter
```

**Typ**

```php
interface CandidateDiscoveryAdapter
```

**Contract**

```php
/** @return list<DiscoveredCandidate> */
public function discover(ReconciliationCommand $command): array;
```

**Verwendete Typen**

```php
App\Application\Reconciliation\ReconciliationCommand
App\Application\Reconciliation\DiscoveredCandidate
```

**Verantwortung**

- generische Discovery-Anfrage entgegennehmen,
- technische Candidate Discovery ausführen,
- `DiscoveredCandidate[]` zurückgeben.

**Nicht verantwortlich**

- Route auswählen,
- Adapter registrieren,
- Domain-Entities `CandidateItem` erzeugen,
- Match Decisions treffen,
- Runtime konfigurieren.

---

## 3.2 CandidateDiscoveryAdapterRegistry

**Pfad**

```text
src/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterRegistry.php
```

**Namespace**

```php
App\Infrastructure\Discovery\Adapter
```

**Typ**

```php
final readonly class CandidateDiscoveryAdapterRegistry
```

**Interne Struktur**

```php
array<string, CandidateDiscoveryAdapter>
```

Der Array-Key ist der `adapterKey`, den eine `DiscoveryRoute` referenziert.

**Konstruktor**

```php
/**
 * @param array<string, CandidateDiscoveryAdapter> $adapters
 */
public function __construct(private array $adapters)
```

Die Registry wird bewusst unveränderlich aufgebaut. Eine nachträgliche `register()`-Methode ist für WP3-001-003 nicht erforderlich. Die vollständige Adapterkonfiguration wird später im Composition Root übergeben.

**Öffentliche API**

```php
public function adapterFor(string $adapterKey): CandidateDiscoveryAdapter
```

**Verhalten**

- gültiger registrierter Key → Adapterinstanz zurückgeben,
- leerer oder nur aus Leerzeichen bestehender Key → `InvalidArgumentException`,
- unbekannter Key → `RuntimeException`,
- Keys und Werte werden nicht normalisiert,
- registrierte Adapterinstanzen werden unverändert zurückgegeben.

**Konstruktorvalidierung**

- jeder Key muss ein nicht leerer String sein,
- jeder Wert muss `CandidateDiscoveryAdapter` implementieren.

---

# 4. Testimplementierung

Der `DummyCandidateDiscoveryAdapter` wird entsprechend LA Review 002 ausschließlich in der Teststruktur geführt.

**Pfad**

```text
tests/Infrastructure/Discovery/Adapter/Support/DummyCandidateDiscoveryAdapter.php
```

**Aufgabe**

- implementiert `CandidateDiscoveryAdapter`,
- speichert den empfangenen `ReconciliationCommand`,
- liefert eine beim Erzeugen vorgegebene Liste von `DiscoveredCandidate`,
- enthält keine produktive Discovery-Logik.

Die Testklasse wird nicht unter `src/` abgelegt und ist kein Bestandteil der Runtime.

---

# 5. Neue Testdateien

## 5.1 CandidateDiscoveryAdapterContractTest

**Pfad**

```text
tests/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterContractTest.php
```

**Testfälle**

1. Dummy-Adapter implementiert `CandidateDiscoveryAdapter`.
2. `ReconciliationCommand` wird unverändert an den Adapter übergeben.
3. Adapter liefert dieselben `DiscoveredCandidate`-Instanzen zurück.
4. Rückgabe bleibt eine geordnete Liste.
5. Der Contract erzeugt keine `CandidateItem`-Entities.

**Erwartete Ausgabe**

```text
WP3-001-003 Candidate Discovery Adapter Contract: OK
```

---

## 5.2 CandidateDiscoveryAdapterRegistryTest

**Pfad**

```text
tests/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterRegistryTest.php
```

**Testfälle**

1. Ein registrierter Adapter wird über seinen `adapterKey` aufgelöst.
2. Mehrere Adapter können unabhängig voneinander aufgelöst werden.
3. Die zurückgegebene Objektidentität bleibt erhalten.
4. Ein leerer Registry-Zustand ist zulässig.
5. Ein unbekannter `adapterKey` erzeugt den dokumentierten Fehler.
6. Ein leerer oder blanker Lookup-Key wird abgewiesen.
7. Ein leerer oder blanker Registrierungs-Key wird abgewiesen.
8. Ein ungültiger Registry-Wert wird abgewiesen.
9. Keys werden nicht automatisch getrimmt oder normalisiert.

**Erwartete Ausgabe**

```text
WP3-001-003 Candidate Discovery Adapter Registry: OK
```

---

# 6. Geplanter Laufzeitvertrag

```text
DiscoveryRoute.adapterKey
        │
        ▼
CandidateDiscoveryAdapterRegistry
        │
        ▼
CandidateDiscoveryAdapter
        │
        ▼
DiscoveredCandidate[]
```

WP3-001-003 stellt ausschließlich Contract und Auflösung bereit.

Die Verbindung mit `DiscoveryConfigurationRegistry` und `DiscoveryRoute` erfolgt erst in WP3-001-004 durch den `CandidateDiscoveryRouter`.

---

# 7. Fehlerverhalten

Vorgesehene Fehlermeldungen:

```text
CandidateDiscoveryAdapterRegistry.adapterKey must not be empty.
```

```text
CandidateDiscoveryAdapterRegistry adapter keys must not be empty.
```

```text
CandidateDiscoveryAdapterRegistry adapters must contain only CandidateDiscoveryAdapter instances.
```

```text
Unknown CandidateDiscoveryAdapter: <adapterKey>
```

Für WP3-001-003 werden keine eigenen Exception-Klassen eingeführt. Der Umfang rechtfertigt derzeit keine zusätzliche Exception-Hierarchie.

---

# 8. Änderungen an bestehenden Dateien

Für WP3-001-003 sind keine Änderungen an bestehenden Produktionsdateien vorgesehen.

Insbesondere unverändert bleiben:

- `CandidateDiscoveryGateway`
- `ReconciliationCommand`
- `DiscoveredCandidate`
- `LegacyCandidateDiscoveryAdapter`
- `DiscoveryRoute`
- `DiscoveryConfigurationRegistry`
- `ReconciliationApplicationService`
- Composition Root
- RuntimeContext
- RuntimeFactory

---

# 9. Geplanter Änderungsumfang

## Neue Produktionsdateien

```text
src/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapter.php
src/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterRegistry.php
```

## Neue Testdateien

```text
tests/Infrastructure/Discovery/Adapter/Support/DummyCandidateDiscoveryAdapter.php
tests/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterContractTest.php
tests/Infrastructure/Discovery/Adapter/CandidateDiscoveryAdapterRegistryTest.php
```

## Geänderte bestehende Dateien

```text
keine
```

---

# 10. Akzeptanzkriterien

WP3-001-003 ist umgesetzt, wenn:

1. `CandidateDiscoveryAdapter` den gemeinsamen Discovery Contract definiert.
2. Der Contract `ReconciliationCommand` entgegennimmt.
3. Der Contract `DiscoveredCandidate[]` zurückgibt.
4. `CandidateDiscoveryAdapterRegistry` Adapter über `adapterKey` auflöst.
5. Registry und Adapter keine Kenntnisse über Discovery Routes oder Routing besitzen.
6. Der Dummy-Adapter ausschließlich in der Teststruktur liegt.
7. Contract- und Registry-Tests erfolgreich sind.
8. Die Tests von WP3-001-001 und WP3-001-002 weiterhin erfolgreich sind.
9. Alle übrigen ausführbaren Bestandstests weiterhin erfolgreich sind.

---

# 11. Out of Scope

Nicht Bestandteil sind:

- produktive Candidate Discovery Adapter,
- Local Reconciliation Store Adapter,
- xTree JSON API Adapter,
- CandidateDiscoveryRouter,
- Discovery Route Selection,
- Runtime Integration,
- Composition Root,
- HTTP, REST oder SPARQL,
- neue Domain-Objekte,
- neue Exception-Hierarchie,
- mutable Adapter Registry.

---

# 12. Engineering-Votum

Der vorgeschlagene Schnitt bleibt KISS-konform:

- ein Interface,
- eine unveränderliche Registry,
- eine Testimplementierung ausschließlich unter `tests/`,
- zwei fokussierte Tests,
- keine Änderungen am bestehenden Runtime-Pfad.

Der Contract spiegelt bewusst die bestehende Signatur des `CandidateDiscoveryGateway`. Dadurch wird WP3-001-004 den Router ohne zusätzliche Konvertierung zwischen Application Port und Technical Adapter implementieren können.
