# ADR001 — Repository Boundary and Persistence Strategy

**Version:** 1.0  
**Status:** Accepted  
**Date created:** 2026-07-16
**Date updated:** 2026-07-16  
**Decision Owner:** LA  
**Implementation Owner:** ED  
**Related:** DM001, DM002, ME001, ME002, ROADMAP_MATCHING, AM001, WP1_CURRENT_DATA_FLOW

---

## 1. Context

Die bestehende Reconcilix-Anwendung verarbeitet OpenRefine-Reconciliation-Requests derzeit unmittelbar über technische Request- und Provider-Modelle.

Der aktuelle Hauptfluss lautet:

```text
OpenRefine Request
→ ReconciliationQuery
→ ReconciliationOrchestrator
→ Provider / lokaler ReconciliationStore
→ Candidate[]
→ OpenRefine Response
```

Dabei bestehen folgende strukturelle Kopplungen:

- `ReconciliationQuery` enthält OpenRefine-spezifische Transportinformationen.
- Der fachliche `SourceValue` ist lediglich als String in `ReconciliationQuery::$term` vorhanden.
- Der aktuelle `Candidate` vermischt Provider-Ergebnis, Ranking, OpenRefine-Ausgabeattribute und technische Metadaten.
- Der `ReconciliationOrchestrator` übernimmt zugleich Validierung, Store-Auswahl, Provider-Auswahl, Candidate Discovery, Ranking und Limitierung.
- Die OpenRefine-Response wird direkt im Controller aus `Candidate[]` erzeugt.
- Eine relationale Persistenz fachlicher Reconciliation-Verläufe existiert noch nicht.
- Der bestehende `ReconciliationStore` ist ein statischer JSON-Suchindex und kein Repository für Domain Objects.

Mit DM001 und DM002 wurde eine neue fachliche und relationale Zielarchitektur festgelegt:

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

WP1 führt diese Domain Objects in die bestehende Anwendung ein. WP2 ergänzt anschließend die relationale Persistenz gemäß DM002.

Dafür muss festgelegt werden:

- wo die Domain Boundary verläuft,
- welche Komponenten Domain Objects erzeugen und verwenden,
- wie Persistenzzugriffe entkoppelt werden,
- welche Repository-Grenzen gelten,
- wo Transaktionen beginnen und enden,
- wie die bestehende Anwendung evolutionär migriert wird.

---

## 2. Decision Drivers

Die Entscheidung wird durch folgende Anforderungen bestimmt:

1. DM001 bleibt das fachliche Referenzmodell.
2. Domain Objects dürfen keine Abhängigkeit zu SQL, PDO, Tabellen, JSON-Dateien, OpenRefine oder xTree besitzen.
3. DM002 darf die Application Layer nicht tabellenorientiert strukturieren.
4. Die bestehende OpenRefine-Schnittstelle muss während WP1 kompatibel bleiben.
5. Der bestehende lokale JSON-Suchstore soll in WP1 weiterverwendet werden.
6. Die Migration erfolgt evolutionär und ohne vollständigen Rewrite.
7. Persistenz eines fachlich konsistenten Reconciliation-Zustands muss transaktional möglich sein.
8. Die Lösung muss mit MySQL 8.0.46 und MariaDB 10.4.32 kompatibel sein.
9. Die Architektur muss spätere externe Candidate-Discovery-Dienste unterstützen.
10. Die Begriffe `ReconciliationStore` und Repository müssen eindeutig getrennt bleiben.

---

## 3. Decision

Reconcilix führt eine explizite **Repository Boundary** zwischen Application Layer und Persistence Layer ein.

Die Domain Objects kennen weder Repository-Implementierungen noch Persistenztechnologien.

Repository-Interfaces werden im **Application Layer** definiert, weil sie Anwendungsfälle und Transaktionsgrenzen unterstützen. Die Domain Layer bleibt frei von Infrastrukturverträgen.

Die konkrete Persistenz wird in WP2 durch relationale Repository-Implementierungen gemäß DM002 realisiert.

### 3.1 Layer Boundary

```text
Transport Layer
OpenRefine Request / ReconciliationQuery
        │
        ▼
Application Layer
ReconciliationApplicationService
        │
        ├── erzeugt und koordiniert Domain Objects
        ├── ruft Candidate Discovery Ports auf
        └── verwendet Repository Interfaces
        │
        ▼
Domain Layer
SourceValue
InterpretationGraph
InterpretationNode
ReconciliationResult
CandidateItem
MatchDecision
        │
        ▼
Infrastructure Layer
Relational Repositories
MySQL / MariaDB
```

Externe Candidate Discovery liegt ebenfalls hinter einem Application Port:

```text
Application Service
→ CandidateDiscoveryGateway
→ Local Store Adapter / xTree Adapter / spätere externe APIs
```

Candidate Discovery ist nicht Teil der Repository Boundary.

### 3.2 Transport DTOs

`ReconciliationQuery` bleibt ein technisches Transportmodell.

Es wird nicht zum Domain Object erweitert.

Die Transformation erfolgt über einen Adapter oder eine Factory:

```text
ReconciliationQuery
→ SourceValueFactory / Request-to-Domain Mapper
→ SourceValue
```

OpenRefine-spezifische Felder bleiben außerhalb der Domain:

| Transportfeld | Behandlung |
|---|---|
| `qid` | technische Korrelation zur Response |
| `term` | wird zu `SourceValue.value` |
| `type` | Request-/Target-Kontext |
| `subVocabularyId` | Target-Vocabulary-Auswahl |
| `limit` | Discovery-/Response-Option |
| `properties` | erst nach ADR002 als `ContextItem` interpretierbar |

### 3.3 Repository Strategy

Es werden **keine öffentlichen Repositories pro Datenbanktabelle** eingeführt.

Die Application Layer arbeitet mit fachlich orientierten Repository-Interfaces.

Für die erste Umsetzung werden zwei Repository-Grenzen festgelegt:

#### `SourceValueRepository`

Verantwortlich für das SourceValue-Aggregat:

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

#### `ReconciliationResultRepository`

Verantwortlich für das Ergebnis-Aggregat:

```text
ReconciliationResult
├── CandidateItem[]
└── MatchDecision[]
```

Die konkrete Implementierung darf intern Data Mapper, Table Gateway oder spezialisierte Persistence Services verwenden. Diese bleiben innerhalb der Infrastructure Layer und werden nicht Teil der Application API.

### 3.4 Aggregate Boundaries

#### SourceValue Aggregate

Aggregate Root:

```text
SourceValue
```

Enthalten:

- `ContextItem`
- `InterpretationGraph`
- `InterpretationNode`

Invarianten:

- jeder `InterpretationGraph` gehört genau zu einem `SourceValue`,
- jede `InterpretationNode` gehört genau zu einem `InterpretationGraph`,
- Parent- und Child-Node gehören zum selben Graphen,
- die Parent-Relation ist azyklisch,
- `sourceFragment` entspricht dem durch `spanStart` und `spanEnd` bestimmten Ausschnitt,
- `level` folgt aus der Parent-Beziehung.

#### ReconciliationResult Aggregate

Aggregate Root:

```text
ReconciliationResult
```

Enthalten:

- `CandidateItem`
- `MatchDecision`

Invarianten:

- jeder `CandidateItem` gehört genau zu einem `ReconciliationResult`,
- eine Candidate-URI kommt pro `ReconciliationResult` höchstens einmal vor,
- eine `MatchDecision` kann nur einen Candidate desselben `ReconciliationResult` auswählen,
- Entscheidungen werden nicht überschrieben,
- `decisionStatus` kennzeichnet die fachliche Gültigkeit einer Entscheidung.

`ReconciliationResult` referenziert die zugehörige `InterpretationNode` über deren persistente Identität.

### 3.5 Transaction Boundaries

Der Application Service eröffnet Transaktionen nicht selbst über PDO oder SQL.

Stattdessen stellt die Infrastructure Layer eine Transaction Boundary bereit.

Für einen einfachen Reconciliation-Lauf gilt:

```text
1. SourceValue-Aggregat anlegen oder aktualisieren
2. ReconciliationResult-Aggregat speichern
3. Commit
```

Beide Speicheroperationen müssen in einer gemeinsamen Datenbanktransaktion ausgeführt werden können.

Eine teilweise Persistenz, bei der beispielsweise ein `SourceValue` gespeichert wird, das zugehörige `ReconciliationResult` jedoch fehlschlägt, ist zu vermeiden.

Die konkrete API des Transaction Service wird in WP2 festgelegt.

### 3.6 ID Strategy

Für WP1 und WP2 werden relationale technische IDs von der Datenbank erzeugt:

```text
BIGINT AUTO_INCREMENT
```

Domain Objects dürfen vor der Persistenz noch keine Datenbank-ID besitzen.

Daher müssen interne IDs in PHP nullable beziehungsweise als noch nicht vergeben repräsentierbar sein.

Fachliche Identifikatoren bleiben davon getrennt:

- `CandidateItem.uri`
- kontrollierte Klassifikations-URIs
- externe Vocabulary- und Source-System-URIs

Eine Einführung von UUIDs oder ULIDs erfolgt nur durch eine spätere ADR.

### 3.7 Read and Rehydration Strategy

Repositories müssen Domain Objects rehydrieren können, ohne dass diese SQL oder Tabellenstrukturen kennen.

Für WP2 ist mindestens erforderlich:

```text
SourceValueRepository::findById(...)
SourceValueRepository::save(...)

ReconciliationResultRepository::findById(...)
ReconciliationResultRepository::findByInterpretationNodeId(...)
ReconciliationResultRepository::save(...)
```

Die exakten Methodensignaturen werden von ED im Implementierungsdesign festgelegt und im Code Review gegen dieses ADR geprüft.

Lazy Loading wird nicht eingeführt.

Aggregate werden für den jeweiligen Anwendungsfall vollständig geladen.

### 3.8 Naming Boundary

Der bestehende Begriff

```text
ReconciliationStore
```

bleibt ausschließlich für den statischen JSON-basierten Candidate-Suchindex reserviert.

Relationale Persistenzkomponenten müssen den Begriff `Repository` tragen.

Beispiele:

```text
SourceValueRepository
ReconciliationResultRepository
PdoSourceValueRepository
PdoReconciliationResultRepository
```

Nicht zulässig sind Bezeichnungen wie:

```text
DatabaseReconciliationStore
PersistentReconciliationStore
```

da sie die bestehende Bedeutung des Store-Begriffs verwischen würden.

---

## 4. Implementation Guidance

### 4.1 WP1 — Domain Integration

WP1 implementiert:

- Domain Objects gemäß DM001,
- Request-to-Domain Mapping,
- einen Application Service für den Reconciliation-Anwendungsfall,
- Candidate Discovery Port,
- Legacy-Adapter für den bestehenden `ReconciliationStoreProvider`,
- Mapping von Legacy-`Candidate` zu Domain-`CandidateItem`,
- OpenRefine Output Adapter,
- Repository-Interfaces,
- Domain-Invarianten und Tests.

WP1 implementiert noch keine vollständige relationale Persistenz.

Repository-Implementierungen dürfen in WP1 durch In-Memory-Varianten ersetzt werden, sofern dies Tests und vertikale Integration erleichtert.

### 4.2 WP2 — Runtime and Persistence Integration

WP2 schafft zunächst die technische Laufzeit- und
Objektverdrahtungsbasis und integriert anschließend die relationale
Persistenz gemäß DM002.

WP2 implementiert:

- Datenbankverbindung,
- Transaktionsservice,
- relationale Repository-Implementierungen,
- Data Mapper,
- Schema gemäß DM002,
- Bootstrap- und Migrationsskripte,
- Rehydration,
- Integrations- und Persistenztests.

#### Umsetzungspakete (ergänzt durch PL, 2026-07-19)
- WP2-001 Runtime Architecture
- WP2-002 Composition Root
- WP2-003 Persistence Foundation
- WP2-004 Schema and Migrations
- WP2-005 Relational Repositories and Data Mapper
- WP2-006 Rehydration
- WP2-007 Transactional Integration



### 4.3 Evolutionary Migration

Der erste vertikale Pfad lautet:

```text
OpenRefine Request
→ ReconciliationQuery
→ ReconciliationApplicationService
→ SourceValue
→ InterpretationGraph
→ initial InterpretationNode
→ Legacy Candidate Discovery Adapter
→ ReconciliationResult
→ CandidateItem[]
→ OpenRefine Output Adapter
→ OpenRefine Response
```

Dabei gelten zunächst:

- keine Änderung am öffentlichen OpenRefine-Endpunkt,
- keine Änderung am JSON-Store-Format,
- keine Aktivierung externer Fallback-Provider,
- keine Übernahme von OpenRefine-Properties als `ContextItem` vor ADR002,
- keine Änderung des Preview-Pfads,
- keine Performanceoptimierung.

---

## 5. Consequences

### 5.1 Positive Consequences

- Domain und Persistenz sind klar getrennt.
- DM001 bleibt unabhängig von DM002.
- OpenRefine-spezifische Strukturen dringen nicht in die Domain ein.
- Der bestehende JSON-Store kann über Adapter weiterverwendet werden.
- Relationale Persistenz kann in WP2 ergänzt werden, ohne WP1 neu zu schneiden.
- Candidate Discovery und Persistenz bleiben getrennte Verantwortlichkeiten.
- Externe APIs können später als Candidate-Discovery-Adapter ergänzt werden.
- Tests können über In-Memory-Repositories aufgebaut werden.
- Transaktionsgrenzen orientieren sich an fachlichen Aggregaten.

### 5.2 Negative Consequences

- Es entstehen zusätzliche Interfaces, Mapper und Adapter.
- Der erste Migrationsschritt enthält zeitweise Legacy- und neue Modelle parallel.
- Aggregate müssen sorgfältig rehydriert werden.
- Zwei Repository-Grenzen erhöhen die Transaktionskoordination.
- Ohne Disziplin können interne Data Mapper versehentlich in die Application Layer gelangen.
- Das bestehende `Candidate`-Modell muss vorübergehend weitergeführt und adaptiert werden.

### 5.3 Risks

- zu große Aggregate können unnötig viele Daten laden,
- zu kleine Repositories könnten wieder tabellenorientierte Application APIs erzeugen,
- Transaktionsgrenzen könnten in Controller oder Domain Objects abrutschen,
- Legacy-Adapter könnten dauerhaft statt vorübergehend bestehen bleiben,
- der Begriff `ReconciliationStore` könnte weiterhin falsch verwendet werden.

Diese Risiken werden durch Code Review und Architekturreview kontrolliert.

---

## 6. Alternatives Considered

### 6.1 Active Record

Domain Objects speichern und laden sich selbst.

**Abgelehnt**, weil:

- Domain Objects dadurch SQL- und Tabellenwissen erhalten,
- DM001 von DM002 abhängig würde,
- Testbarkeit und spätere Infrastrukturwechsel erschwert würden.

### 6.2 Repository per Table

Für jede Tabelle aus DM002 wird ein öffentliches Repository eingeführt.

**Abgelehnt**, weil:

- DM002 die Application Layer strukturieren würde,
- fachliche Invarianten über mehrere Repositories verteilt würden,
- Transaktionen und Aggregate schwerer nachvollziehbar wären.

### 6.3 Single `ReconciliationRepository`

Ein Repository speichert und lädt den vollständigen Reconciliation-Verlauf.

**Nicht gewählt**, weil:

- das Aggregat für den aktuellen Scope zu breit wäre,
- getrennte Lebenszyklen von Interpretation und Resultaten schwerer abbildbar wären,
- die API zu einer universellen Sammelschnittstelle anwachsen könnte.

Die Option kann später erneut bewertet werden, falls ein explizites `ReconciliationTrace`- oder `ReconciliationRun`-Konzept eingeführt wird.

### 6.4 DAO / Table Gateway Only

Die Application Layer arbeitet direkt mit Data Access Objects oder Table Gateways.

**Abgelehnt**, weil:

- Persistenzdetails nach außen sichtbar würden,
- fachliche Aggregate und Invarianten nicht geschützt wären.

### 6.5 ORM / Unit of Work

Ein ORM verwaltet Mapping und Objektzustände.

**Zurückgestellt**, weil:

- aktuell kein Composer- oder Framework-Stack vorhanden ist,
- die Anzahl der Tabellen überschaubar ist,
- die Migration bewusst KISS folgt,
- ORM-spezifische Komplexität für WP1/WP2 keinen ausreichenden Mehrwert bietet.

Eine spätere Einführung erfordert eine eigene ADR.

---

## 7. Constraints

- PHP 8.2 lokal und PHP 8.3 auf dem Zielsystem müssen unterstützt werden.
- MySQL 8.0.46 und MariaDB 10.4.32 müssen unterstützt werden.
- Es wird zunächst kein Framework und kein ORM eingeführt.
- Der bestehende eigene Autoloader kann für WP1 beibehalten werden.
- Repository Interfaces dürfen keine PDO-, SQL- oder Tabellenbegriffe enthalten.
- URI-Vergleiche und -Persistenz richten sich nach DM002.
- `success` wird in der ersten Phase nicht für fachliche Steuerung verwendet.
- `MatchDecision` darf zunächst leer bleiben, wenn OpenRefine keine Entscheidung liefert.
- Tenant-Persistenz ist nicht Bestandteil von ADR001 und wird nicht vorweggenommen.
- Kontextübernahme wird in ADR002 entschieden.

---

## 8. Validation

Die Entscheidung gilt als erfolgreich umgesetzt, wenn:

1. `ReconciliationQuery` weiterhin ausschließlich Transport DTO ist.
2. Domain Objects keine OpenRefine-, xTree-, JSON-, SQL- oder PDO-Abhängigkeiten besitzen.
3. der bestehende lokale Store über einen Adapter weiterverwendet wird.
4. der Controller keine Domain-to-OpenRefine-Abbildung mehr selbst enthält.
5. Repository Interfaces im Application Layer liegen.
6. keine öffentliche Repository-Schnittstelle je DM002-Tabelle entsteht.
7. SourceValue- und ReconciliationResult-Aggregate gespeichert und rehydriert werden können.
8. ein vollständiger einfacher Reconciliation-Lauf transaktional persistiert werden kann.
9. Regressionstests den bisherigen OpenRefine-Response-Vertrag absichern.
10. die zehn bekannten Testwerte weiterhin fachlich vergleichbare Candidates liefern.

---

## 9. Follow-up Decisions

Folgende Entscheidungen sind nicht Bestandteil von ADR001:

- ADR002 — Context Transfer Architecture
- Candidate Discovery Port und Provider-Integration
- URI Strategy für alle internen kontrollierten Vokabulare
- externe Vector-Search-API
- lobid-gnd Connector
- Tenant-Persistenz
- Caching des lokalen JSON-Stores
- Preview-Datenquelle
- Framework-, Composer- oder ORM-Einführung
- explizites `ReconciliationRun`- oder `ReconciliationTrace`-Konzept

---

## 10. Decision Outcome

Mit Annahme dieses ADR gilt:

> Reconcilix trennt Transport, Application, Domain und Infrastructure durch explizite Ports, Adapter und fachlich orientierte Repository-Grenzen.

WP1 führt die Domain Boundary und die Repository-Interfaces ein.

WP2 implementiert die relationale Persistenz hinter dieser Boundary.

Die bestehende Anwendung wird dabei evolutionär migriert; öffentliche OpenRefine-Verträge und der lokale JSON-Suchstore bleiben im ersten Schritt erhalten.
