# 0002 — Adapter-Pattern mit ParlamentAdapter-Basisklasse + Registry | | | |---|---| | **Status** | accepted | | **Datum** | 2026-04-10 | | **Refs** | Issues #2, #3, #4, #19, #21, #23, #24, #26, #56, parlamente.py | ## Kontext Der GWÖ-Antragsprüfer soll Anträge aus 16 Bundesländern + dem Bundestag analysieren können. Jedes Parlament hat ein eigenes Doku-System mit unterschiedlichen Endpoints, Schemas, Auth-Anforderungen und Quirks: | Familie | Mitglieder | Charakter | |---|---|---| | portala/eUI | LSA, BE, BB, RP | JSON-Tree-Search mit `parsed`-Strings, HTML-Hits in efxRecordRepeater | | StarWeb (eUI v2) | HE | `browse.tt.json` 2-step + Perl-Dump-Comments | | PARiS (Java-Servlet) | HB | älterer StarWeb-Vorgänger, eigenes Hit-Format | | ParlDok 8.x | MV, HH, TH | SPA mit Fulltext/Search + Resultpage Pagination | | StarFinder-CGI | SH | `lissh.lvn.parlanet.de/cgi-bin/starfinder/0` | | OPAL native | NRW | älteres Eigensystem | | PARLIS (eUI-Variante) | BW | polling, JSON-in-HTML-Comments | | Eigensysteme | BY (TYPO3-Solr), SL (Umbraco/.NET), SN (ASP.NET-Webforms), BUND (DIP-API) | jeweils komplett unique | ## Optionen ### Option A — Eine Mega-Klasse mit Strategy-Pattern Eine `ParlamentAdapter`-Klasse mit Strategy-Object pro Doku-System, gewählt über Konfiguration. **Vorteile:** zentral, ein Eintrittspunkt für gemeinsame Logik wie Fraktion-Normalisierung. **Nachteile:** kompliziert sich schnell, wenn jede Strategy ihre eigenen Quirks hat (z.B. BB hat Datum-vor-Drucksachennummer im h6, BE hat 730d-Window-Hack, HE hat Perl-Hex-Decoder). Die Trennung wäre "Strategy mit Subclasses" — und damit sind wir bei B. ### Option B — Abstract Base + Eine konkrete Klasse pro Familie `ParlamentAdapter(ABC)` mit `search/get_document/download_text`-Methoden. Pro Doku-System eine konkrete Klasse: `PortalaAdapter`, `ParLDokAdapter`, `StarFinderCGIAdapter`, `StarWebHEAdapter`, `PARiSHBAdapter`, `PARLISAdapter`, `BayernAdapter`, `SaarlandAdapter`, `BundestagAdapter`, `SNEdasXmlAdapter`, `NRWAdapter`. Wo möglich, wird die Klasse via Konstruktor-Parameter parametrisiert (`PortalaAdapter` nimmt 6 BLs auf, `ParLDokAdapter` 3) statt jede Wiederverwendung als Subklasse. Registriert in einem `ADAPTERS: dict[str, ParlamentAdapter]` am Datei-Ende, indexiert per BL-Code (`"NRW"`, `"BUND"`, …). **Vorteile:** - Klare Verantwortlichkeit: ein Test-Lauf gegen einen LT trifft genau eine Klasse. - Reverse-Engineering-Findings können als Class-Docstring dokumentiert werden, wo sie hingehören. - Parametrisierung erlaubt Wiederverwendung ohne Vererbungs-Hierarchie. - `get_adapter(bundesland)` ist O(1). **Nachteile:** - Cross-Cutting-Concerns (Fraktion-Normalisierung) müssen separat gelöst werden — siehe ADR-Folge zur `parteien`-Zentralisierung in #55. ### Option C — Separates Modul pro Adapter Ein Python-File pro Adapter (z.B. `app/adapters/lsa.py`). **Vorteile:** maximale Datei-Granularität. **Nachteile:** Import-Overhead, der Registry-Eintrag muss in einer dritten Datei liegen, kein nennenswerter Vorteil gegenüber B solange die Adapter-Klassen unter ~500 Zeilen bleiben (PortalaAdapter ist mit ~520 Zeilen der größte). ## Entscheidung **Option B**. Eine `parlamente.py` mit einer Klasse pro Doku-System, parametrisierbar wo sinnvoll, registriert in `ADAPTERS`. Cross-Cutting-Logik wie Fraktion-Normalisierung wandert in separate Module (`parteien.py`, `bundeslaender.py`) und wird per Import oder Funktions-Argument injiziert. ## Konsequenzen ### Positiv - **17 Adapter sauber separierbar** in ~3000 Zeilen `parlamente.py`. Jeder Adapter kommt mit seinem Reverse-Engineering-Kommentar als Docstring, was bei Re-Reads nach Wochen die Wiedereinarbeit auf Minuten bringt. - **Tests parametrisieren über `ADAPTERS`-Registry**: ein einzelner Test-Helper iteriert alle aktiven Adapter und prüft die Akzeptanz- Kriterien (search liefert ≥3 Hits, get_document funktioniert, download_text extrahiert Text). - **Neue Bundesländer** kommen als 100-300-Zeilen-Klasse + 1 Eintrag in `ADAPTERS` rein, ohne Berührung anderer Adapter. ### Negativ - **`parlamente.py` ist groß** (~3000 Zeilen, 17 Klassen). Code-Navigation via Grep statt Auto-Imports. - **Reverse-Engineering-Findings sind in Kommentaren**, nicht in einer reference-Sektion. Wenn sich das System ändert, kann der Kommentar veralten — Mitigation: jede Reverse-Engineering-Session legt einen HAR-Trace in `TEMP/` und referenziert ihn im Class-Docstring. - **Adapter-Bugs leak nicht in andere Adapter**, aber **gemeinsame Fraktion-Disambiguation** muss manuell konsistent gehalten werden (siehe #55-Refactor und seine Regression in Issue #60-Side-Befund: ein Refactor-Rest in `embeddings.py:528` wurde durch ein anderes Modul verbreitet). ### Folgen für andere ADRs - ADR 0001 (LLM-Citation-Binding) ist davon unabhängig — der Postprocess läuft adapter-agnostisch nach jedem `analyze_antrag`. - Eine künftige Module-Splitter-Entscheidung (`adapters/` als Folder) würde den ADR superseden, wenn `parlamente.py` über ~5000 Zeilen wächst.