6e03093ce9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
194 lines
15 KiB
Markdown
194 lines
15 KiB
Markdown
# Architektur-Übersicht: Domain & Application
|
||
|
||
Dieses Dokument beschreibt die fachliche und anwendungsnahe Schicht des PDF-Umbenenners: das Modul `pdf-umbenenner-domain` und das Modul `pdf-umbenenner-application`. Es richtet sich an Entwickler, die in diesen beiden Modulen arbeiten, und soll als alleiniger Architekturkontext ausreichen – ergänzt durch die `CLAUDE.md` im Projektroot. Nicht enthalten sind Adapter-Implementierungen (Dateisystem, PDFBox, SQLite, HTTP-Clients); diese sind in `adapter-overview.md` beschrieben. GUI-spezifische Ports und deren Einbettung in den Konfigurationseditor sind in `gui-overview.md` dokumentiert.
|
||
|
||
---
|
||
|
||
## 1. Modulzweck
|
||
|
||
### `pdf-umbenenner-domain`
|
||
|
||
Enthält ausschließlich fachliche Kerntypen (Records, Enums, Sealed Interfaces) ohne jegliche Infrastrukturabhängigkeiten. Alle Typen modellieren den Problembereich und sind von anderen Modulen referenzierbar, ohne transitive Abhängigkeiten einzuschleppen.
|
||
|
||
### `pdf-umbenenner-application`
|
||
|
||
Definiert Use-Case-Orchestrierung sowie alle Inbound- und Outbound-Ports der hexagonalen Architektur. Enthält anwendungsnahe Dienste (KI-Antwort-Parsing, Pre-Check-Auswertung, Retry-Entscheidung) und Konfigurationsmodelle, aber keinerlei Infrastrukturcode (kein JDBC, kein PDFBox, kein HTTP-Client, kein JavaFX).
|
||
|
||
---
|
||
|
||
## 2. Paketstruktur
|
||
|
||
### `pdf-umbenenner-domain`
|
||
|
||
| Paket | Verantwortung |
|
||
|-------|---------------|
|
||
| `de.gecheckt.pdf.umbenenner.domain` | Wurzelpaket; enthält nur `package-info.java` |
|
||
| `de.gecheckt.pdf.umbenenner.domain.model` | Alle fachlichen Kerntypen: Records, Sealed Interfaces und Enums, die die Verarbeitungsdomäne beschreiben |
|
||
|
||
### `pdf-umbenenner-application`
|
||
|
||
| Paket | Verantwortung |
|
||
|-------|---------------|
|
||
| `de.gecheckt.pdf.umbenenner.application` | Wurzelpaket des Application-Moduls |
|
||
| `de.gecheckt.pdf.umbenenner.application.port.in` | Inbound-Ports (Use-Case-Interfaces) – Einstiegspunkte für den Aufrufer |
|
||
| `de.gecheckt.pdf.umbenenner.application.port.out` | Outbound-Ports – Verträge gegenüber Infrastruktur-Adaptern (Persistenz, Dateisystem, KI, Uhr, Logging) |
|
||
| `de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog` | Spezialisierter Outbound-Port für den Abruf verfügbarer KI-Modelle; ausschließlich im GUI-Pfad genutzt (siehe `gui-overview.md`) |
|
||
| `de.gecheckt.pdf.umbenenner.application.port.out.history` | Outbound-Port für lesende Historien-Abfragen aus dem Historien-Tab; bewusst getrennt von den bestehenden Repositories, um diese nicht mit GUI-spezifischen Methoden aufzublähen |
|
||
| `de.gecheckt.pdf.umbenenner.application.service` | Anwendungsnahe, zustandslose Dienste: KI-Antwort-Parsing, Pre-Check-Auswertung, Verarbeitungs-Pipeline, Retry-Entscheidung |
|
||
| `de.gecheckt.pdf.umbenenner.application.config` | Konfigurationsmodelle der Anwendungsschicht (`RuntimeConfiguration`, Provider-Konfiguration) |
|
||
| `de.gecheckt.pdf.umbenenner.application.config.startup` | Vollständiges Startup-Konfigurationsmodell (`StartConfiguration`) |
|
||
| `de.gecheckt.pdf.umbenenner.application.config.provider` | Modelle für KI-Provider-Konfiguration (Provider-Familie, Einzelkonfiguration, Multi-Provider) |
|
||
| `de.gecheckt.pdf.umbenenner.application.validation.editor` | Validierungslogik für den GUI-Konfigurationseditor (Findings, Report, API-Key-Auflösung); siehe `gui-overview.md` |
|
||
| `de.gecheckt.pdf.umbenenner.application.validation.technicaltest` | Technischer Selbsttest: Pfad-Checks, Korrekturpläne, Checkpoints; Details in `gui-overview.md` |
|
||
| `de.gecheckt.pdf.umbenenner.application.usecase` | Paket-Marker für Use-Case-Implementierungen |
|
||
|
||
---
|
||
|
||
## 3. Schlüsselklassen
|
||
|
||
### Domain-Modul
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentCandidate`**
|
||
Record für einen PDF-Kandidaten aus dem Quellordner. Enthält keinen `Path`, sondern einen opaken `SourceDocumentLocator`, damit die Domain frei von NIO-Typen bleibt.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint`**
|
||
Record mit einem SHA-256-Hex-String (64 Zeichen) als stabiler Dokumentidentität; Grundlage für Idempotenz und Persistenz-Lookup.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome`**
|
||
Sealed Interface mit sechs Implementierungen, die alle möglichen Ausgänge der Dokumentverarbeitung exhaustiv abbilden:
|
||
|
||
| Implementierung | Bedeutung |
|
||
|-----------------|-----------|
|
||
| `PreCheckPassed` | Vorprüfung bestanden, KI-Pfad freigegeben |
|
||
| `PreCheckFailed` | Deterministischer Inhaltsfehler vor KI-Aufruf |
|
||
| `TechnicalDocumentError` | Technischer Fehler ohne erneuten KI-Aufruf |
|
||
| `NamingProposalReady` | KI-Antwort gültig, Vorschlag liegt vor |
|
||
| `AiTechnicalFailure` | Transienter technischer Fehler beim KI-Aufruf |
|
||
| `AiFunctionalFailure` | Deterministischer fachlicher Fehler der KI-Antwort |
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus`**
|
||
Enum mit acht Zuständen. Dokumentiert Zustandsübergänge und Retry-Schwellen; fachliches Herzstück der Persistenz-Semantik.
|
||
|
||
| Status | Bedeutung |
|
||
|--------|-----------|
|
||
| `READY_FOR_AI` | Verarbeitbar, KI-Pfad noch nicht durchlaufen |
|
||
| `FAILED_RETRYABLE` | Verarbeitbar, transient fehlgeschlagen |
|
||
| `PROPOSAL_READY` | Eingangszustand für Dateinamensbildung und Zielkopie |
|
||
| `SUCCESS` | Terminaler Enderfolg – nur nach Zielkopie und konsistenter Persistenz |
|
||
| `FAILED_FINAL` | Terminal, wird nicht erneut fachlich verarbeitet |
|
||
| `SKIPPED_ALREADY_PROCESSED` | Historisierter Skip für `SUCCESS`-Dokumente |
|
||
| `SKIPPED_FINAL_FAILURE` | Historisierter Skip für `FAILED_FINAL`-Dokumente |
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.NamingProposal`**
|
||
Record mit aufgelöstem Datum, `DateSource`, validiertem Titel und KI-Begründung. Führende Quelle für die Zieldateinamensbildung.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.domain.model.BatchRunContext`**
|
||
Klasse mit Run-ID, Zeitstempel und optionalem Fingerabdruck-Filter; steuert den Umfang eines Batch-Laufs.
|
||
|
||
---
|
||
|
||
### Application-Modul
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration`**
|
||
Schmales Laufzeit-Record (`maxPages`, `maxRetriesTransient`, `aiContentSensitivity`). Wird von den Use Cases verwendet, enthält keine Pfade.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration`**
|
||
Vollständige typisierte Startup-Konfiguration; einziger Ort in der Anwendungsschicht, an dem `java.nio.file.Path` vorkommt. Wird vom `ConfigurationPort` geliefert und von Bootstrap ausgewertet.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingService`**
|
||
Statische Hilfsklasse: überführt ein Extraktionsergebnis über den Pre-Check in ein `DocumentProcessingOutcome`. Kompakte Pipeline-Klasse; guter Einstieg zum Verständnis der Verarbeitungslogik.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.service.AiResponseParser`**
|
||
Statischer Parser für KI-Antworten in `ParsedAiResponse`. Erzwingt reines JSON-Objekt; Validierungslogik liegt vollständig in der Anwendungsschicht.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.port.out.ProcessingAttempt`**
|
||
Record für einen Versuchshistorie-Eintrag; enthält u. a. Provider-Identifikator, Modellname, Prompt-Identifikator, aufgelöstes Datum und finalen Zieldateinamen.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord`**
|
||
Record für den Dokument-Stammsatz; enthält Gesamtstatus, Fehler- und Transientzähler sowie letzten Zielpfad.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.port.out.history.HistoryQuery`**
|
||
Record mit den Abfrageparametern für den Historien-Tab: optionaler Suchbegriff (`searchText`, Teilstring, case-insensitiv), optionaler Status-Filter (`statusFilter` als Enum-Name) und Limit der zurückzugebenden Zeilen (Standard `DEFAULT_LIMIT = 501`). Das Limit 501 ermöglicht der aufrufenden Schicht zu erkennen, ob mehr als 500 Treffer vorhanden sind.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.port.out.history.DocumentHistoryRow`**
|
||
Einzelzeile der Dokumentenliste im Historien-Tab. Felder: `fingerprint`, `overallStatus`, `sourceFileName`, `targetFileName` (null wenn noch kein Erfolg), `sourcePath`, `updatedAt` und `attemptCount`. Stammt aus `document_record` mit einem `COUNT`-Ausdruck über `processing_attempt`.
|
||
|
||
**`de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult`**
|
||
Versiegeltes Ergebnis-Interface für `PromptPort.savePrompt(String)`. Zulässige Ausprägungen: `Saved` (Erfolg, enthält absoluten Pfad), `WriteFailed` (I/O-Fehler beim Schreiben der Temp-Datei), `TargetDirectoryMissing` (Zielordner fehlt), `AtomicMoveFailed` (atomares Verschieben nicht möglich; kein stiller Fallback).
|
||
|
||
**Neue Use-Case-Implementierungen im Paket `de.gecheckt.pdf.umbenenner.application.usecase`**
|
||
|
||
| Klasse | Zweck |
|
||
|--------|-------|
|
||
| `DefaultHistoryOverviewUseCase` | Lädt die gefilterte Dokumentenübersicht über `HistoryQueryPort.loadOverview`; gibt `HistoryOverviewResult` mit Liste und `hasMore`-Flag zurück |
|
||
| `DefaultHistoryDetailsUseCase` | Lädt Stammsatz und alle Verarbeitungsversuche für einen Fingerprint über `HistoryQueryPort`; gibt `HistoryDetailsResult` zurück |
|
||
| `DefaultHistoryResetDocumentStatusUseCase` | Feldgenauer Status-Reset via `UnitOfWorkPort.TransactionOperations.resetDocumentStatusForRetry`; setzt `overall_status`, `content_error_count`, `transient_error_count` und `last_failure_instant` zurück; lässt die Versuchshistorie unangetastet |
|
||
| `DefaultDeleteDocumentHistoryUseCase` | Löscht Stammsatz und alle Verarbeitungsversuche vollständig und transaktional via `UnitOfWorkPort` |
|
||
| `DefaultPromptEditorUseCase` | Delegiert Laden, Speichern und Standard-Anlegen der Prompt-Datei an `PromptPort` und `ResourceCreationPort`; wird im GUI-Pfad über `GuiPromptEditorPort` angesteuert |
|
||
|
||
---
|
||
|
||
## 4. Inbound Ports
|
||
|
||
### `BatchRunProcessingUseCase`
|
||
|
||
```
|
||
de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase
|
||
```
|
||
|
||
Zentraler Use-Case-Einstiegspunkt für den gesamten Batch-Betrieb. Beschreibt den Anwendungszweck in einer einzigen Methode:
|
||
|
||
```java
|
||
BatchRunOutcome execute(BatchRunContext context);
|
||
```
|
||
|
||
Mögliche Ergebnisse:
|
||
|
||
| Ergebnis | Bedeutung |
|
||
|----------|-----------|
|
||
| `SUCCESS` | Lauf technisch ordnungsgemäß abgeschlossen |
|
||
| `LOCK_UNAVAILABLE` | Run-Lock konnte nicht erworben werden |
|
||
| `FAILURE` | Harter technischer Fehler beim Laufstart |
|
||
|
||
---
|
||
|
||
## 5. Outbound Ports
|
||
|
||
Alle Outbound-Ports liegen in `de.gecheckt.pdf.umbenenner.application.port.out` (bzw. dessen Unterpaket `modelcatalog`). Implementierungen befinden sich ausschließlich in `pdf-umbenenner-adapter-out`; Details dort sind in `adapter-overview.md` beschrieben.
|
||
|
||
| Interface | Zweck | Hauptmethode(n) |
|
||
|-----------|-------|-----------------|
|
||
| `SourceDocumentCandidatesPort` | Scannt Quellordner, liefert Kandidaten in deterministischer Reihenfolge | `List<SourceDocumentCandidate> loadCandidates()` |
|
||
| `FingerprintPort` | Berechnet SHA-256-Fingerabdruck eines Kandidaten | `FingerprintResult computeFingerprint(SourceDocumentCandidate)` |
|
||
| `PdfTextExtractionPort` | Extrahiert Text und Seitenanzahl aus einer PDF | `PdfExtractionResult extractTextAndPageCount(...)` |
|
||
| `AiInvocationPort` | Ruft den aktiven KI-Dienst auf; provider-neutral | `AiInvocationResult invoke(AiRequestRepresentation)` |
|
||
| `PromptPort` | Lädt das Prompt-Template aus der konfigurierten Quelle; speichert geänderten Inhalt atomar via `savePrompt(String)` – der Pfad stammt aus der Adapter-internen Konfiguration, nicht aus dem Port-Aufruf | `PromptLoadingResult loadPrompt()`, `PromptSaveResult savePrompt(String content)` |
|
||
| `TargetFileCopyPort` | Kopiert Quelldokument unter aufgelöstem Namen in den Zielordner (Temp + Rename) | `TargetFileCopyResult copyToTarget(...)` |
|
||
| `TargetFileRenamePort` | Atomare Umbenennung einer bereits kopierten Zieldatei (manuelle Korrektur) | `TargetFileRenameResult rename(...)` |
|
||
| `RunLockPort` | Exklusiver Lauf-Lock gegen parallele Instanzen | `acquire()` / `release()` |
|
||
| `PersistenceSchemaInitializationPort` | Idempotente Schema-Initialisierung der SQLite-Datenbank | `initializeSchema()` |
|
||
| `ClockPort` | Abstraktion des Systemtakts | `Instant now()` |
|
||
| `ConfigurationPort` | Lädt die typisierte Startup-Konfiguration | `StartConfiguration loadConfiguration()` |
|
||
| `ProcessingLogger` | Logging-Delegation; sensibles KI-Content-Logging über Flag gesteuert | `info/debug/warn/error/debugSensitiveAiContent(...)` |
|
||
| `AiModelCatalogPort` | Abruf verfügbarer Modelle vom Provider (nur GUI-Pfad, siehe `gui-overview.md`) | `ModelCatalogResult fetchAvailableModels(...)` |
|
||
| `PathCheckPort` | Lesende Pfad-Prüfung für den technischen Selbsttest | `isDirectoryReadable`, `isDirectoryWritableOrCreatable`, `isFileReadable`, `isSqlitePathUsable` |
|
||
| `ResourceCreationPort` | Schreibende Korrektur-Aktionen (Ordner anlegen, Prompt-Datei erzeugen, SQLite-Pfad vorbereiten) | `createDirectory`, `createPromptFile`, `prepareSqlitePath` |
|
||
| `ApiKeyResolutionPort` | Ermittelt API-Key-Herkunft pro Provider-Familie für die GUI-Validierung | `EffectiveApiKeyDescriptor resolve(...)` |
|
||
| `HistoryQueryPort` | Lesender Zugriff auf die Verarbeitungshistorie für den Historien-Tab; bewusst getrennt von den regulären Repositories | `List<DocumentHistoryRow> loadOverview(HistoryQuery)`, `Optional<DocumentRecord> findRecordByFingerprint(DocumentFingerprint)`, `List<ProcessingAttempt> findAttemptsByFingerprint(DocumentFingerprint)` |
|
||
|
||
> **Hinweis zu GUI-spezifischen Ports:** `AiModelCatalogPort`, `PathCheckPort`, `ResourceCreationPort`, `ApiKeyResolutionPort` und `HistoryQueryPort` werden ausschließlich im GUI-Pfad genutzt. Ihre Implementierungen und der Aufrufkontext sind in `gui-overview.md` bzw. `adapter-overview.md` beschrieben.
|
||
|
||
> **Hinweis zu `UnitOfWorkPort.TransactionOperations`:** Die innere Schnittstelle `TransactionOperations` wurde um die Methode `resetDocumentStatusForRetry(DocumentFingerprint)` erweitert. Diese setzt feldgenau `overall_status → READY_FOR_AI`, `content_error_count → 0`, `transient_error_count → 0` und `last_failure_instant → NULL`, ohne die Versuchshistorie zu berühren. Die Implementierung liegt in `SqliteUnitOfWorkAdapter`.
|
||
|
||
---
|
||
|
||
## 6. Einstiegspunkte für neue Entwickler
|
||
|
||
Die folgende Lesereihenfolge gibt den kürzesten Weg zum Gesamtverständnis:
|
||
|
||
1. **`de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase`** – beschreibt den gesamten Anwendungszweck in einer Methode.
|
||
2. **`de.gecheckt.pdf.umbenenner.domain.model.ProcessingStatus`** – fachliches Herzstück; dokumentiert Zustandsübergänge und Retry-Schwellen.
|
||
3. **`de.gecheckt.pdf.umbenenner.domain.model`** (gesamtes Paket) – gemeinsame Sprache aller Schichten; vollständig in wenigen Minuten lesbar.
|
||
4. **`de.gecheckt.pdf.umbenenner.application.service.DocumentProcessingService`** – kompakte Pipeline-Klasse; zeigt, wie Pre-Check und Ergebnis-Typen zusammenspielen.
|
||
5. **`de.gecheckt.pdf.umbenenner.application.port.out`** (gesamtes Paket) – vollständige Außengrenzen der Architektur; jeder Infrastrukturzugriff ist hier als Port definiert.
|