# Current Data Flow

**Dokumentzweck:** Technische Bestandsaufnahme der bestehenden Reconcilix-Anwendung als Vorbereitung für ADR001 (Repository Pattern) und WP1 (Domain Integration).

**Analysierter Projektstand:** Übergabe an ED vom 16.07.2026  
**Rolle:** ED – Engineering Agent  
**Status:** Ist-Aufnahme; noch keine Architekturentscheidung

---

## 1. Entry Point from OpenRefine

### 1.1 Öffentlicher Reconciliation-Endpunkt

Der zentrale HTTP-Entry-Point ist:

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

Die projektweite `.htaccess` leitet alle nicht als Datei vorhandenen Requests auf diesen Entry Point um:

```apache
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]

RewriteRule ^.*$ public/reconcile.php [L]
```

Dadurch wird beispielsweise folgender OpenRefine-Serviceaufruf verarbeitet:

```text
http://localhost:8080/reconcile/v2/?api_key=<api-key>
```

`public/reconcile.php` übernimmt aktuell Composition-Root-Aufgaben:

1. Laden des eigenen Autoloaders über `src/bootstrap.php`
2. Laden der Konfigurationen:
   - `config/tenants.php`
   - `config/vocabularies.php`
   - `config/providers.php`
3. Registrierung der Provider im `ProviderRegistry`
4. Erzeugung des `ReconciliationController`
5. Übergabe der Request-Verarbeitung an `ReconciliationController::handle()`

Registrierte Provider:

```php
XtreeProvider
ReconciliationStoreProvider
MockExternalProvider('wikidata')
MockExternalProvider('gbif')
MockExternalProvider('gnd')
```

Für die aktuelle produktive Candidate Discovery der MCT-Objektfacette wird der `ReconciliationStoreProvider` verwendet. Der `XtreeProvider::search()` ist derzeit noch nicht implementiert und liefert ein leeres Array.

### 1.2 Request-Typen am Reconciliation-Endpunkt

Der `ReconciliationController` unterscheidet:

```text
OPTIONS
→ CORS-Preflight

GET /v2/
→ OpenRefine-Manifest

GET /v2/preview?id=...
→ HTML-Preview eines Concepts

POST /v2/
→ Reconciliation Batch Request
```

Authentifizierung erfolgt vor der eigentlichen Verarbeitung über:

```text
ApiKeyAuthenticator
→ TenantContext
```

Der API-Key wird als Query-Parameter erwartet:

```text
?api_key=<api-key>
```

### 1.3 Weitere relevante Entry Points

#### Store Builder

```text
tools/buildReconciliationStore.php
```

Aktuell sowohl als CLI- als auch als HTTP-Tool nutzbar:

```text
CLI:
php tools/buildReconciliationStore.php matcult-the_vocnet_org_00000019

HTTP:
http://localhost:8080/reconcile/tools/buildReconciliationStore.php?store=matcult-the_vocnet_org_00000019
```

Das Tool:

1. lädt `config/vocabularies.php`,
2. ermittelt die Store-Konfiguration,
3. wählt anhand von `ReconciliationStore::builder` einen Builder,
4. erzeugt `meta.json`, `terms.json` und `voc_items.json`.

Aktuell unterstützter Builder:

```text
xtree_voc_item_builder
→ XtreeVocItemBuilder
```

#### Evaluation Export

```text
public/evaluation-export.php
```

Das Tool nimmt einen OpenRefine-Projektexport entgegen und erzeugt daraus eine Evaluationstabelle:

```text
OpenRefine .openrefine.tar.gz
→ OpenRefineProjectReader
→ EvaluationTableBuilder
→ CsvEvaluationTableExporter
→ CSV-Download
```

Dieser Datenfluss ist fachlich relevant für WP5, aber nicht Bestandteil des aktuellen Reconciliation-Laufzeitpfads.

#### CLI Evaluation Export

```text
tools/exportOpenRefineEvaluationTable.php
```

CLI-Einstieg für denselben bzw. einen verwandten Evaluationsworkflow.

---

## 2. SourceValue Representation

### 2.1 Aktuelle Repräsentation

Ein Source Value wird aktuell nicht als eigenständiges Domain Object repräsentiert.

Die OpenRefine-Eingabe wird in:

```text
OpenRefineRequest::queriesFromPost()
```

aus dem POST-Feld `queries` gelesen und direkt in ein technisches Request-Objekt transformiert:

```php
ReconciliationQuery
```

Aktuelle Felder:

```php
public readonly string $qid
public readonly string $term
public readonly ?string $type
public readonly ?string $subVocabularyId
public readonly int $limit
public readonly ReconciliationContext $context
```

Bedeutung:

| Feld | Aktuelle Funktion |
|---|---|
| `qid` | technische OpenRefine-Batch-ID, z. B. `q0` |
| `term` | Source Value als einfacher String |
| `type` | von OpenRefine übergebener Type-Identifier |
| `subVocabularyId` | intern erkannte SubVocabulary-URI |
| `limit` | maximale Candidate-Anzahl |
| `context` | Container für OpenRefine-Properties |

Der fachliche Source Value ist damit aktuell nur:

```php
ReconciliationQuery::$term
```

### 2.2 Kontextrepräsentation

OpenRefine-Properties werden aktuell unverändert in:

```php
ReconciliationContext
```

übernommen:

```php
final class ReconciliationContext
{
    public function __construct(
        public readonly array $properties = []
    ) {}
}
```

Es existiert noch:

- keine fachliche Typisierung der Properties,
- kein `ContextItem`,
- keine URI-basierte Context-Property-Klassifikation,
- keine Validierung,
- keine Persistenz,
- keine Trennung zwischen technischem Request-Kontext und fachlichem Kontext.

### 2.3 Abgrenzung zu DM001

`ReconciliationQuery` ist ein Transport-/Requestmodell und darf nicht ohne Weiteres mit `SourceValue` gleichgesetzt werden.

Vorgesehene Trennung für WP1:

```text
OpenRefine Request DTO
    ReconciliationQuery
        ↓ Adapter/Factory
Domain
    SourceValue
```

Dabei sollten mindestens folgende Informationen getrennt behandelt werden:

```text
qid
→ Transportkorrelation zu OpenRefine

term
→ SourceValue.value

type / subVocabularyId
→ Reconciliation-Ziel bzw. Strategy-/Vocabulary-Kontext

limit
→ Request-/Presentation-Parameter

properties
→ später ContextItem[], abhängig von ADR002
```

`SourceValue` sollte nicht von OpenRefine-spezifischen Feldern wie `qid` oder `limit` abhängig sein.

---

## 3. Reconciliation Processing

### 3.1 Aktueller Hauptfluss

Der aktuelle Laufzeitfluss für einen POST-Request lautet:

```text
HTTP POST
→ public/reconcile.php
→ ReconciliationController::handle()
→ OpenRefineRequest::queriesFromPost()
→ ReconciliationQuery[]
→ pro Query:
   ReconciliationOrchestrator::reconcile()
→ Candidate[]
→ ReconciliationController::toOpenRefineResults()
→ JsonResponse::send()
```

### 3.2 Request Parsing

`OpenRefineRequest::queriesFromPost()`:

1. liest `$_POST['queries']`,
2. dekodiert das JSON als assoziatives Array,
3. begrenzt `limit` zwischen Default- und Maximalwert,
4. liest `type`,
5. erkennt, ob `type` einer konfigurierten SubVocabulary entspricht,
6. erzeugt `ReconciliationContext`,
7. erzeugt je Batch-Eintrag ein `ReconciliationQuery`.

Aktueller Array-Datenfluss:

```text
$_POST['queries'] JSON string
→ array<string,array>
→ ReconciliationQuery[]
```

### 3.3 Orchestrierung

`ReconciliationOrchestrator::reconcile()` übernimmt derzeit mehrere Verantwortlichkeiten:

- Eingangsvalidierung
- Store-Auswahl
- Tenant-Berechtigungsprüfung
- Provider-Auswahl
- perspektivische Fallback-Ketten
- Provider-Aufruf
- Candidate-Zusammenführung
- Ranking
- Limitierung

Der aktuelle Primärpfad:

```text
ReconciliationQuery
→ findEnabledStoreForQuery()
→ ReconciliationStore
→ ProviderRegistry::get('reconciliation-store')
→ ReconciliationStoreProvider::search()
→ Candidate[]
```

Wenn kein Store gefunden wird, versucht der Orchestrator den älteren Provider-/Fallback-Pfad zu verwenden. Dieser Teil ist derzeit inkonsistent mit der aktuellen objektbasierten Vocabulary-Konfiguration und noch als technischer Prototyp zu betrachten.

### 3.4 Candidate Discovery im lokalen Store

`ReconciliationStoreProvider::search()`:

1. erhält die Store-Konfiguration als Array-Eintrag `['store' => ReconciliationStore]`,
2. berechnet den lokalen Pfad zu `terms.json`,
3. lädt und dekodiert die vollständige JSON-Datei,
4. normalisiert den Query-Term,
5. sucht einen direkten Term-Key,
6. verwendet bei fehlendem direkten Treffer eine Tokenanalyse,
7. erzeugt für jeden Hit ein `Candidate`-Objekt,
8. begrenzt das Ergebnis auf `query->limit`.

Direkter Treffer:

```text
query.term
→ TextNormalizer::normalize()
→ terms[normalizedTerm]
→ hit[]
→ Candidate[]
```

Fallback über Tokenanalyse:

```text
query.term
→ QueryTokenizer::analyze()
→ gewichtete Tokens
→ Lookup jedes Tokens in terms.json
→ Gruppierung nach Candidate-URI
→ Sortierung nach Tokenanzahl und Gewicht
→ Candidate[]
```

### 3.5 Ranking

Der `DefaultRanker` sortiert ausschließlich absteigend nach:

```php
Candidate::$score
```

Im lokalen Store-Pfad wird der Ranker aktuell nicht mehr aufgerufen, da der Orchestrator bei vorhandenem Store direkt aus `reconcile()` zurückkehrt.

Das bedeutet:

```text
Local Store Path
→ Reihenfolge wird vom Store-Provider bestimmt

Fallback Provider Path
→ Reihenfolge wird zusätzlich durch DefaultRanker bestimmt
```

Diese unterschiedliche Behandlung sollte bei WP1/WP3 bewusst bereinigt werden.

### 3.6 Aktuelles Candidate-Objekt

```php
final class Candidate
{
    public function __construct(
        public readonly string $id,
        public readonly string $label,
        public readonly int $score = 0,
        public readonly bool $match = false,
        public readonly array $meta = [],
    ) {}
}
```

`Candidate` vermischt aktuell:

- Provider-Ergebnis,
- Ranking-Ergebnis,
- OpenRefine-Ausgabeattribute,
- technische Metadaten.

Es entspricht nicht dem `CandidateItem` aus DM001.

Insbesondere fehlen bzw. sind nicht sauber getrennt:

- Zugehörigkeit zu einem `ReconciliationResult`
- Rank als eigenes Attribut
- normalisierte Score-Skala `0..1`
- Matching Strategy bzw. Technique
- Vocabulary Matching Pattern
- Provenienz
- persistierbare Identität
- Lebenszyklus und Entscheidung

### 3.7 Fehlende Domain-Schritte

Aktuell nicht vorhanden:

```text
SourceValue
→ InterpretationGraph
→ InterpretationNode
→ ReconciliationResult
→ CandidateItem
→ MatchDecision
```

Die Candidate Discovery wird unmittelbar aus `ReconciliationQuery` gestartet.

---

## 4. xTree/MCT Access

### 4.1 Lokaler Reconciliation Store

Für die MCT-Objektfacette wird aktuell der lokale Store verwendet.

Konfiguration:

```text
config/vocabularies.php
→ SubVocabulary
→ ReconciliationStore[]
```

Aktiver Store:

```text
matcult-the_vocnet_org_00000019
```

Der Store besteht aus:

```text
meta.json
terms.json
voc_items.json
```

#### `terms.json`

Funktion:

```text
normalisierter Term
→ Candidate-Hits
```

Ein Hit enthält typischerweise:

```text
id
local_id
record_type
label
role
lang
qualifier
weight
```

`ReconciliationStoreProvider` verwendet aktuell nur `terms.json`.

#### `voc_items.json`

Funktion:

```text
Concept-URI
→ lokale Projektion des vollständigen xTree-Vokabularelements
```

Enthält unter anderem:

```text
Labels
Broader-Relationen
Status
Processing Status
Notes
Mappings
```

Diese Datei wird für die Candidate Discovery aktuell nicht gelesen.

#### `meta.json`

Enthält Metadaten über:

- Store-ID
- Builder
- Quelldatei
- Erstellungszeitpunkt
- Formatversion
- Statistiken

### 4.2 Store-Erzeugung

`XtreeVocItemBuilder` verarbeitet einen vollständigen xTree-JSON-Export.

Aktueller Datenfluss:

```text
xTree JSON export
→ source/<store-id>/*.json
→ XtreeVocItemBuilder
→ terms.json
→ voc_items.json
→ meta.json
```

Der Builder:

- verarbeitet nur `Concept`,
- bildet Concept-URIs,
- extrahiert Labels,
- extrahiert Broader-Relationen,
- extrahiert Notes und Mappings,
- erzeugt Termgewichte anhand der Label Role,
- schreibt JSON-Dateien.

Zu beachten:

Im vorliegenden Code von `XtreeVocItemBuilder` ist im sichtbaren Hauptloop keine explizite Abweisung von `status = deleted` vor der Übernahme in `voc_items.json` erkennbar. Da die analysierten Store-Daten gelöschte Items offenbar nicht enthalten, muss vor einer Änderung verifiziert werden, ob der Filter in einem weiteren Methodenteil erfolgt oder der konkrete Store mit einer anderen Codefassung erzeugt wurde.

### 4.3 Live-xTree-Zugriff

Vorhandene Klassen:

```text
XtreeJsonApiClient
XtreeJsonApiResultMapper
SourceSystemFactory
CredentialStore
XtreeProvider
```

#### `XtreeJsonApiClient`

Verantwortlich für:

- Login am xTree-REST-Dienst,
- Session-Cookie,
- `getSearchVocItemsByTerm`,
- `getFetchHierarchy`,
- JSON-Decodierung,
- HTTP-Fehlerbehandlung.

Technik:

```text
PHP cURL
PHPSESSID Cookie
Array-basierte JSON-Responses
```

#### `XtreeJsonApiResultMapper`

Transformiert:

```text
xTree API array
→ Candidate[]
```

Aktuelles Verhalten:

- ignoriert Concepts mit `status = deleted`,
- wählt bevorzugt deutsches `prefLabel`,
- erzeugt einen konstanten Score von `80`,
- begrenzt die Trefferzahl,
- übernimmt nur wenige Metadaten.

#### `XtreeProvider`

`XtreeProvider::search()` ist aktuell nicht implementiert:

```php
return [];
```

Die Klasse wird derzeit hauptsächlich für Preview verwendet.

#### `SourceSystemFactory`

Kann aktuell nur folgenden Client erzeugen:

```text
xtree-json
→ XtreeJsonApiClient
```

Die in `providers.php` genannten Clients:

```text
xtree-solr
xtree-sparql
wikidata-reconcile
wikidata-sparql
gnd-lobid
gbif-rest
```

sind noch nicht in der Factory implementiert.

### 4.4 Preview-Pfad

```text
GET /preview?id=<concept-uri>
→ PreviewRenderer
→ Provider-Auswahl anhand URI-String
→ XtreeProvider::preview()
→ XtreeRdfPreviewClient
→ HTML
```

Die Preview nutzt somit nicht den lokalen `voc_items.json`-Store, sondern einen separaten RDF-Zugriff.

---

## 5. Response Generation

### 5.1 Interne Response-Transformation

Der Controller wandelt `Candidate[]` direkt in OpenRefine-kompatible Arrays um:

```php
[
    'id' => $candidate->id,
    'name' => $candidate->label,
    'type' => [],
    'score' => $candidate->score,
    'match' => $candidate->match,
]
```

Aktueller Datenfluss:

```text
Candidate[]
→ array<int,array>
→ Batch-Response:
   qid => ['result' => ...]
→ JsonResponse::send()
```

### 5.2 OpenRefine-Ausgabe

Beispiel:

```json
{
  "q0": {
    "result": [
      {
        "id": "http://matcult-the.vocnet.org/48905400",
        "name": "Altarretabel",
        "type": [],
        "score": 85,
        "match": false
      }
    ]
  }
}
```

### 5.3 Technische Kopplungen

Die OpenRefine-Ausgabe wird aktuell direkt im Controller gebaut. Dadurch ist der Controller an die konkrete Struktur von `Candidate` gekoppelt.

Die bereits vorhandene Klasse:

```text
OpenRefineResponseBuilder
```

wird im aktuellen Controllerpfad nicht verwendet. Sie stammt aus einem früheren bzw. alternativen Implementierungsstand.

Für WP1 sollte die spätere Grenze lauten:

```text
Domain CandidateItem / ReconciliationResult
→ OpenRefine Response Adapter
→ OpenRefine JSON
```

Die Domain Objects dürfen keine OpenRefine-Felder wie `name`, `match` oder `type: []` erzwingen.

---

## 6. Existing Store, Service and Gateway Classes

### 6.1 Store-bezogene Klassen

| Klasse | Aktuelle Rolle | Einordnung |
|---|---|---|
| `ReconciliationStore` | Konfigurationsobjekt für lokalen Store | Kein Repository; keine Laufzeitpersistenz |
| `XtreeVocItemBuilder` | Erzeugt JSON-Suchstore aus xTree-Export | Builder/ETL-Komponente |
| `ReconciliationStoreProvider` | Sucht Candidates in `terms.json` | Provider bzw. Read Gateway |
| `QueryTokenizer` | Token-/Kompositanalyse | Matching-Hilfskomponente |
| `TextNormalizer` | normalisiert Suchwerte | Matching-Hilfskomponente |

### 6.2 Provider- und Gateway-Klassen

| Klasse | Aktuelle Rolle | Einordnung |
|---|---|---|
| `ProviderInterface` | gemeinsamer Vertrag für Search und Preview | Provider Port, aktuell stark an Legacy-Modelle gekoppelt |
| `ProviderRegistry` | Registry nach Provider-Key | Laufzeitauflösung |
| `XtreeJsonApiClient` | HTTP-Client zum xTree-REST-Dienst | External Gateway |
| `XtreeJsonApiResultMapper` | xTree-Response zu `Candidate[]` | Anti-Corruption-/Mapper-Ansatz |
| `XtreeProvider` | Provider für xTree; Search noch leer | unvollständiger Adapter |
| `MockExternalProvider` | Platzhalter externer Systeme | Prototyp |
| `SourceSystemFactory` | baut technische Clients | Factory für External Gateways |
| `CredentialStore` | lädt Client-Zugangsdaten | Infrastrukturservice |

### 6.3 Reconciliation Services

| Klasse | Aktuelle Rolle |
|---|---|
| `ReconciliationController` | HTTP-Steuerung und Response-Mapping |
| `OpenRefineRequest` | statischer Request Parser / DTO Factory |
| `ReconciliationOrchestrator` | Store-/Provider-Auswahl, Fallback und Ablauf |
| `DefaultRanker` | Sortierung nach Legacy-Score |
| `ManifestBuilder` | OpenRefine-Manifest |
| `PreviewRenderer` | Preview-Provider-Auswahl |

### 6.4 Authentifizierung und Tenant-Kontext

```text
ApiKeyAuthenticator
→ TenantContext
```

`TenantContext` enthält aktuell:

- Tenant-ID
- Name
- erlaubte Vokabulare
- erlaubte Provider/Source Systems
- erlaubte SubVocabularies
- Feature Flags
- API-Key

Für WP1 soll Tenant-Zugehörigkeit zunächst konzeptionell berücksichtigt, aber nicht umfassend in alle Domain Objects und Persistenztabellen eingebaut werden, sofern ADR001/AM001 dies nicht ausdrücklich festlegt.

### 6.5 Keine vorhandenen Domain-Repositories

Im aktuellen Projekt existieren keine Repositories für:

```text
SourceValue
InterpretationGraph
InterpretationNode
ReconciliationResult
CandidateItem
MatchDecision
```

Ebenso fehlen:

- PDO-/DBAL-Abstraktion,
- Unit of Work,
- ORM,
- Transaction Service,
- Migration Runner,
- Repository Interfaces,
- Data Mapper für DM002.

Der Begriff `ReconciliationStore` bezeichnet aktuell einen statischen JSON-Suchindex und darf nicht mit einem Repository für Reconciliation-Laufdaten verwechselt werden.

---

## 7. Proposed Integration Points for DM001

### 7.1 Grundprinzip

WP1 sollte den aktuellen OpenRefine-Vertrag und die Provider zunächst erhalten, aber zwischen Transportmodell, Domainmodell und Providerdaten klare Adapter einführen.

Empfohlener vertikaler Einstieg:

```text
OpenRefineRequest
→ ReconciliationQuery (Transport DTO)
→ SourceValueFactory / Application Service
→ SourceValue
→ InterpretationGraph
→ initialer InterpretationNode
→ Candidate Discovery Port
→ ReconciliationResult
→ CandidateItem[]
→ OpenRefine Response Adapter
```

### 7.2 Eingriffspunkt A: nach Request Parsing

Aktuell:

```text
OpenRefineRequest
→ ReconciliationQuery
→ ReconciliationOrchestrator
```

WP1:

```text
OpenRefineRequest
→ ReconciliationQuery
→ Reconciliation Application Service
   → SourceValue erzeugen
   → initialen InterpretationGraph erzeugen
   → initialen InterpretationNode erzeugen
```

`ReconciliationQuery` bleibt dabei ein technisches DTO.

### 7.3 Eingriffspunkt B: Orchestrator auf Application Service reduzieren

Der aktuelle `ReconciliationOrchestrator` enthält zu viele Verantwortlichkeiten.

Für WP1 sollte er schrittweise in Richtung eines Application Service entwickelt werden, beispielsweise:

```text
ReconcileSourceValue
oder
ReconciliationApplicationService
```

Verantwortung:

1. Source Value anlegen
2. initiale Interpretation anlegen
3. Candidate Discovery auslösen
4. Reconciliation Result erzeugen
5. Candidate Items übernehmen
6. Ergebnis an Output Adapter geben

Nicht seine Verantwortung:

- direktes Lesen von JSON-Dateien,
- xTree-HTTP-Aufrufe,
- OpenRefine-JSON-Erzeugung,
- spätere SQL-Details.

### 7.4 Eingriffspunkt C: Provider Interface

Aktuell:

```php
search(
    ReconciliationQuery $query,
    TenantContext $tenant,
    array $vocabularyConfig
): Candidate[]
```

Das Interface ist an:

- OpenRefine-Transportdaten,
- TenantContext,
- untypisierte Konfigurationsarrays,
- Legacy-`Candidate`

gekoppelt.

Für WP1 sollte ein neuer fachlicher Port vorbereitet werden. Mögliche Form, noch durch ADR001/weitere ADRs festzulegen:

```text
CandidateDiscoveryGateway
```

Eingabe beispielsweise:

```text
InterpretationNode
TargetVocabulary
DiscoveryOptions
```

Ausgabe beispielsweise:

```text
DiscoveredCandidate[]
```

Der Application Service erzeugt daraus:

```text
ReconciliationResult
CandidateItem[]
```

KISS-Hinweis: Das bestehende `ProviderInterface` muss nicht sofort entfernt werden. Ein Adapter kann zunächst den alten Provider aufrufen und dessen `Candidate[]` in neue `CandidateItem`-Daten transformieren.

### 7.5 Eingriffspunkt D: lokaler Store Provider

`ReconciliationStoreProvider` ist der wichtigste erste Adapter für WP1.

Kurzfristiger Migrationspfad:

```text
ReconciliationQuery
→ bestehender ReconciliationStoreProvider
→ Candidate[]
→ LegacyCandidateToCandidateItemMapper
→ ReconciliationResult + CandidateItem[]
```

Zielpfad:

```text
InterpretationNode
→ LocalStoreCandidateDiscoveryGateway
→ DiscoveredCandidate[]
→ ReconciliationResult + CandidateItem[]
```

Der JSON-Store selbst bleibt in WP1 unverändert ein Read Model.

### 7.6 Eingriffspunkt E: Response Generation

Aktuell:

```text
Candidate[]
→ ReconciliationController::toOpenRefineResults()
```

WP1:

```text
ReconciliationResult
→ OpenRefineReconciliationResultMapper
→ OpenRefine JSON array
```

Damit wird OpenRefine zu einem Output Adapter und verliert die direkte Kopplung an Domain Objects.

### 7.7 Eingriffspunkt F: Persistenzgrenze für ADR001

ADR001 muss insbesondere entscheiden:

- Repository je Aggregate oder je Entity?
- Was ist das Aggregate Root?
- Wird `SourceValue` gemeinsam mit Graph/Nodes gespeichert?
- Wird `ReconciliationResult` gemeinsam mit `CandidateItem` gespeichert?
- Wie werden IDs erzeugt?
- Wo liegen Transaktionsgrenzen?
- Werden Domain Objects vollständig rehydriert oder nur geschrieben?
- Werden Repository Interfaces im Domain- oder Application-Namespace definiert?
- Wie bleibt der statische `ReconciliationStore` vom relationalen Repository begrifflich getrennt?

Technisch naheliegende Aggregate-Kandidaten:

```text
SourceValue
└── ContextItem[]
└── InterpretationGraph[]
    └── InterpretationNode[]

ReconciliationResult
└── CandidateItem[]
└── MatchDecision?
```

Die endgültige Festlegung gehört in ADR001 bzw. in die Architekturentscheidung von LA.

### 7.8 Empfohlene Kompatibilitätsstrategie

Für den ersten WP1-Schnitt:

1. keine Änderung des öffentlichen OpenRefine-Endpunkts,
2. keine Änderung am JSON-Store-Format,
3. keine Aktivierung externer Fallbacks,
4. neue Domain Objects parallel zu Legacy-Modellen einführen,
5. Legacy-zu-Domain-Mapper verwenden,
6. bestehende zehn Testwerte als schnelle Regression Fixture verwenden,
7. große OpenRefine-Exporte später für WP5/Regression nutzen.

---

## 8. PHP, Framework and Autoloading Structure

### 8.1 PHP-Version

- Lokale Testumgebung PL: PHP Version 8.2.12
- Zielserver:  8.3.6

Der Quellcode verwendet unter anderem:

```php
readonly properties
constructor property promotion
match expressions
str_starts_with()
never return type
default new object in constructor parameter
```

Daraus folgt:

```text
technische Mindestversion: PHP 8.1
```

Für die konkrete lokale Laufzeit muss separat geprüft werden:

```bash
php -v
```

Zusätzlich ist bei XAMPP die Apache-PHP-Version zu prüfen, da CLI und Apache unterschiedliche PHP-Binaries bzw. Konfigurationen verwenden können.

### 8.2 Framework

Es wird kein Framework verwendet.

Nicht vorhanden:

- Symfony
- Laravel
- Slim
- Doctrine
- Composer-basierte Dependency Injection
- Routing Library

Die Anwendung ist eine eigenständige PHP-Anwendung mit manuellem Composition Root.

### 8.3 Autoloading

`src/bootstrap.php` registriert einen eigenen PSR-4-ähnlichen Autoloader:

```text
Namespace prefix:
App\

Basisverzeichnis:
src/
```

Beispiel:

```text
App\Orchestrator\ReconciliationOrchestrator
→ src/Orchestrator/ReconciliationOrchestrator.php
```

Es gibt derzeit:

- keine `composer.json`,
- kein Composer-Autoloading,
- keine externen PHP-Abhängigkeiten im übergebenen Archiv.

### 8.4 Konfiguration

Konfiguration erfolgt über PHP-Dateien, die Objekte und Arrays zurückgeben:

```text
config/tenants.php
config/vocabularies.php
config/providers.php
config/credentials.local.php
```

Die Konfiguration ist derzeit gemischt:

- objektbasierte Konfiguration für `Vocabulary`, `SubVocabulary`, `ReconciliationStore`,
- arraybasierte Konfiguration für Provider, Tenants und Serviceparameter.

Einige ältere Codepfade erwarten noch ältere Arraystrukturen. Dies ist insbesondere im Fallback-Teil des `ReconciliationOrchestrator` sichtbar.

### 8.5 Tests

Die README nennt ein `tests/`-Verzeichnis, im übergebenen Projektstand ist jedoch keine erkennbare PHPUnit-/Composer-Testinfrastruktur vorhanden.

Vor WP1 sollte mindestens eine kleine ausführbare Regressionsebene geschaffen werden für:

- Request Parsing,
- Store-Auswahl,
- Candidate Discovery,
- OpenRefine Response,
- später Domain-Invarianten.

Dies muss nicht zwingend vor ADR001 vollständig umgesetzt werden, sollte aber Bestandteil der WP1-Implementierungsplanung sein.

---

## 9. Open Questions

### Für ADR001 relevant

1. Welche Aggregate Roots werden verbindlich festgelegt?
2. Soll ein Repository vollständige Graph-Aggregate oder einzelne Entities speichern?
3. Wo werden Transaktionen geöffnet und abgeschlossen?
4. Welche IDs werden in PHP erzeugt, welche in der Datenbank?
5. Wie werden Domain Objects rehydriert, ohne sie an PDO/SQL zu koppeln?
6. Sollen Repository Interfaces im Domain Layer oder Application Layer liegen?
7. Ist ein separates `ReconciliationRun`-Konzept nötig oder für die aktuelle Phase ausdrücklich zu vermeiden?
8. Wie wird die begriffliche Kollision zwischen statischem `ReconciliationStore` und relationalen Repositories vermieden?
9. Wird `MatchDecision` im ersten Persistenzschnitt bereits angelegt, obwohl OpenRefine derzeit keine Entscheidung zurückmeldet?
10. Wird Tenant-ID in Phase 1 persistiert oder nur als späterer Erweiterungspunkt berücksichtigt?

### Technisch zu verifizieren

1. Exakte PHP-Version der XAMPP-Apache-Laufzeit
2. Exakte PHP-Version der CLI-Laufzeit
3. Ob `XtreeVocItemBuilder` im aktuellen Stand `status=deleted` tatsächlich filtert
4. Ob der lokale Store bei jedem Request vollständig aus `terms.json` geladen werden soll oder später gecacht wird
5. Welche ältere Fallback-Logik noch aktiv bleiben muss
6. Ob `OpenRefineResponseBuilder` entfernt oder als Output Adapter weiterentwickelt wird
7. Ob Preview in WP1 weiterhin live aus RDF geladen wird oder perspektivisch `voc_items.json` nutzt
8. Welche Minimaltests PL/LA für die Freigabe von WP1 erwarten

---

## 10. Compact Current Flow Diagram

```text
OpenRefine
   |
   | POST queries=<JSON>
   v
.htaccess
   |
   v
public/reconcile.php
   |
   | Composition Root
   v
ReconciliationController
   |
   +--> ApiKeyAuthenticator --> TenantContext
   |
   +--> OpenRefineRequest
   |       |
   |       v
   |   ReconciliationQuery[]
   |
   v
ReconciliationOrchestrator
   |
   +--> Store für SubVocabulary vorhanden?
           |
           +-- ja --> ReconciliationStoreProvider
           |              |
           |              +--> terms.json
           |              +--> TextNormalizer
           |              +--> QueryTokenizer
           |              |
           |              v
           |          Candidate[]
           |
           +-- nein --> Provider-/Fallback-Prototyp
                          |
                          +--> XtreeProvider::search()
                               aktuell leer
   |
   v
ReconciliationController::toOpenRefineResults()
   |
   v
JsonResponse
   |
   v
OpenRefine
```

## 11. Intended WP1 Flow

```text
OpenRefine Request
   |
   v
Transport DTO: ReconciliationQuery
   |
   v
Application Service
   |
   +--> SourceValue
   +--> InterpretationGraph
   +--> initial InterpretationNode
   |
   v
Candidate Discovery Port
   |
   +--> Local Store Adapter
   +--> später xTree Live Adapter
   |
   v
ReconciliationResult
   |
   +--> CandidateItem[]
   |
   v
OpenRefine Output Adapter
   |
   v
OpenRefine Response
```
