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
ADAPTERSrein, ohne Berührung anderer Adapter.
Negativ¶
parlamente.pyist 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:528wurde 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, wennparlamente.pyüber ~5000 Zeilen wächst.