# ALIGNMENT_REVIEW_009

## 1. Scope

### Geprüfte Lieferung

- `WP2-007_Transaction_Boundary_and_Repository_Coordination`

### Geprüfte Implementierungsartefakte

- `README.md`
- `docs/architecture/ADR001-WP2-007_TRANSACTION_BOUNDARY_AND_REPOSITORY_COORDINATION_v2.md`
- `src/Application/Persistence/TransactionManager.php`
- `src/Application/Reconciliation/ReconciliationApplicationService.php`
- `src/Infrastructure/Database/PdoTransactionManager.php`
- `src/Infrastructure/Factory/RuntimeFactory.php`
- `src/Infrastructure/Composition/ReconciliationCompositionRoot.php`
- `tests/Persistence/PersistenceFoundationTest.php`
- `tests/Persistence/TransactionBoundaryTest.php`

### Architektur-Baseline

- `ADR002_SOURCE_DELIVERY_MODEL_V4.md`
- `DM001_Domain_Model_v0.5_FINAL.md`
- `DM002_Persistence_Model_v0.5_ED-LA.md`
- `ME001_Interpretation_Properties_and_Vocabulary_Matching_Patterns_v0.2_ALPHA.md`
- `ME002_Interpretation_Driven_Reconciliation_v0.6_DRAFT.md`

---

# 2. Executive Summary

WP2-007 setzt eine technisch saubere und gut getestete Transaction Boundary auf Application-Ebene um. Positiv sind insbesondere:

- eine Transaktion pro persistiertem Reconciliation-Use-Case,
- zentrale Koordination durch den `ReconciliationApplicationService`,
- keine Transaktionssteuerung in Repositorys oder Data Mappern,
- Commit erst nach erfolgreicher Ausführung beider Repository-Aufrufe,
- Rollback bei jeder Exception innerhalb einer aktiven Transaktion,
- unveränderte Weitergabe der ursprünglichen Exception,
- All-or-nothing-Konfiguration der Persistenzabhängigkeiten,
- weiterhin funktionsfähiger nichtpersistenter Laufzeitpfad,
- keine Einführung von ORM, Unit of Work, Identity Map oder Repository-zu-Repository-Abhängigkeiten.

Gegen die aktuelle Architektur-Baseline besteht jedoch ein wesentlicher struktureller Konflikt:

> WP2-007 behandelt `SourceValue` und `ReconciliationResult` als zwei getrennte Aggregate mit je einem Repository. DM001 v0.5 FINAL definiert dagegen `ReconciliationResult`, `CandidateItem` und `MatchDecision` als Bestandteile des `SourceValue` Aggregate.

Damit ist die Transaction Boundary technisch korrekt für die ältere Persistenzstruktur, aber nicht vollständig mit der aktuellen Aggregate-Definition aus DM001 v0.5 ausgerichtet.

Zusätzlich übernimmt WP2-007 mehrere bereits vorhandene Baseline-Abweichungen aus früheren Work Packages:

- `SourceValue` besitzt keine verpflichtende `sourceDeliveryId`,
- der Originalwert wird vor Anlage durch `trim()` verändert,
- `CandidateItem.entityType` wird aus der Erwartung des Requests statt aus dem tatsächlichen Candidate-Typ gebildet,
- `candidateProviderUri` wird nicht gesetzt,
- `targetVocabularyUri` fällt ersatzweise auf `preferredEntityTypeUri` zurück,
- `InterpretationProperty`, `VocabularyMatchingPattern` und `MatchDecision` sind im produktiven Flow noch nicht integriert.

## Gesamturteil

**Bewertung: 🟡 bedingt baseline-konform**

Die Transaktionsmechanik selbst ist stabil und architektonisch gut umgesetzt. Vor einer Einstufung als vollständig baseline-konform muss jedoch die Repository- und Aggregate-Koordination an DM001 v0.5 und ADR002 angepasst werden.

---

# 3. Methodik

Das Review unterscheidet drei Kategorien:

1. **Stabile Architekturentscheidung**  
   Die Lieferung setzt eine Baseline-Entscheidung korrekt um.

2. **Abweichung der Lieferung**  
   Eine Implementierungsentscheidung widerspricht der Baseline.

3. **Übernommene Altlast**  
   Die Abweichung wurde nicht durch WP2-007 neu eingeführt, wird durch das Work Package aber weiterverwendet oder technisch verfestigt.

Diese Unterscheidung ist für WP2-007 wichtig, weil das Work Package primär die Transaktionskoordination ergänzt und auf einer bereits vorhandenen Domain- und Repository-Struktur aufbaut.

---

# 4. Stabile Architekturentscheidungen

## 4.1 Transaction Boundary auf Application-Ebene

**Bewertung: 🟢 stabil**

Der `ReconciliationApplicationService` eröffnet und beendet die Transaktion. Das entspricht der Baseline-Regel, dass aggregateübergreifende fachliche Vollständigkeit durch einen koordinierenden Application Service an der Transaktionsgrenze sichergestellt wird.

Der Ablauf ist klar:

```text
TransactionManager.begin()
    ↓
Repository-Aufruf 1
    ↓
Repository-Aufruf 2
    ↓
TransactionManager.commit()
```

Im Fehlerfall:

```text
Exception
    ↓
TransactionManager.isActive()
    ↓
TransactionManager.rollback()
    ↓
ursprüngliche Exception unverändert weiterwerfen
```

Die Transaction Boundary ist damit weder im Controller noch im Repository oder Data Mapper angesiedelt.

## 4.2 TransactionManager als Application Port

**Bewertung: 🟢 stabil**

`TransactionManager` liegt im Application Layer und abstrahiert die technische Transaktionsimplementierung:

```php
interface TransactionManager
{
    public function begin(): void;
    public function commit(): void;
    public function rollback(): void;
    public function isActive(): bool;
}
```

`PdoTransactionManager` ist die Infrastrukturimplementierung. Dadurch bleibt der Application Service von PDO entkoppelt.

## 4.3 Repositorys verwalten keine Transaktionen

**Bewertung: 🟢 stabil**

Die Repositorys werden innerhalb einer bereits geöffneten Transaktion aufgerufen. Sie:

- beginnen keine Transaktion,
- committen nicht,
- rollen nicht zurück,
- kennen einander nicht.

Diese Trennung ist fachlich und technisch sinnvoll.

## 4.4 All-or-nothing-Konfiguration

**Bewertung: 🟢 stabil**

Der Konstruktor des Application Service akzeptiert die Persistenzabhängigkeiten nur vollständig:

- `TransactionManager`
- `SourceValueRepository`
- `ReconciliationResultRepository`

Eine Teilkonfiguration führt zu `InvalidArgumentException`.

Damit wird verhindert, dass der Service in einen unklaren Mischzustand zwischen persistenter und nichtpersistenter Verarbeitung gelangt.

## 4.5 Nichtpersistenter Runtime-Pfad bleibt erhalten

**Bewertung: 🟢 stabil**

Ist Persistenz deaktiviert, wird der bisherige In-Memory-Flow weiterhin ausgeführt. Die Composition Root verdrahtet Transaction Manager und Repositorys ausschließlich bei aktivierter Datenbankkonfiguration.

Das bewahrt die optionale Persistenz und hält den bestehenden Runtime-Modus funktionsfähig.

## 4.6 Gemeinsame PDO-Verbindung

**Bewertung: 🟢 stabil**

Composition Root, Transaction Manager und beide Repositorys verwenden dieselbe Verbindung aus der `DatabaseConnectionFactory`.

Nur dadurch umfasst die Transaktion tatsächlich sämtliche beteiligten SQL-Operationen.

## 4.7 Fehlerbehandlung

**Bewertung: 🟢 stabil, mit dokumentierter Trade-off-Entscheidung**

Bei einem Fehler wird ein aktiver Transaktionskontext zurückgerollt. Schlägt auch der Rollback fehl, bleibt die ursprüngliche Exception maßgeblich.

Das entspricht dem workpackage-internen ADR. Der Rollback-Fehler wird bewusst nicht an die Stelle der ursprünglichen Ursache gesetzt.

Für den späteren produktiven Betrieb sollte der Rollback-Fehler zusätzlich geloggt oder als `previous`/suppressed information beobachtbar gemacht werden. Das ist eine Betriebsanforderung, keine Domain-Abweichung.

## 4.8 Tests der Transaktionsreihenfolge

**Bewertung: 🟢 stabil**

`TransactionBoundaryTest.php` prüft unter anderem:

```text
begin
save-source
save-result
commit
```

sowie:

```text
begin
save-source
save-result
rollback
```

Zusätzlich werden geprüft:

- unveränderte Weitergabe der ursprünglichen Exception,
- inaktiver Transaktionszustand nach Commit und Rollback,
- technische ID der persistierten `InterpretationNode`,
- Zurückweisung einer partiellen Persistenzkonfiguration.

Die Tests sichern die zentrale technische Entscheidung des Work Packages angemessen ab.

---

# 5. Wesentliche Architekturabweichungen

## AR-009-01 — Repository-Struktur widerspricht der Aggregate-Grenze aus DM001 v0.5

**Schweregrad: hoch**  
**Status: durch WP2-007 verfestigte Altlast**

WP2-007 folgt dem Modell:

```text
SourceValue Aggregate
└── InterpretationGraph
    └── InterpretationNode

ReconciliationResult Aggregate
├── CandidateItem
└── MatchDecision
```

Daraus leitet die Lieferung zwei Repositorys ab:

- `SourceValueRepository`
- `ReconciliationResultRepository`

DM001 v0.5 FINAL definiert dagegen:

```text
SourceValue Aggregate
├── ContextItem
├── InterpretationGraph
├── InterpretationNode
├── ReconciliationResult
├── CandidateItem
└── MatchDecision
```

Damit sind `ReconciliationResult`, `CandidateItem` und `MatchDecision` keine eigenständigen Aggregate Roots, sondern interne Bestandteile des `SourceValue` Aggregate.

### Konsequenz

Die technische Zerlegung auf mehrere Mapper oder Tabellen ist zulässig. Ein separates Repository für einen internen Aggregate-Bestandteil ist jedoch mit der in DM001 definierten Repository-Verantwortung nicht konsistent.

Die Transaction Boundary koordiniert derzeit zwei Repositorys für Teile desselben fachlichen Aggregates. Damit wird eine technische Tabellen- beziehungsweise Persistenzgrenze fälschlich als Aggregate-Grenze behandelt.

### Erforderliche Klärung

Es bestehen zwei saubere Lösungswege:

**Variante A — Baseline beibehalten**

- `SourceValueRepository` persistiert das vollständige `SourceValue` Aggregate einschließlich Reconciliation Results, Candidates und Decisions.
- Interne Mapper dürfen weiterhin getrennt bleiben.
- Der Application Service koordiniert nur echte Aggregate Roots, beispielsweise `SourceDelivery` und `SourceValue`.

**Variante B — Domain Model ändern**

- `ReconciliationResult` wird ausdrücklich wieder als eigenständiges Aggregate Root definiert.
- DM001 und DM002 müssen diese Entscheidung fachlich begründen und konsistent aktualisieren.

Auf Basis der gegenwärtigen Baseline ist **Variante A** maßgeblich.

---

## AR-009-02 — ADR002-Provenienz fehlt im persistierten Use Case

**Schweregrad: hoch**  
**Status: übernommene Altlast**

ADR002 und DM001 verlangen:

```text
SourceSystem
    ↓
SourceDelivery
    ↓
SourceValue
```

Jeder `SourceValue` muss genau eine `sourceDeliveryId` referenzieren.

Der Application Service erzeugt den `SourceValue` jedoch ohne eine solche Referenz:

```php
SourceValue::create(
    value: trim($command->sourceValue),
    preferredEntityTypeUri: $command->preferredEntityTypeUri,
    sourceField: $command->sourceField,
    sourceRecordId: $command->sourceRecordId,
    language: $command->language,
);
```

WP2-007 persistiert damit einen `SourceValue`, dessen verpflichtender Delivery Context in der aktuellen Baseline nicht abgebildet ist.

### Konsequenz

Die erfolgreiche Transaktion garantiert zwar die technische Atomarität von `SourceValue` und `ReconciliationResult`, nicht aber die fachlich verpflichtende Provenienzkette.

### Erforderliche Änderung

Der persistente Use Case muss entweder:

- eine bestehende `SourceDelivery` referenzieren oder
- `SourceSystem`, `SourceDelivery` und mindestens einen `SourceValue` in einer fachlich vollständigen Transaktion koordinieren.

Die konkrete Variante hängt davon ab, ob die Lieferung vor dem Reconciliation-Aufruf bereits existiert.

---

## AR-009-03 — Original Value wird verändert

**Schweregrad: hoch**  
**Status: übernommene Altlast**

DM001 und DM002 definieren `SourceValue.value` als unveränderten Originalwert.

Der Application Service verwendet:

```php
value: trim($command->sourceValue)
```

Dadurch werden führende und nachgestellte Leerzeichen entfernt.

### Konsequenz

Der persistierte Wert ist nicht zwingend identisch mit dem gelieferten Eingangswert. Dies beeinträchtigt:

- Provenienznachweis,
- Reproduzierbarkeit,
- Span-Positionen,
- spätere Interpretation des Originalstrings.

### Erforderliche Änderung

Der Originalwert muss unverändert gespeichert werden. Eine normalisierte Form kann zusätzlich als abgeleiteter Interpretationswert geführt werden, darf aber den Originalwert nicht ersetzen.

---

## AR-009-04 — Technische Persistenz-ID steuert die Erzeugungsreihenfolge innerhalb eines fachlichen Aggregates

**Schweregrad: mittel bis hoch**  
**Status: Folge von AR-009-01**

WP2-007 speichert zunächst `SourceValue`, damit der Mapper der `InterpretationNode` eine technische ID zuweist. Erst danach wird `ReconciliationResult` erzeugt:

```text
SourceValueRepository.save()
    ↓
InterpretationNode erhält technische ID
    ↓
ReconciliationResult.create(interpretationNodeId)
```

Die Lieferung begründet dies mit der technischen Fremdschlüsselabhängigkeit.

### Bewertung

Die Reihenfolge ist für das vorhandene relationale Schema nachvollziehbar. Sie zeigt jedoch, dass die Domain-Objekterzeugung von einer durch Persistenz vergebenen technischen ID abhängt.

Das steht in Spannung zu:

- Persistence Ignorance,
- stabilen fachlichen Identitäten,
- der Definition des vollständigen `SourceValue` Aggregate.

### Empfehlung

Für Aggregate-interne Beziehungen sollte eine Identität bereits vor der technischen Speicherung verfügbar sein, beispielsweise durch:

- anwendungsseitig erzeugte stabile IDs,
- eine explizite interne Identity,
- oder eine Mapper-Strategie, die den vollständigen Objektgraphen in einem Repository-Aufruf persistiert.

Eine Datenbank-ID darf technische Referenz sein, sollte aber nicht bestimmen, wann ein fachliches Objekt überhaupt erzeugt werden kann.

---

## AR-009-05 — Candidate Entity Type wird aus der Erwartung statt aus dem Candidate übernommen

**Schweregrad: mittel**  
**Status: übernommene Altlast**

Bei der Erzeugung von `CandidateItem` wird gesetzt:

```php
entityTypeUri: $command->preferredEntityTypeUri
```

DM001 unterscheidet ausdrücklich:

- `SourceValue.preferredEntityType`: erwarteter Typ,
- `CandidateItem.entityType`: tatsächlich festgestellter Candidate-Typ.

### Konsequenz

Ein Candidate kann nicht als abweichender semantischer Typ dokumentiert werden. Das erschwert insbesondere das Matching Pattern `NOT_TARGET_ENTITY_TYPE`.

### Erforderliche Änderung

`DiscoveredCandidate` beziehungsweise der Gateway-Vertrag muss den tatsächlichen Entity Type liefern. Dieser Wert ist in `CandidateItem.entityType` zu übernehmen.

---

## AR-009-06 — Candidate Provider URI fehlt

**Schweregrad: mittel**  
**Status: übernommene Altlast**

DM001 und DM002 verlangen eine semantische Trennung zwischen:

- Herkunft des Eingangswerts über `SourceSystem`,
- Anbieter des Candidates über `candidateProviderUri`,
- fachlichem Zielvokabular über `targetVocabularyUri`.

Beim Aufbau des `CandidateItem` wird `candidateProviderUri` nicht gesetzt.

### Konsequenz

Die Herkunft eines konkreten Candidate-Vorschlags ist nicht vollständig nachvollziehbar, insbesondere bei:

- mehreren Providern,
- QLever,
- Lobid,
- lokalen Stores,
- aggregierter Candidate Discovery.

### Erforderliche Änderung

Der Discovery-Vertrag muss pro Candidate die Provider-URI liefern und der Application Service muss sie in das Domain Object übernehmen.

---

## AR-009-07 — Fallback für Target Vocabulary ist semantisch falsch

**Schweregrad: mittel**  
**Status: übernommene Altlast**

Der Application Service setzt:

```php
targetVocabularyUri:
    $command->subVocabularyId ?? $command->preferredEntityTypeUri
```

Ein Entity Type ist kein Target Vocabulary.

### Konsequenz

Fehlt `subVocabularyId`, wird eine semantisch falsche URI als Zielvokabular persistiert.

### Erforderliche Änderung

- `targetVocabularyUri` muss aus einer echten Vocabulary-Konfiguration stammen.
- Ist kein Target Vocabulary bestimmbar, muss der Request abgewiesen oder der Zustand explizit als nicht bestimmt modelliert werden.
- Ein Fallback auf `preferredEntityTypeUri` ist zu entfernen.

---

# 6. Methodische Abdeckung

## 6.1 ME001 — InterpretationProperty

**Bewertung: 🟡 noch nicht integriert**

Die produktive Verarbeitung erzeugt eine Root Node, vergibt jedoch keine `InterpretationProperty`.

Das ist keine durch WP2-007 verursachte Abweichung. WP2-007 erweitert ausschließlich die Persistenzkoordination. Für die Gesamtbaseline bleibt die Integration dennoch offen.

## 6.2 ME001 — VocabularyMatchingPattern

**Bewertung: 🟡 noch nicht integriert**

`ReconciliationResult` wird ohne `VocabularyMatchingPattern` erzeugt und gespeichert.

ME001 erlaubt `0..1`, sodass ein fehlendes Pattern formal zulässig ist. Für die vorgesehene methodische Pipeline ist die systematische Klassifikation jedoch noch ausstehend.

## 6.3 ME002 — Iterativer Interpretationsprozess

**Bewertung: 🟡 nur initialer Teil umgesetzt**

Der aktuelle Flow unterstützt:

```text
SourceValue
→ genau ein Whole-Value-InterpretationGraph
→ Root Node
→ Candidate Discovery
→ ReconciliationResult
```

Noch nicht umgesetzt sind unter anderem:

- mehrere interpretierbare Spans,
- weitere Interpretation Nodes,
- strategy-basierte Iteration,
- Evaluation des Ergebnisses,
- erneute Interpretation bei unzureichendem Ergebnis.

WP2-007 widerspricht ME002 nicht, bildet aber nur den bisherigen Minimalflow persistent ab.

## 6.4 MatchDecision

**Bewertung: 🟡 noch nicht Teil des Flows**

Obwohl Mapper und Repository-Struktur eine `MatchDecision` unterstützen können, erzeugt der Application Service keine Entscheidung.

Dies ist für einen Candidate-Discovery-Schritt nachvollziehbar. Der vollständige ME002-Ablauf endet jedoch mit einer Match Decision.

---

# 7. Testbewertung

## 7.1 Gut abgedeckt

Die Tests prüfen:

- korrekte Erfolgsreihenfolge,
- Rollback bei Fehler des zweiten Repositorys,
- aktive und inaktive Transaktionszustände,
- unveränderte Exception-Weitergabe,
- All-or-nothing-Konfiguration,
- Nutzung der nach Persistierung vergebenen Node-ID,
- Wiederverwendung derselben PDO-Verbindung.

## 7.2 Fehlende beziehungsweise empfohlene Tests

Folgende Szenarien sollten ergänzt werden:

1. Fehler im `SourceValueRepository` führt zu Rollback.
2. Fehler bei `begin()` führt nicht zu einem unzulässigen Rollback-Versuch.
3. Fehler bei `commit()` führt bei weiterhin aktiver Transaktion zu Rollback.
4. Rollback-Fehler bewahrt die ursprüngliche Exception und wird beobachtbar protokolliert.
5. Nichtpersistenter Flow verwendet keinerlei Repository- oder Transaction-Operation.
6. Persistierter `SourceValue.value` entspricht bytegenau dem gelieferten Originalwert.
7. Persistierter `SourceValue` enthält eine gültige `sourceDeliveryId`.
8. Candidate Entity Type und Candidate Provider werden aus dem Discovery-Ergebnis übernommen.
9. Eine Transaktion hinterlässt bei Fehlern keine partielle Persistenz.
10. Ein echter Datenbank-Integrationstest verifiziert Commit und Rollback über beide Persistenzbereiche.

---

# 8. Baseline Alignment

| Baseline-Dokument | Bewertung | Begründung |
|---|---:|---|
| ADR002 | 🔴 | `SourceDelivery` und verpflichtende `sourceDeliveryId` fehlen im persistierten Use Case |
| DM001 | 🔴 | `ReconciliationResult` wird als separates Aggregate/Repository behandelt; mehrere Domain-Attribute sind nicht baseline-konform |
| DM002 | 🟡 | Transaction Boundary und atomare Koordination sind korrekt; Persistenzabhängigkeiten und Provenienzmodell sind jedoch nicht vollständig ausgerichtet |
| ME001 | 🟡 | Keine direkte Verletzung, aber Interpretation Properties und Matching Patterns bleiben ungenutzt |
| ME002 | 🟡 | Initialer persistenter Flow ist vorhanden; iterative Interpretation und Match Decision fehlen noch |

---

# 9. Priorisierte Maßnahmen

## Priorität 1 — Aggregate- und Repository-Modell entscheiden

Die Diskrepanz zwischen WP2-007 und DM001 v0.5 muss vor weiterer Persistenzentwicklung aufgelöst werden.

Empfohlene Zielrichtung:

```text
SourceValueRepository
    persistiert das vollständige SourceValue Aggregate
```

Separate Mapper bleiben möglich. Ein separates `ReconciliationResultRepository` wäre dann kein öffentliches Repository eines Aggregate Roots.

## Priorität 2 — ADR002 in Runtime und Persistenzfluss integrieren

Ein persistierter `SourceValue` benötigt zwingend eine `sourceDeliveryId`.

Zu klären ist:

- wird `SourceDelivery` vor dem Reconciliation Request angelegt,
- oder erzeugt ein koordinierender Use Case `SourceDelivery` und `SourceValue` gemeinsam?

## Priorität 3 — Originalwert unverändert erhalten

`trim()` darf nicht auf den gespeicherten Originalwert angewandt werden.

## Priorität 4 — Candidate-Semantik korrigieren

- tatsächlichen Candidate Entity Type übernehmen,
- `candidateProviderUri` persistieren,
- Target Vocabulary korrekt bestimmen.

## Priorität 5 — methodische Objekte in Folge-Work-Packages integrieren

- `InterpretationProperty`,
- `VocabularyMatchingPattern`,
- `MatchDecision`,
- iterative Interpretation gemäß ME002.

---

# 10. Abschlussbewertung

WP2-007 enthält eine **gute technische Umsetzung der Transaktionssteuerung**. Die folgenden Entscheidungen können als stabil gelten:

- Transaction Boundary im Application Service,
- Transaction Manager als Port,
- PDO-Implementierung im Infrastructure Layer,
- Repositorys ohne eigene Transaktionssteuerung,
- eine gemeinsame Verbindung,
- Commit nach vollständigem Erfolg,
- Rollback bei Fehler,
- keine Unit of Work,
- klare Tests der Operationsreihenfolge.

Die Lieferung kann jedoch **nicht uneingeschränkt als alignment-konform zur aktuellen Baseline freigegeben werden**, weil sie auf einer älteren Aggregate- und Provenienzstruktur aufsetzt.

## Freigabeempfehlung

**Technische Transaktionsmechanik: freigeben.**

**Gesamtes Work Package als Baseline-Implementierung: nur unter Auflage freigeben.**

Die Auflage besteht darin, die in diesem Review dokumentierten Abweichungen — insbesondere Aggregate-Grenze und Source-Delivery-Provenienz — in einem expliziten Alignment-Work-Package zu beheben oder durch eine aktualisierte Architekturentscheidung neu zu legitimieren.
