ti&m e-gov-framework

9 Architekturentscheidungen

Architecture Decision Records (ADRs) dokumentieren die wichtigen Entscheidungen, die die Architektur des e-gov-framework prägen. Jeder ADR hält Kontext, getroffene Entscheidung, betrachtete Alternativen und Konsequenzen fest.


9.1 ADR-001: Framework als eingebettete Java-Bibliothek, nicht als eigenständiger Service

Status: Akzeptiert
Datum: 2023 (Projektgründung)

9.1.1 Kontext

E-Government-Fachapplikationen verschiedener Schweizer Behörden teilen denselben Bedarf an Querschnittsfunktionen: row-based Security, Audit-Trail, Stammdaten, Persistence-Abstraktion. Die Frage war, wie diese Funktionen bereitgestellt werden sollen.

Alternativen:

Kriterium Bibliothek (JAR) Sidecar / Microservice SaaS-Plattform
Deploymentkomplexität Gering (eine JVM) Hoch (zusätzlicher Prozess) Abhängig von Anbieter
Latenz Permission-Check Keine (in-process) Netzwerk-Roundtrip (~ms) Netzwerk-Roundtrip
Hoheit über Infrastruktur Vollständig bei Behörde Teilweise Keine
Compliance / Data Residency Einfach (Daten verlassen nie die JVM) Möglich Kritisch
Versionierung je Applikation Unabhängig via BOM Gemeinsamer Release-Zyklus Vorgeschrieben

9.1.2 Entscheidung

Das Framework wird als Maven-Bibliothek distribuiert. Jede consuming App zieht das Framework über das BOM als Abhängigkeit und betreibt es in derselben JVM.

9.1.3 Konsequenzen

Positiv: - Kein Netzwerk-Overhead bei Permission-Checks (kritisch für Row-based Security) - Jede Behörde kann unabhängig upgraden oder pinnen - Keine zusätzliche Laufzeitinfrastruktur notwendig - Vollständige Datenisolation pro Behörde garantiert

Negativ: - Framework-Logik läuft in derselben JVM wie Applikationslogik — Fehler im Framework können die gesamte Applikation betreffen - Consuming Apps tragen die Last des Upgrades (pom.xml-Änderung, Migrationstests) - Kein zentrales Monitoring über alle Deployments hinweg möglich


9.2 ADR-002: Native JDBC für die Query-Schicht statt JPA Criteria API

Status: Akzeptiert
Datum: 2023

9.2.1 Kontext

Das Framework muss jede SELECT-Abfrage automatisch um rollenbasierte WHERE-Klauseln erweitern (Row-based Security) und eine eigene Filter-DSL aus REST-Parametern umsetzen. JPA/Hibernate übernimmt das Entity-Mapping. Für die Query-Schicht standen zwei Ansätze zur Auswahl.

Alternativen:

Kriterium JPA Criteria API Native JDBC / SQL
Kontrolle über WHERE-Klauseln Eingeschränkt (Hibernate-Abstraktion) Vollständig
Lesbarkeit komplexer Queries Schlecht (Boilerplate) Gut (klares SQL)
Multi-DB-Unterstützung Via Hibernate-Dialekte Manuell je Dialekt
Performance Overhead durch Metadaten-Lookups Minimal
Permission-Filter injizieren Schwierig (kein direkter SQL-Zugriff) Trivial (String-Konkatenation)

9.2.2 Entscheidung

Die BaseRepositoryImpl und der QueryBuilder verwenden natives JDBC für alle Leseabfragen. JPA/Hibernate bleibt für Entity-Mapping und Schreiboperationen (saveWithTx) zuständig.

9.2.3 Konsequenzen

Positiv: - PermissionBuilder kann WHERE-Klauseln direkt als SQL-Fragment injizieren - Filter-DSL lässt sich sauber auf SQL abbilden ohne Criteria-API-Umweg - Queries sind lesbar und debuggbar - Performance-Ziel (<500 ms für 10 000 Entitäten) erreichbar

Negativ: - Zwei Persistenzmechanismen (JPA + JDBC) müssen koordiniert werden - SQL-Dialekte (PostgreSQL vs. SQL Server) müssen in DatabaseConfigurationService manuell behandelt werden - BaseRepositoryImpl ist deswegen komplex


9.3 ADR-003: Command-Pattern für alle Schreiboperationen

Status: Akzeptiert
Datum: 2023

9.3.1 Kontext

Behörden-Applikationen unterliegen strikten Audit-Anforderungen: Jede fachliche Aktion muss nachvollziehbar sein (wer hat was wann getan). Ausserdem müssen Schreiboperationen validierbar und rollback-fähig sein. Es wurden zwei Modelle verglichen.

Alternativen:

Kriterium Command-Pattern (persistiert) Direkte Service-Calls
Audit-Trail Vollständig (jede Aktion als Datenbankzeile) Nur via separates Log
Validierung vor/nach Ausführung Steuerbar via ExecutionPlan Inline, nicht standardisiert
Nachvollziehbarkeit für Behörde Hoch (egov_cmd_command-Tabelle abfragbar) Gering
Komplexität für consuming Apps Mittel (BaseExecutor, DomainCommandFactory) Gering
Distributed Execution Möglich (RemoteCommandExecutor) Nicht vorgesehen

9.3.2 Entscheidung

Jede fachliche Schreiboperation wird als Command-JPA-Entity angelegt und mit einem Status-Lifecycle (INITIALIZED → VALIDATED → EXECUTED oder HARD_VALIDATION_FAILED) in der Tabelle egov_cmd_command persistiert. BaseExecutor-Implementierungen orchestrieren den Ablauf.

9.3.3 Konsequenzen

Positiv: - Lückenloser Audit-Trail ohne Zusatzkonfiguration für consuming Apps - Validerungsregeln (RuleSet) werden deklarativ via Annotationen definiert - ExecutionPlan ermöglicht flexible Validierungsstrategien pro Use-Case - Erweiterbar auf verteilte Ausführung über mehrere Instanzen (ZooKeeper)

Negativ: - Jede Schreiboperation erzeugt einen zusätzlichen Datenbank-Insert (Command-Tabelle) - Consuming Apps müssen DomainCommandFactory und BaseExecutor ableiten und kennen - Es besteht so das Risiko, dass das Framework falsch verwendet wird.


9.4 ADR-004: Row-based Security als opt-out statt opt-in

Status: Akzeptiert
Datum: 2023

9.4.1 Kontext

Datenpannen in Behörden entstehen oft durch vergessene oder inkorrekte Berechtigungsprüfungen in Applikationscode. Das Framework sollte sicherstellen, dass kein Entwickler einer consuming App versehentlich ungeschützte Abfragen absetzt.

Alternativen:

Ansatz Beschreibung Risiko
Opt-in (explizit) Entwickler ruft PermissionBuilder manuell auf Vergessen möglich → Datenpanne
Opt-out (Default) QueryBuilder fügt immer Permission-Filter ein; explizites Abschalten nötig Kein versehentliches Bypass
Applikationsschicht Permission-Prüfung liegt komplett in der consuming App Fehleranfällig, kein Framework-Beitrag

9.4.2 Entscheidung

Der QueryBuilder bindet den PermissionBuilder immer automatisch ein. Jede BaseRepositoryImpl.find()-Abfrage ist per Default row-level-gefiltert. Ein expliziter Bypass ist nur für interne/Admin-Operationen über den Admin-Modus (egov.admin.is.activated) vorgesehen.

9.4.3 Konsequenzen

Positiv: - Ein Sachbearbeiter sieht niemals Datensätze ohne Berechtigung — auch nicht durch Programmierfehler in der consuming App - Sicherheitsverantwortliche können die Filterlogik zentral in egov_iam_permission_definition administrieren ohne App-Deployments

Negativ: - Höherer Aufwand beim initialen Setup: Permission-Definitionen müssen vollständig in der IAM-Tabelle gepflegt sein, bevor Daten sichtbar sind - Interne Batch-Jobs oder Admin-Operationen müssen den Admin-Modus explizit aktivieren


9.5 ADR-005: Vorgelagertes IAM — kein eigener Authentifizierungsstack

Status: Akzeptiert
Datum: 2023

9.5.1 Kontext

Schweizer Behörden betreiben eigene Identity-Management-Systeme (Azure AD, LDAP, eigene IdPs). Ein Framework, das selbst Authentifizierung implementiert, würde entweder diese Systeme duplizieren oder Integrationsaufwand in jeder Applikation erzwingen.

Alternativen:

Ansatz Beschreibung
Eigener JWT-Stack Framework validiert Tokens selbst, kennt Claims-Schema
Spring Security Integration Framework konfiguriert Spring Security mit konfigurierbarem IdP
Vorgelagertes IAM (gewählt) F5-Firewall setzt EGOV_TOKEN; Framework vertraut dem Cookie

9.5.2 Entscheidung

Das Framework führt keine Authentifizierung durch. Es vertraut darauf, dass der vorgelagerte Stack (Azure AD → F5 → EGOV_TOKEN-Cookie) eine authentifizierte userId liefert. Das UserIdProvider-Interface ist der einzige Integrationspunkt: Consuming Apps implementieren es einmalig für ihren spezifischen Cookie/Header.

9.5.3 Konsequenzen

Positiv: - Framework ist unabhängig vom konkreten Identity-Provider der Behörde - Keine doppelte Token-Validierungslogik in Framework und vorgelagertem Stack - Consuming Apps können UserIdProvider auf beliebige Auth-Quellen (JWT-Claim, Header, Session) mappen

Negativ: - Framework kann keine Claims-basierten Attribute (Abteilung, Berechtigungsgruppe) direkt aus dem Token lesen — consuming App muss das ggf. in UserIdProvider auflösen - Framework vertraut blind dem Cookie-Inhalt; korrekte TLS-Terminierung und Cookie-Sicherheit liegen vollständig beim vorgelagerten Stack


9.6 ADR-006: Unterstützung von PostgreSQL und SQL Server parallel

Status: Akzeptiert
Datum: 2023

9.6.1 Kontext

Verschiedene Behörden haben unterschiedliche Infrastrukturvorgaben: Einige betreiben PostgreSQL-Cluster (bevorzugt, Open Source, günstiger), andere sind durch Rahmenverträge oder IT-Governance an Microsoft SQL Server gebunden. Ein Framework, das nur eine Datenbank unterstützt, würde Behörden mit SQL-Server-Vorgaben ausschliessen.

Alternativen:

Kriterium Nur PostgreSQL Nur SQL Server Dual-DB (gewählt) Polyglot (+ H2 / MySQL)
Abdeckung Behördenanforderungen Unvollständig (MSSQL-Kunden ausgeschlossen) Unvollständig (PG-Kunden ausgeschlossen) Vollständig (alle aktuellen Kunden) Überabdeckung, unnötige Komplexität
Implementierungsaufwand Gering Gering Mittel (2 Dialekte) Hoch (n Dialekte)
CI-Aufwand (Testcontainers) 1 DB-Typ 1 DB-Typ 2 DB-Typen n DB-Typen
Wartbarkeit Dialekt-Code Hoch Hoch Mittel Gering

9.6.2 Entscheidung

Das Framework unterstützt beide Datenbanktypen vollständig. DatabaseConfigurationService liest egov.persistence.database.type und konfiguriert: - Hibernate-Dialekt - Spalten-Namensschema (CAMEL_CASE / SNAKE_CASE) - Datenbank-spezifische SQL-Fragmente im QueryBuilder - Flyway-Skripte pro Datenbank-Typ

9.6.3 Konsequenzen

Positiv: - Framework ist für alle Behörden einsetzbar, unabhängig von ihrer DB-Infrastruktur - Wechsel von PostgreSQL zu SQL Server (oder umgekehrt) erfordert nur Property-Änderung

Negativ: - Jede Änderung am Query-Layer muss gegen beide Datenbanken getestet werden - QueryBuilder und Flyway-Skripte müssen Dialekt-Unterschiede kennen und behandeln - Erhöhter CI-Aufwand: Integrationstests laufen gegen beide Datenbanken via Testcontainers


9.7 ADR-007: Spring AI + Azure OpenAI für LLM-gestützte Regelgenerierung

Status: Akzeptiert (experimentell)
Datum: 2024

9.7.1 Kontext

Die Pflege von Validierungsregeln in egov_iam_rule_set ist aufwändig und erfordert SQL-/Framework-Kenntnisse. Die Idee: Admins beschreiben Regeln in natürlicher Sprache, das System generiert die technische Regeldarstellung automatisch.

Für die LLM-Integration standen zwei Optionen:

Kriterium Spring AI (Abstraktionsschicht) Direktes OpenAI SDK
LLM-Austauschbarkeit Hoch (Providerwechsel via Config) Gering (Vendor-Lock-in)
Reife Milestone-Release (1.0.0-M6) Stabil (GA)
Integration in Spring Boot Native Manuell
Zukünftige On-Premise-Option Möglich (lokales LLM via Spring AI) Schwierig

9.7.2 Entscheidung

Das Modul e-gov-ai-rule-generation verwendet Spring AI als Abstraktionsschicht und Azure OpenAI (gpt-4o) als Backend. Der Prompt enthält die Grammatik der erlaubten Validierungsfunktionen; RuleTranslator konvertiert die LLM-Antwort in das Framework-Regelformat.

Das Modul wird bis zum GA-Release von Spring AI als experimentell gekennzeichnet.

9.7.3 Konsequenzen

Positiv: - Admins können Regeln aus Freitext generieren ohne SQL-Kenntnisse - Spring AI ermöglicht späteren Wechsel auf On-Premise-LLM ohne Code-Änderung

Negativ: - Breaking Changes zwischen Milestones möglich (→ Risiko R-4) - Azure OpenAI-Abhängigkeit erfordert Netzwerkzugang und API-Key-Verwaltung - Modul ist bisher ohne Testabdeckung (→ technische Schuld TD-1) - Azure OpenAI erfordert Cloud-Egress — dies steht in Konflikt mit der Randbedingung «Kubernetes On-Premise» (→ Sektion 2); der Konflikt ist bewusst akzeptiert: e-gov-ai-rule-generation ist optional, Behörden ohne Cloud-Freigabe lassen das Modul weg


9.8 ADR-008: LUID als cluster-sichere ID-Generierungsstrategie

Status: Akzeptiert
Datum: 2024

9.8.1 Kontext

Fachapplikationen, die auf Kubernetes mit mehreren Replikas laufen, brauchen eine ID-Generierungsstrategie, die ohne zentrale Koordination skaliert. Standard-Alternativen wurden bewertet:

Strategie Sortierbarkeit DB-Abhängigkeit Cluster-sicher Lücken
UUID v4 Nicht sortierbar Keine Ja Keine
DB-Sequenz Sortierbar Ja (Single Point) Nur mit DB-Failover Möglich
ULID Sortierbar (ms-Präzision) Keine Bedingt (Kollision möglich) Keine
LUID (gewählt) Sortierbar (sek-Präzision) Keine Ja (via segmentId) Toleriert

9.8.2 Entscheidung

Das Modul e-gov-luid stellt LUIDGenerator bereit — einen Hibernate IdentifierGenerator, der zeitbasierte, sortierbare IDs ohne Datenbankzugriff generiert. Jeder Pod erhält eine eindeutige segmentId (0–99), die Kollisionen zwischen Instanzen ausschliesst.

ID = ((Sekunden seit 2025-01-01) * maxIDsPerTick * maxSegments)
   + (maxIDsPerTick * segmentId)
   + laufende Nummer [0 … maxIDsPerTick − 1]

9.8.3 Konsequenzen

Positiv: - Keine Datenbankabfrage bei ID-Generierung → Durchsatz und Latenz verbessert - IDs sind sortierbar → natürliche Paginierung und Index-Effizienz - Skaliert bis 100 parallele Pods bei Defaultkonfiguration

Negativ: - segmentId muss beim Deployment korrekt konfiguriert sein — falscher Wert kann Kollisionen verursachen - IDs sind nicht zufällig (Information Disclosure: Erstellungszeitpunkt ableitbar) - Fehlkonfiguration (zwei Pods mit gleicher segmentId) führt zu Kollisionen ohne sofortige Fehlermeldung