8 Querschnittliche Konzepte
Dieses Kapitel beschreibt technische Konzepte, die mehrere Bausteine des e-gov-framework betreffen und systemweit einheitlich umgesetzt sind.
8.1 Sicherheit und Autorisierung
8.1.1 Authentifizierung (vorgelagert)
Das Framework führt keine eigene Authentifizierung durch. Die
Identität des Nutzers wird durch die vorgelagerte Infrastruktur
festgestellt. Die F5-Firewall setzt nach erfolgtem Login einen
EGOV_TOKEN-Cookie. Der ContextInterceptor des
Frameworks extrahiert daraus die userId über das
UserIdProvider-Interface, das jede consuming App
implementiert, und speichert sie für die Dauer des Requests im
DataHolder-ThreadLocal.
HTTP-Request
→ ContextInterceptor.preHandle()
→ UserIdProvider.getUserId(request) ← consuming-App-Implementierung
→ DataHolder.setUserId(userId) ← ThreadLocal
→ alle nachgelagerten Komponenten lesen userId über DataHolder.getUserId()
→ ContextInterceptor.afterCompletion()
→ DataHolder.clear() ← ThreadLocal aufräumen
8.1.2 Row-based Security
Jede Datenbankabfrage wird automatisch um rollenbasierte
WHERE-Klauseln erweitert. Kein Code in der consuming App muss explizit
filtern — die Sicherheit greift auf Infrastruktur- ebene im
QueryBuilder.
Datenmodell (Flyway DDL):
| Tabelle | Zweck |
|---|---|
egov_iam_role |
Rollendefinitionen |
egov_iam_role_membership |
Benutzer-Rollen-Zuordnungen (mit
valid_from/valid_to) |
egov_iam_permission_definition |
Rollenspezifische SQL-Filterbedingungen pro Entity und Operation |
egov_iam_rule_set |
Validierungsregelsets pro Action |
egov_iam_condition |
Bedingungen innerhalb eines RuleSets |
Ablauf:
BaseRepositoryImpl.find(parameters)
→ PermissionBuilder.getPermissions(userId, entityClass, operation)
→ PermissionService.getAllowedRecordsQuery(userId, operation, clazz)
→ Rollenermittlung aus egov_iam_role_membership (userId, aktuelles Datum)
→ SQL-Filterausdruck aus egov_iam_permission_definition (role, entity, operation)
→ Rückgabe: AllowedRecordsQuery (Query-String + Parameter-Werte)
→ ParametrizedQuery wird als WHERE-Klausel in native SQL integriert
8.2 Command-Pattern und Audit-Trail
Alle fachlichen Operationen werden als
Command-JPA-Entity in der Tabelle
egov_cmd_command persistiert. Dadurch entsteht ein
lückenloses Audit-Log aller Aktionen.
8.2.1 Status-Lifecycle
INITIALIZED
→ RULESET_SELECTED (nach RuleSet-Auflösung)
→ VALIDATED (nach Validierung)
→ COMPLETED
→ EXECUTED (Endzustand: Erfolg)
→ HARD_VALIDATION_FAILED (Endzustand: Fehler)
Beide Felder status und executionPlan sind
als @Enumerated(EnumType.STRING) gemappt.
8.2.2 ExecutionPlan
Der ExecutionPlan steuert, ob und in welcher Reihenfolge
Validierung und Ausführung stattfinden:
| Wert | Verhalten |
|---|---|
VALIDATE_BEFORE_EXECUTION |
Bricht bei Validierungsfehler ab (Standard) |
VALIDATE_BEFORE_EXECUTING_ANYWAY |
Validiert, führt aber immer aus |
VALIDATE_AFTER_EXECUTION |
Führt zuerst aus, validiert danach |
DONT_VALIDATE |
Keine Validierung |
DONT_EXECUTE |
Keine Ausführung (z.B. Dry-Run) |
8.2.3 Executor-Varianten
| Interface | Merkmal | Verwendung |
|---|---|---|
Executor |
@Transactional auf execute() |
Standard-Schreiboperationen |
NonTxExecutor |
kein @Transactional |
Operationen, die ausserhalb einer Spring-Transaktion laufen müssen (bekanntes Risiko R-2) |
RemoteCommandExecutor |
Ausführung über ZooKeeper | Verteilte Commands über mehrere Instanzen |
BatchUpdateExecutor<T> |
Batch-Verarbeitung mit Progress-Logging | Massenoperationen |
Basisimplementierung:
CommandHandlerBase<T> orchestriert Validation →
Execution → Status-Persistierung.
8.3 Fehlerbehandlung
8.3.1 Exception-Hierarchie
Alle Framework-Exceptions erben von EGovException und
tragen einen strukturierten code (String) sowie optionale
additionalInfo (Map).
EGovException
├── NoPermissionForEntityException → HTTP 403
├── ActionNotAllowedException → HTTP 405
├── UnauthorizedException → HTTP 401
└── SystemException → HTTP 500
RuntimeException
└── ValidationException (intern, kein HTTP-Mapping)
8.3.2 Einheitliche Fehlerantwort
GenericExceptionHandler (@ControllerAdvice)
fängt alle Exceptions und wandelt sie in ein einheitliches
ResponseExceptionBody-DTO um:
{
"message": "No permission for entity 'Person' (operation: READ)",
"exceptionId": "f3a1bc42-...",
"errorCode": "EGOV_FRM_0012",
"additionalInfo": { "tableName": "person", "userId": "u42" },
"stackTrace": null
}Stacktrace wird nur ausgegeben wenn
egov.exception-log.debug=true gesetzt ist.
8.3.3 Fehler-Code-Schema
| Code | Bedeutung |
|---|---|
EGOV_FRM_0000 |
Generischer Framework-Fehler |
EGOV_FRM_0001 |
Ungültige Daten |
EGOV_FRM_0012 |
Keine Berechtigung auf Entity |
EGOV_FRM_0013 |
Kein Executor gesetzt |
EGOV_FRM_0014 |
Ungültige Action |
EGOV_FRM_0015 |
ZooKeeper-Fehler |
EGOV_PRS_0000 |
ID-Limit überschritten (LUID) |
8.4 Persistenz
8.4.1 Repository-Pattern
Alle Domänen-Entitäten der consuming App erweitern
BaseRepositoryImpl<T>. Diese Basisklasse stellt
generische CRUD-Operationen bereit und delegiert den Query-Bau an den
QueryBuilder.
8.4.2 Filter-DSL
REST-Anfragen können einen Filterausdruck als String übergeben. Der
FilterParser und Tokenizer übersetzen diesen
String in einen Abstract Syntax Tree; der QueryBuilder
erzeugt daraus native SQL ohne SQL-Injection-Risiko.
8.4.3 Multi-Datenbankunterstützung
| Property | Werte | Standardwert |
|---|---|---|
egov.persistence.database.type |
POSTGRESQL, SQLSERVER |
— (Pflichtfeld) |
egov.persistence.database.naming.style |
CAMEL_CASE, SNAKE_CASE |
CAMEL_CASE |
DatabaseConfigurationService liest diese Werte und
konfiguriert Dialekt und Naming- Strategie beim Start.
8.4.4 Datenbankmigrationen
Das Framework liefert eigene Flyway-Skripte für seine IAM- und
Command-Tabellen mit. Consuming Apps fügen applikationsspezifische
Skripte hinzu. Die Flyway-Schema-Filterung erfolgt über
EgovSchemaFilterProvider, damit Framework-Migrations nicht
mit App- Migrations kollidieren.
8.5 Konfiguration
Alle Framework-Properties sind unter dem Namespace
egov.* in der application.yml der consuming
App gruppiert. Das Framework liefert egov-default.yml mit
sicheren Defaults mit.
Zentrale Properties:
| Property | Default | Beschreibung |
|---|---|---|
egov.persistence.database.type |
— | Datenbanktyp (POSTGRESQL / SQLSERVER) |
egov.persistence.database.naming.style |
CAMEL_CASE |
Spaltenbenennungsschema |
egov.persistence.luid.max-ids-per-tick |
10000 |
IDs pro Sekunde pro Segment |
egov.persistence.luid.max-segments |
100 |
Anzahl paralleler Pods/JVMs |
egov.persistence.luid.start-year |
2025 |
Basisjahr für LUID-Berechnung |
egov.admin.is.activated |
false |
Admin-Modul aktivieren |
egov.permission.log.activated |
false |
Permission-Entscheidungen loggen |
egov.exception-log.debug |
false |
Stacktrace in Fehlerantworten |
egov.service.discovery.activated |
false |
ZooKeeper-Service-Discovery |
egov.service.discovery.zookeeper.host |
zookeeper |
ZooKeeper-Hostname |
egov.service.discovery.zookeeper.port |
2181 |
ZooKeeper-Port |
egov.subscriber.core-pool-size |
5 |
ThreadPool für Event-Subscriptions |
egov.subscriber.max-pool-size |
20 |
Max-Threads für Event-Subscriptions |
8.6 Logging
Das Framework verwendet SLF4j mit Logback als Implementierung. Alle
relevanten Klassen tragen @Slf4j (Lombok). Kein MDC-Einsatz
im Framework-Kern — die consuming App ist für das Anreichern von Logs
(z.B. mit userId oder requestId)
verantwortlich.
Log-Level-Konfiguration (Auszug):
logging:
level:
ch.ti8m.egov.framework.exceptionhandling.exceptionhandlers: DEBUG
ch.ti8m.egov.framework.validation.engine.FieldValueExtractorComponent: DEBUGKlassen mit relevantem Logging:
ContextInterceptor, PermissionBuilder,
GenericExceptionHandler, CommandHandlerBase,
LUIDGenerator, ZookeeperConnector.
ContextInterceptor.preHandle() extrahiert neben der
userId auch Request-Parameter (filter,
sort, offset, limit,
page, size, search) in den
DataHolder. afterCompletion() bereinigt den
ThreadLocal.
8.7 Internationalisierung
Das optionale e-gov-mdm-Modul stellt mehrsprachige
Stammdaten (Vocabularies) bereit.
8.7.1 Datenstruktur
Vocabulary
└── MasterDataGenericEntity (pro Eintrag und Sprache)
├── code (fachlicher Schlüssel)
├── languageCode (de, fr, it, en, ...)
├── shortName
├── longName
├── validFrom
└── validTo
LanguageDefinition verknüpft ein Vocabulary mit den
verfügbaren Sprachcodes.
8.7.2 Sprachauswahl
Die aktive Sprache wird in folgender Reihenfolge ermittelt:
Content-Language-Header des HTTP-Requests (gesetzt durchContextInterceptor)UserLanguageProvider.getLanguage()— Default-Implementierung:Locale.getDefault()
Consuming Apps können UserLanguageProvider als
Spring-Bean überschreiben, um die Sprache aus dem Nutzerprofil oder
einem anderen Request-Attribut zu lesen.
API-Ausgabe: ResponseTranslation mit
shortName und longName pro Sprache.
8.8 ID-Generierung (LUID)
Das optionale e-gov-luid-Modul stellt eine
clustersichere, sortierbare ID-Strategie bereit, die keine
Datenbanksequenz benötigt.
8.8.1 Algorithmus (LUIDGenerator)
ID = tickStep + incFromBase
tickStep = (Millisekunden seit startYear) / 1000 * maxIDsPerTick * maxSegments
incFromBase = maxIDsPerTick * segmentId + [0 … maxIDsPerTick − 1]
segmentId: eindeutiger Index des Pods/der JVM-Instanz (0–99 bei Default)maxIDsPerTick: Anzahl IDs, die pro Sekunde und Segment vergeben werden könnenmaxSegments: maximale Anzahl paralleler Instanzen im Cluster
LUIDGenerator ist als Hibernate
IdentifierGenerator implementiert, generate()
ist synchronized. Konfigurierbar über
egov.persistence.luid.*-Properties (siehe
Konfiguration).
Eigenschaften: zeitbasiert, sortierbar, gap-tolerant, kein Single-Point-of-Failure, kein Round-Trip zur Datenbank.
8.9 Testbarkeit
8.9.1 Test-Pyramide
| Ebene | Technologie | Beispiele |
|---|---|---|
| Unit-Tests | JUnit 5 | BaseRepositoryImplTest,
CommandHandlerBaseTest, FilterBuilderTest |
| Integrationstests | JUnit 5 + Testcontainers | BaseRepositoryImplIT,
SubscriptionServiceIT |
| ZooKeeper-Tests | Testcontainers (zookeeper:3.9.3) |
ZookeeperTestContainer-Basisklasse |
8.9.2 Testcontainers-Konfiguration
Datenbank-Integrationstests verwenden das JDBC-URL-Schema von Testcontainers:
# application-test.yml
spring:
datasource:
url: jdbc:tc:postgresql:17.4:///integration-tests-db
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
jpa:
hibernate:
ddl-auto: create-dropWeitere Profile: application-h2.yml
(In-Memory), application-test-sqlserver.yml,
application-test-zookeeper.yml,
application-test-logging-debug.yml.