6e03093ce9
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
357 lines
19 KiB
Markdown
357 lines
19 KiB
Markdown
# Architektur-Übersicht: Adapter-Out, CLI & Bootstrap
|
||
|
||
Diese Datei beschreibt die drei Module `pdf-umbenenner-adapter-out`, `pdf-umbenenner-adapter-in-cli`
|
||
und `pdf-umbenenner-bootstrap`: ihren Zweck, ihre Paketstruktur, die wichtigsten Klassen und die
|
||
Verdrahtungslogik beim Programmstart. Sie richtet sich an Entwickler, die in einem dieser Module
|
||
arbeiten wollen und noch keinen Überblick über das Projekt haben. Domain- und Application-Schicht
|
||
(Port-Verträge, fachliche Domänenobjekte, Use-Case-Interfaces) sind nicht Gegenstand dieses
|
||
Dokuments – sie sind in `docs/architecture/domain-overview.md` beschrieben. GUI-interne Ports und
|
||
die Struktur des GUI-Adapters finden sich in `docs/architecture/gui-overview.md`. Die hexagonale
|
||
Abhängigkeitsrichtung ist strikt: Adapter kennen Domain und Application, nicht umgekehrt. Adapter
|
||
dürfen außerdem nicht direkt voneinander abhängen.
|
||
|
||
---
|
||
|
||
## 1. Modulzweck
|
||
|
||
### pdf-umbenenner-adapter-out
|
||
|
||
Enthält alle Outbound-Adapter-Implementierungen, also die konkreten technischen Lösungen für
|
||
sämtliche Outbound-Ports der Application. Dazu gehören: Dateisystemzugriff, PDF-Textextraktion
|
||
via PDFBox, SQLite-Persistenz (Schema, Repositories, Unit of Work), HTTP-Clients für zwei
|
||
KI-Provider-Familien (OpenAI-kompatibel und Anthropic nativ), Properties-Konfiguration inklusive
|
||
Legacy-Migration, dateibasierter Run-Lock sowie Systemuhr und SHA-256-Fingerprint.
|
||
|
||
### pdf-umbenenner-adapter-in-cli
|
||
|
||
Schlanker Inbound-Adapter für den kopflosen Batch-Betrieb. Enthält genau eine Klasse
|
||
(`SchedulerBatchCommand`), die den CLI-Einstiegspunkt bildet und ausschließlich über das
|
||
Inbound-Port-Interface an die Application delegiert. Keine eigene Fachlogik.
|
||
|
||
### pdf-umbenenner-bootstrap
|
||
|
||
Composition Root der Anwendung. Verantwortlich für: CLI-Argument-Parsing,
|
||
Konfigurationsauflösung und -validierung, Aufbau des vollständigen Objektgraphen (manuell, ohne
|
||
DI-Framework), Auswahl der aktiven KI-Adapter-Implementierung, Dispatch auf GUI- oder
|
||
Headless-Pfad sowie Exit-Code-Ableitung. Bootstrap ist die einzige Stelle, an der alle Module
|
||
zusammengeführt werden.
|
||
|
||
---
|
||
|
||
## 2. Paketstruktur
|
||
|
||
### pdf-umbenenner-adapter-out
|
||
|
||
Wurzelpaket: `de.gecheckt.pdf.umbenenner.adapter.out`
|
||
|
||
| Unterpaket | Inhalt |
|
||
|-------------------------|-------------------------------------------------------------------------------------|
|
||
| `.ai` | HTTP-Adapter für OpenAI-kompatible Schnittstelle und Anthropic Messages API |
|
||
| `.clock` | Systemuhr-Adapter (`Instant.now()`) |
|
||
| `.configuration` | Properties-Laden, Multi-Provider-Parsing/-Validierung, Legacy-Migration |
|
||
| `.fingerprint` | SHA-256-Inhalts-Fingerprint |
|
||
| `.lock` | Dateibasierter Run-Lock |
|
||
| `.modelcatalog` | HTTP-Modellabruf für den GUI-Konfigurationseditor |
|
||
| `.pathcheck` | Pfadprüfung für den GUI-Editor |
|
||
| `.pdfextraction` | PDFBox-3.x-Adapter: Textextraktion und Seitenanzahl |
|
||
| `.prompt` | Prompt-Template-Lader |
|
||
| `.resourcecreation` | Anlegen von Ordnern und Dateien (korrigierende technische Tests) |
|
||
| `.sourcedocument` | Quellordner-Scanner (nicht rekursiv) |
|
||
| `.sqlite` | Schema-Initialisierung, Repositories, Unit of Work |
|
||
| `.targetcopy` | Zielkopie via Temp-Datei und atomarem Move |
|
||
| `.targetfolder` | Kollisionsfreier Zieldateiname, Umbenennung bestehender Zieldateien |
|
||
| `.validation` | API-Key-Auflösung aus Umgebungsvariablen (GUI-Editor) |
|
||
| `.bootstrap.validation` | `StartConfiguration`-Validierung vor Prozessstart |
|
||
|
||
### pdf-umbenenner-adapter-in-cli
|
||
|
||
Wurzelpaket: `de.gecheckt.pdf.umbenenner.adapter.in.cli`
|
||
|
||
Enthält ausschließlich `SchedulerBatchCommand` sowie die zugehörige `package-info.java`.
|
||
|
||
### pdf-umbenenner-bootstrap
|
||
|
||
Wurzelpaket: `de.gecheckt.pdf.umbenenner.bootstrap`
|
||
|
||
| Unterpaket | Inhalt |
|
||
|---------------------|-------------------------------------------------------------------------------------------------------|
|
||
| *(Wurzel)* | `PdfUmbenennerApplication` (main), `BootstrapRunner`, `AiProviderSelector` |
|
||
| `.adapter` | Bootstrap-interne Adapter: `Log4jProcessingLogger`, `GuiConfigurationPropertiesWriter`, `AiModelCatalogDispatcher` |
|
||
| `.singleinstance` | `SingleInstanceGuard` – Einzelinstanz-Schutz via Loopback-ServerSocket |
|
||
| `.startup` | `StartupMode`, `StartupArguments`, `CliArgumentParser` |
|
||
|
||
---
|
||
|
||
## 3. Schlüsselklassen
|
||
|
||
Die folgenden Klassen sind für das Verständnis der drei Module zentral. FQN-Kürzel: `...` steht
|
||
jeweils für das Wurzelpaket des Moduls.
|
||
|
||
### Adapter-Out
|
||
|
||
#### KI-Adapter
|
||
|
||
- **`...ai.OpenAiHttpAdapter`** – implementiert `AiInvocationPort` für OpenAI-kompatible Endpunkte.
|
||
POST `{baseUrl}/v1/chat/completions`, Bearer-Authentifizierung, extrahiert
|
||
`choices[0].message.content`, klassifiziert HTTP-Fehler und Timeouts als
|
||
`AiInvocationTechnicalFailure`.
|
||
|
||
- **`...ai.AnthropicClaudeHttpAdapter`** – implementiert `AiInvocationPort` für die native
|
||
Anthropic Messages API. POST `/v1/messages`, Header `x-api-key` und `anthropic-version`,
|
||
konkateniert `text`-Content-Blöcke aus dem Antwort-Array.
|
||
|
||
Beide Adapter liefern denselben Domain-Typ (`NamingProposal`) und enthalten keinerlei
|
||
provider-spezifische Typen in öffentlichen Signaturen. Welche Implementierung aktiv ist, entscheidet
|
||
ausschließlich der Bootstrap (→ `AiProviderSelector`).
|
||
|
||
#### Modell-Katalog (GUI)
|
||
|
||
- **`...modelcatalog.ClaudeModelCatalogAdapter`** – `AiModelCatalogPort` für Claude,
|
||
GET `/v1/models` mit `x-api-key`.
|
||
|
||
- **`...modelcatalog.OpenAiCompatibleModelCatalogAdapter`** – `AiModelCatalogPort` für
|
||
OpenAI-kompatibel, GET `/v1/models` mit Bearer.
|
||
|
||
#### PDF-Extraktion
|
||
|
||
- **`...pdfextraction.PdfTextExtractionPortAdapter`** – PDFBox-3.x-Adapter. Alle technischen
|
||
Fehler werden als `PdfExtractionTechnicalError` zurückgegeben; es werden keine Exceptions
|
||
propagiert.
|
||
|
||
#### SQLite
|
||
|
||
- **`...sqlite.SqliteSchemaInitializationAdapter`** – Flyway-basierte Schema-Initialisierung
|
||
mit `V1__initial_schema.sql`. Drei-Fall-Strategie: leere Datenbank (Flyway führt das Skript
|
||
vollständig aus), bestehender Datenbestand ohne Flyway-History (Schema-Prüfung, datiertes
|
||
Backup, dann Baseline-Eintrag ohne Skriptausführung), regulärer Folgestart mit Flyway-History
|
||
(idempotenter Lauf). Foreign-Key-Durchsetzung via `SQLiteConfig.enforceForeignKeys(true)` auf
|
||
DataSource-Ebene, sodass jede neue Verbindung automatisch `PRAGMA foreign_keys = ON` erhält.
|
||
|
||
- **`...sqlite.SqliteUnitOfWorkAdapter`** – implementiert `UnitOfWorkPort`. Setzt
|
||
`autoCommit=false`, führt atomare Commits durch, rollt bei Fehlern zurück. Die innere
|
||
`TransactionOperations`-Implementierung wurde um `resetDocumentStatusForRetry(DocumentFingerprint)`
|
||
erweitert: setzt feldgenau `overall_status = 'READY_FOR_AI'`, `content_error_count = 0`,
|
||
`transient_error_count = 0`, `last_failure_instant = NULL`; alle anderen Felder und alle
|
||
`processing_attempt`-Einträge bleiben unangetastet.
|
||
|
||
- **`...sqlite.SqliteDocumentRecordRepositoryAdapter`** – Stammsatz pro SHA-256-Fingerprint
|
||
(Gesamtstatus, Fehlerzähler, Zieldateiname usw.).
|
||
|
||
- **`...sqlite.SqliteProcessingAttemptRepositoryAdapter`** – Versuchshistorie, referenziert
|
||
über Fingerprint. Enthält u. a. Provider-Identifikator, Modellname, Prompt-Identifikator,
|
||
KI-Rohantwort und finalen Zieldateinamen.
|
||
|
||
- **`...sqlite.SqliteHistoryQueryAdapter`** – implementiert `HistoryQueryPort`. Kapselt alle
|
||
lesenden Datenbankoperationen für den Historien-Tab: Übersicht (`loadOverview` mit
|
||
Sortierung `updated_at DESC, fingerprint ASC`, LIMIT 501-Strategie, case-insensitive
|
||
Freitextsuche via `LOWER()` mit Sonderzeichen-Escape für `%` und `_`), Stammsatz-Lookup
|
||
(`findRecordByFingerprint`) und Versuchshistorie (`findAttemptsByFingerprint`).
|
||
|
||
#### Konfiguration
|
||
|
||
- **`...configuration.PropertiesConfigurationPortAdapter`** – implementiert `ConfigurationPort`.
|
||
Lädt `config/application.properties` (oder einen `--config`-Override), parst via
|
||
`MultiProviderConfigurationParser`, löst API-Keys aus Umgebungsvariablen
|
||
(`OPENAI_COMPATIBLE_API_KEY`, `ANTHROPIC_API_KEY`).
|
||
|
||
- **`...configuration.LegacyConfigurationMigrator`** – erkennt alte Flat-Key-Konfigurationen
|
||
(Schlüssel wie `api.baseUrl`, `api.model`), legt eine `.bak`-Sicherung an und überführt den
|
||
Inhalt in das aktuelle Multi-Provider-Schema.
|
||
|
||
#### Prompt-Adapter
|
||
|
||
- **`...prompt.FilesystemPromptPortAdapter`** – implementiert `PromptPort`. Lädt das
|
||
Prompt-Template aus einer externen Datei und leitet den Identifikator aus dem Dateinamen ab.
|
||
Die neue Methode `savePrompt(String content)` schreibt den Inhalt atomar: temporäre Datei
|
||
im selben Verzeichnis anlegen (gleiche Partition), Inhalt in UTF-8 schreiben, dann
|
||
`ATOMIC_MOVE` zur Zieldatei. Kein stiller Fallback bei `AtomicMoveNotSupportedException`.
|
||
Der Pfad stammt aus der Adapter-internen Konfiguration, nicht aus dem Port-Aufruf.
|
||
|
||
#### Laufzeitinfrastruktur
|
||
|
||
- **`...lock.FilesystemRunLockPortAdapter`** – Lock-Datei mit PID-Inhalt. Wirft
|
||
`RunLockUnavailableException`, wenn die Datei bereits vorhanden ist. Release löscht die Datei
|
||
(best-effort).
|
||
|
||
- **`...clock.SystemClockAdapter`** – delegiert an `Instant.now()`.
|
||
|
||
- **`...fingerprint.Sha256FingerprintAdapter`** – SHA-256 über den Rohdatei-Inhalt. Fehler als
|
||
`FingerprintTechnicalError`.
|
||
|
||
#### Zieldatei
|
||
|
||
- **`...targetcopy.FilesystemTargetFileCopyAdapter`** – kopiert die Quelldatei zunächst in eine
|
||
`.tmp`-Datei, dann atomarer Move (Fallback: Standard-Move). Die Quelldatei wird in keinem Fall
|
||
verändert.
|
||
|
||
- **`...targetfolder.FilesystemTargetFolderAdapter`** – ermittelt einen kollisionsfreien
|
||
Zieldateinamen mit `(1)`, `(2)`-Suffix. Erkennt inhaltsidentische Duplikate via SHA-256.
|
||
|
||
#### Validierung vor Prozessstart
|
||
|
||
- **`...bootstrap.validation.StartConfigurationValidator`** – validiert die geladene
|
||
`StartConfiguration` auf Pflichtfelder, Wertebereiche, URI-Syntax und Pfadbedingungen.
|
||
Wird im Bootstrap-Headless-Pfad unmittelbar nach dem Laden der Konfiguration aufgerufen.
|
||
|
||
---
|
||
|
||
### Adapter-In-CLI
|
||
|
||
- **`...adapter.in.cli.SchedulerBatchCommand`** – einziger Inbound-Adapter für den Headless-Betrieb.
|
||
Nimmt einen `BatchRunContext` entgegen, delegiert an `BatchRunProcessingUseCase.execute()` und
|
||
gibt `BatchRunOutcome` zurück. Enthält keine eigene Fachlogik; die Verdrahtung mit dem
|
||
Use-Case-Interface erfolgt ausschließlich im Bootstrap.
|
||
|
||
---
|
||
|
||
### Bootstrap
|
||
|
||
- **`...bootstrap.PdfUmbenennerApplication`** – `main`-Methode. Parst CLI-Argumente via
|
||
`CliArgumentParser`, bricht bei ungültiger Verwendung mit Exit-Code 1 ab, delegiert an
|
||
`BootstrapRunner.run()` und ruft abschließend `System.exit()` mit dem zurückgegebenen Code auf.
|
||
|
||
- **`...bootstrap.BootstrapRunner`** – Herzstück der Verdrahtung. Baut den Objektgraph für
|
||
Headless- und GUI-Pfad, dispatcht über `StartupMode`, enthält `buildProductionBatchUseCase()`
|
||
und `runHeadlessBatch()` als zentrale Kompositionsmethoden, liefert den Exit-Code zurück.
|
||
|
||
- **`...bootstrap.AiProviderSelector`** – einzige Stelle, an der `AiProviderFamily` auf eine
|
||
konkrete `AiInvocationPort`-Implementierung abgebildet wird:
|
||
`OPENAI_COMPATIBLE` → `OpenAiHttpAdapter`, `CLAUDE` → `AnthropicClaudeHttpAdapter`.
|
||
|
||
- **`...bootstrap.startup.CliArgumentParser`** – parst `--headless` und `--config <Pfad>` zu einem
|
||
typsicheren `StartupArgumentsParseResult` (sealed: `Valid` / `Invalid`).
|
||
|
||
- **`...bootstrap.singleinstance.SingleInstanceGuard`** – bindet einen Loopback-ServerSocket auf
|
||
Port 47832. Wirft `AnotherInstanceRunningException`, wenn der Port bereits belegt ist. Ein
|
||
Shutdown-Hook gibt den Socket frei.
|
||
|
||
- **`...bootstrap.adapter.AiModelCatalogDispatcher`** – Bootstrap-interner Dispatcher für die GUI.
|
||
Routet `AiModelCatalogPort`-Aufrufe anhand des `providerIdentifier` an den Claude- oder
|
||
OpenAI-kompatiblen Modell-Katalog-Adapter. Thread-safe.
|
||
|
||
- **`...bootstrap.ApplicationVersionProvider`** – statische Hilfsklasse ohne Zustand. Liest
|
||
`Implementation-Version` aus dem Paket-Manifest via `getClass().getPackage().getImplementationVersion()`.
|
||
Fallback `"dev"` bei IDE-Start und ungepacktem Betrieb (kein Manifest-Eintrag vorhanden).
|
||
Der aufgelöste Wert wird im GUI-Pfad in `GuiStartupContext.applicationVersion` eingesetzt.
|
||
|
||
- **`...bootstrap.adapter.Log4jProcessingLogger`** – implementiert `ProcessingLogger` auf Basis
|
||
von Log4j2. Unterdrückt sensitive KI-Inhalte, wenn `AiContentSensitivity.PROTECT_SENSITIVE_CONTENT`
|
||
gesetzt ist.
|
||
|
||
- **`...bootstrap.adapter.GuiConfigurationPropertiesWriter`** – schreibt die im GUI-Editor
|
||
bearbeitete Konfiguration als normalisierte `application.properties` zurück auf das Dateisystem.
|
||
|
||
---
|
||
|
||
## 4. Verdrahtungslogik in Bootstrap
|
||
|
||
Die folgende Sequenz beschreibt den Ablauf von `main()` bis zum Start des eigentlichen Adapters.
|
||
Der Objektgraph wird ausschließlich durch manuelle `new`-Aufrufe aufgebaut; es wird kein
|
||
DI-Framework verwendet.
|
||
|
||
**Argument-Parsing**
|
||
- `PdfUmbenennerApplication.main()` → `CliArgumentParser.parse(args)`
|
||
- Ergebnis `Invalid` → Exit-Code 1, keine weiteren Schritte
|
||
|
||
**Einzelinstanz-Schutz**
|
||
- `BootstrapRunner.run()` → `SingleInstanceGuard.acquire()`
|
||
- `AnotherInstanceRunningException` → Exit-Code 1; im GUI-Modus zusätzlich ein Swing-Warndialog
|
||
|
||
**Modus-Dispatch**
|
||
- `BootstrapRunner.run()` wertet `startupArguments.mode()` aus:
|
||
- `HEADLESS` → `runHeadlessBatch()`
|
||
- `GUI` → `startGuiMode()`
|
||
|
||
**Konfigurationsauflösung (Headless-Pfad)**
|
||
- Prüfung, ob `--config`-Datei existiert (Fehler → Exit-Code 1)
|
||
- `LegacyConfigurationMigrator.migrateIfLegacy()` bei erkannter Legacy-Form
|
||
- `PropertiesConfigurationPortAdapter` lädt und parst die Properties
|
||
- `StartConfigurationValidator` validiert die geladene `StartConfiguration`
|
||
- Validierungsfehler → Exit-Code 1
|
||
|
||
**KI-Provider-Auswahl**
|
||
- Innerhalb von `buildProductionBatchUseCase()`:
|
||
`multiProviderConfiguration().activeProviderFamily()` → `AiProviderSelector.select(family, providerConfig)`
|
||
- Ergebnis: genau eine `AiInvocationPort`-Instanz
|
||
|
||
**Objektgraph-Aufbau (Headless)**
|
||
- Erzeugte Instanzen (Reihenfolge nach Abhängigkeit): `Sha256FingerprintAdapter`,
|
||
`SqliteDocumentRecordRepositoryAdapter`, `SqliteProcessingAttemptRepositoryAdapter`,
|
||
`SqliteUnitOfWorkAdapter`, `FilesystemTargetFolderAdapter`, `FilesystemTargetFileCopyAdapter`,
|
||
`FilesystemPromptPortAdapter`, `SystemClockAdapter`, `SourceDocumentCandidatesPortAdapter`,
|
||
`PdfTextExtractionPortAdapter`, `Log4jProcessingLogger`
|
||
- Application-Services (`DocumentProcessingCoordinator`, `AiResponseValidator`,
|
||
`AiNamingService`) werden verdrahtet und in `DefaultBatchRunProcessingUseCase` eingebettet
|
||
|
||
**CLI-Adapter**
|
||
- `BootstrapRunner` erzeugt `SchedulerBatchCommand` mit dem fertigen `BatchRunProcessingUseCase`
|
||
|
||
**Exit-Code-Ableitung**
|
||
- `BatchRunOutcome` → 0 (Lauf technisch erfolgreich) oder 1 (harter Bootstrap-/Konfigurationsfehler)
|
||
- `PdfUmbenennerApplication` ruft `System.exit(exitCode)` auf
|
||
|
||
**GUI-Pfad**
|
||
- `startGuiMode()` baut via `buildGuiStartupContext()` einen `GuiStartupContext`:
|
||
enthält `AiModelCatalogDispatcher`, `EnvironmentApiKeyResolutionAdapter`,
|
||
`TechnicalTestOrchestrator`, `GuiConfigurationPropertiesWriter`
|
||
- Bootstrap verdrahtet zusätzlich vier neue History-Use-Cases (`DefaultHistoryOverviewUseCase`,
|
||
`DefaultHistoryDetailsUseCase`, `DefaultHistoryResetDocumentStatusUseCase`,
|
||
`DefaultDeleteDocumentHistoryUseCase`) und den `DefaultPromptEditorUseCase` als anonyme
|
||
Bridge-Implementierungen in den `GuiStartupContext`
|
||
- `ApplicationVersionProvider.resolveVersion()` wird aufgerufen und der Wert in
|
||
`GuiStartupContext.applicationVersion` gesetzt
|
||
- Wenn eine Konfigurationsdatei beim Start bekannt ist, erzeugt Bootstrap zusätzlich einen
|
||
vollständig verdrahteten `GuiPromptEditorPort` (kombiniert `FilesystemPromptPortAdapter` mit
|
||
`DefaultPromptEditorUseCase`); ohne Konfiguration erhält der Context einen No-Op-Port
|
||
- `GuiAdapter.start(context)` übernimmt; ab diesem Punkt liegt die Kontrolle beim GUI-Adapter
|
||
- Im GUI-Pfad: keine SQLite-Schema-Initialisierung beim Start, kein Run-Lock-Erwerb, kein Batch-Use-Case;
|
||
History-Operationen initialisieren die Schema-Verbindung ad-hoc pro Aufruf
|
||
- GUI-interne Ports und deren Verbindung mit Outbound-Adaptern sind in
|
||
`docs/architecture/gui-overview.md` beschrieben
|
||
|
||
---
|
||
|
||
## 5. Einstiegspunkte je Modul
|
||
|
||
### pdf-umbenenner-adapter-out
|
||
|
||
1. **`...ai.OpenAiHttpAdapter`** – zeigt das typische Adapter-Muster: Port-Interface implementieren,
|
||
alle provider-spezifischen Details kapseln, `ProviderConfiguration` als einzige
|
||
Konfigurationsquelle konsumieren. Danach `AnthropicClaudeHttpAdapter` zum Vergleich lesen.
|
||
|
||
2. **`...sqlite.SqliteSchemaInitializationAdapter`** – erklärt das Datenbankschema, das alle
|
||
SQLite-Adapter voraussetzen. Hier sieht man, welche Felder in `document_record` und
|
||
`processing_attempt` existieren und wie Schema-Evolution additiv umgesetzt ist.
|
||
|
||
3. **`...configuration.PropertiesConfigurationPortAdapter`** – Einstieg in die
|
||
Konfigurationskette. Von hier aus `MultiProviderConfigurationParser` und
|
||
`LegacyConfigurationMigrator` nachverfolgen.
|
||
|
||
### pdf-umbenenner-adapter-in-cli
|
||
|
||
1. **`...adapter.in.cli.SchedulerBatchCommand`** – komprimiertes Inbound-Adapter-Muster in einer
|
||
einzigen Klasse. Zeigt, wie ein Inbound-Adapter ausschließlich über Port-Interfaces mit der
|
||
Application kommuniziert.
|
||
|
||
2. **`package-info.java`** – beschreibt Abhängigkeitsrichtung und Verdrahtungsvertrag dieses
|
||
Adapters.
|
||
|
||
3. **`SchedulerBatchCommandTest`** – zeigt, wie der Adapter ohne Bootstrap testbar ist.
|
||
|
||
### pdf-umbenenner-bootstrap
|
||
|
||
1. **`PdfUmbenennerApplication`** – Startpunkt; die kurze Kette von `main()` bis `System.exit()`
|
||
gibt einen ersten Überblick über die gesamte Startsequenz.
|
||
|
||
2. **`BootstrapRunner`** – Herzstück; `buildProductionBatchUseCase()` zeigt, wie der vollständige
|
||
Objektgraph manuell aufgebaut wird. `runHeadlessBatch()` zeigt den Headless-Kontrollfluss.
|
||
|
||
3. **`AiProviderSelector`** – kleinste Klasse mit größter Hebelwirkung: hier liegt die einzige
|
||
Stelle, an der die Provider-Auswahl aus der Konfiguration auf eine konkrete
|
||
`AiInvocationPort`-Implementierung trifft.
|
||
|
||
---
|
||
|
||
*Port-Verträge und Domain-Typen: `docs/architecture/domain-overview.md`*
|
||
*GUI-interne Ports und GUI-Adapter-Struktur: `docs/architecture/gui-overview.md`*
|