01. Einführung und Ziele
Aufgabenstellung
Das RAL Gütezeichen Anhörungsportal (CAF.Applications.RAL.QualityMarks.HearingPortal) bildet
das Anhörungsverfahren für RAL-Gütezeichen digital ab: Ein Verfahren wird angelegt, Institutionen
und deren Kontakte werden als Teilnehmer aufgenommen, Unterlagen werden zusammengestellt und nach
Vier-Augen-Freigabe versendet, Teilnehmer geben über ihr Institutionskonto (Zielzustand) oder,
solange dessen Ablösung läuft, noch über einen befristeten Gastlink Stellungnahmen ab, das
Verfahren wird intern beantwortet, beschieden und schließlich abgeschlossen und archiviert. Der
Lebenszyklus verzweigt je nach gewählter Abschlussvariante (US-33):
Draft → PendingApproval → Approved → Dispatched → Adjudication → Completed (zweistufig)
Draft → PendingApproval → Approved → Dispatched ─────────────→ Completed (einstufig)
└────────────────────── benannte Rücksprünge/Abbruch nach Fachregel ──────────────────────┘
Bei der einstufigen Variante entfällt der eigene Adjudication-Status: Bescheidung und
Endfassung gehen in einem Vorgang hinaus. Details zur Wahl der Variante stehen in
Laufzeitsicht.
Die Anwendung ist Teil des CAF-Monorepos: eines modularen ABP-/.NET-10-Monolithen mit
wiederverwendbaren, anwendungsneutralen Bausteinen (CAF.Modules.Authentication,
CAF.Modules.Documents[.Contracts], CAF.Modules.UI.Blazor) und der einzigen aktuell
existierenden Anwendung, dem Anhörungsportal, unter src/Applications/RAL/QualityMarks/HearingPortal/.
Das Portal ist damit gleichzeitig Referenzanwendung für die No-Layer-Architektur des Monorepos und
Träger der eigentlichen Fachlogik. Details zu Modulschnitt und Abhängigkeitsrichtung stehen im
Bausteinsicht; die noch offenen Bereiche und ihre Nachweislücken benennt
Risiken und technische Schulden.
Wesentliche fachliche Ziele
- Durchgängiger, nachvollziehbarer Anhörungsprozess: Von der Anlage über Vier-Augen-Freigabe, zuverlässigen Versand, Fristen/Erinnerungen, Stellungnahmen und internen Kritikdialog bis zu Bescheidung, Abschluss/Archiv oder kontrolliertem Abbruch — jeder Statuswechsel läuft über benannte Operationen, nie über einen frei setzbaren Status.
- Sicherer, kontobasierter Teilnehmerzugang: Zielzustand ist ein ausschließlich an das Institutionskonto gebundener Zugang ohne anonym erreichbaren Link, ohne Rückfallweg und mit sofort wirksamem Entzug (Bereich „Institutionszugang neu“, US-22/US-22a/US-22b/US-22c). Der bisherige ablaufende Gastlink ist der Übergangspfad bis zur vollständigen Ablösung, kein gleichwertiger Dauerzugang; interne Rollen (Administrator, Referent, Geschäftsführung) erhalten nur die ihnen erlaubten Aktionen.
- Verlässliche Institutions- und Kontaktverwaltung: Institutionen mit Anschrift, Typ, Pflichtpartnerstatus und mehreren Kontakten pflegen, samt CSV-Import/-Export und geschütztem Löschen erst nach Nutzungsprüfung.
- Kontrollierte Kontoprovisionierung: Institutionskontakte und Mitarbeitende werden über explizite, auditierte Wege zu Keycloak-Konten und genau einer Produktrolle zugeordnet — ohne Dublettenkonten oder automatische Rollenvergabe.
- Betriebsfähigkeit ab Tag eins: Health-Endpunkte, PII-arme Kennzahlen mit Korrelationsverfolgung vom Versandfehler bis zur Nachricht, sowie ein bei jedem Commit laufendes automatisches Qualitätsgate.
Qualitätsziele (Top-Prioritäten)
| Rang | Qualitätsziel | Motivation / Ausprägung |
|---|---|---|
| 1 | Sicherheit & Autorisierung | Jede geschützte Operation prüft Permission und Objektzugriff serverseitig; eine DocumentId, URL oder ein Dateiname verleiht nie Zugriff. Dokumentzugriffswege (intern, Kontobindung als Zielzustand, Direkt-Share; Gastlink als auslaufender Übergangspfad, siehe Qualitätsziele) revalidieren bei jedem Zugriff neu; ein versteckter Button ist keine Autorisierung. |
| 2 | Nachvollziehbarkeit & Integrität des Verfahrens | Freigabe erzwingt eine andere handelnde Person als die Einreichung, sperrt die geprüfte Zusammensetzung über eine Kompositionsprüfsumme; Versand läuft über eine persistente Outbox mit Idempotency Keys statt „best effort“; eine PII-arme fachliche Historie ergänzt das ABP-Audit-Log. |
| 3 | Modularität & Wartbarkeit | No-Layer-Architektur mit klaren, architekturtestgesicherten Modulgrenzen (Contracts/Runtime/Blazor je Feature); CAF.Modules.* bleiben anwendungsneutral und dürfen nie von der Anwendung abhängen; Cross-Module-Referenzen laufen ausschließlich über IDs und Contracts, nie über fremde Entities oder Tabellen. |
| 4 | Verifizierbarkeit / Testtiefe | Jede Funktion wird nach Fach-, Komponenten-, SQL-Server- und (für Kernstrecken) echten Browsertests bewertet, nicht nur nach vorhandenem Quellcode; ein automatisches Qualitätsgate blockiert Commits bei rotem Zustand, übersprungenen Pflichttests oder ausstehenden EF-Modelländerungen. |
| 5 | Bedienbarkeit & Barrierefreiheit | Einheitliche Cohesion-Oberfläche auf Deutsch, WCAG 2.1 AA (axe, serious/critical als Fehler) über alle Fach- und Portalseiten, responsive und tastaturbedienbar; Datenseiten behandeln Loading/Empty/Error/Forbidden/Validation/Saving/Stale-Concurrency konsistent. |
Diese Rangfolge steuert Zielkonflikte: Sicherheits- und Integritätsanforderungen sind nicht verhandelbar und gehen im Zweifel vor Bedienkomfort oder Implementierungsaufwand. Die geforderte Nachweistiefe je Änderungsart steht im Entwicklungsleitfaden §15.1; Qualitätsszenarien mit konkreten Ereignis/Antwort-Paaren stehen im Qualitätsanforderungen.
Stakeholder-Überblick
| Rolle | Erwartungshaltung |
|---|---|
| Interne Benutzer (Administrator, Referent, Geschäftsführung) | Verfahren anlegen, bearbeiten, freigeben, versenden, bescheiden, abschließen oder abbrechen — jeweils nur mit den ihrer Rolle zugewiesenen Rechten; Administrator und Geschäftsführung dürfen zusätzlich laufende Verfahren abbrechen. |
| Externe Teilnehmer (Institutionskontakte) | Über gebundenes Konto (/portal/zugang-uebernehmen, Zielzustand) oder — solange die Ablösung läuft — über Gastlink fristgerecht Stellungnahmen abgeben, eigene Unterlagen einsehen/herunterladen und interne Antworten auf die eigene Kritik sehen — ohne Einblick in fremde Teilnehmerdaten. |
| Fachadministration / Mitarbeitendenkonten-Verwalter | Mitarbeitende über /administration/mitarbeitendenkonten kontrolliert anlegen, genau eine Produktrolle zuweisen, wiedereinladen sowie app-lokal sperren/entsperren. |
| Betrieb | Lokalen Stack (SQL Server, Keycloak, Mailhog) über doctor.cmd/setup.cmd/start.cmd reproduzierbar betreiben; Gesundheitszustand über /health/live und /health/ready, Versand-/Fristenprobleme über PII-freie .NET-Kennzahlen mit Korrelationskennung verfolgen. |
| Entwicklungsteam | Modulgrenzen, No-Layer-Konventionen und das bei jedem Commit laufende Qualitätsgate einhalten; verbindliche Regeln stehen im Entwicklungsleitfaden und in conventions/. |
| Keycloak (technischer Stakeholder) | Alleiniger OIDC-Token-Issuer und Identitätsquelle; die Anwendung selbst betreibt keinen lokalen OpenIddict-Server. |
Weiterführend: Randbedingungen, Kontextabgrenzung, Lösungsstrategie sowie die Betriebs- und Startpfade in ../GETTING-STARTED.md und im Portal-README.
02. Randbedingungen
Dieses Kapitel fasst die Vorgaben zusammen, innerhalb derer Architektur- und Implementierungs-
entscheidungen getroffen werden. Sie sind größtenteils verbindlich gesetzt (Technologie-,
Team- und Prozessvorgaben) und nicht das Ergebnis einer freien Architekturwahl. Details zu Ownership
und Modulschnitt stehen im Entwicklungsleitfaden; Begründungen je
Einzelkonvention in docs/conventions-decisions.md.
1. Technische Randbedingungen
| Bereich | Vorgabe | Bindend seit |
|---|---|---|
| Runtime/Sprache | .NET 10, C# mit Nullable enable |
Projektstart |
| Anwendungsarchitektur | ABP Single-Layer-/No-Layer-Template — bewusst kein klassisches 8-Projekt-Layering (Domain.Shared/Domain/Application.Contracts/…); ein Feature besteht aus {Feature}.Contracts + Runtime, optional .Blazor |
ABP-Vorgabe, siehe AGENTS.md |
| UI-Technologie | Blazor Server, ausschließlich InteractiveServer (kein WebAssembly, kein @rendermode in Feature-Code) |
Architekturtest-erzwungen |
| UI-Komponentenbibliothek | Cohesion (Syncfusion-Basis) ist verpflichtend für jede CAF-/GZ-Oberfläche; MudBlazor/Syncfusion/Blazorise dürfen im Anwendungscode nicht direkt gerendert werden. Ausnahme: von ABP gelieferte Administration/Account/LeptonX bleiben auf ABP-MudBlazor | AGENTS.md Abschnitt „Architektur der Benutzeroberfläche" |
| Persistenz | EF Core, SQL Server als einziger Provider; PascalCase-Tabellennamen mit Owner-Präfix (RALQM…, CAF…); ein Modul besitzt seine Entities und Configure{Feature}()-Erweiterung, der Host bleibt Migration Host |
DEVELOPMENT-GUIDE.md Abschnitt 9 |
| Authentifizierung/Autorisierung | Keycloak als OIDC-Server via OpenIddict-Client-Integration (CAF.Modules.Authentication); ABP Identity/Permission Management führt lokale Benutzer, Rollen und Permissions. Kein lokaler OpenIddict-Server, kein Feature Management ohne nachgewiesenen Bedarf |
AGENTS.md |
| Nicht zugelassene Infrastruktur | PostgreSQL, Redis, RabbitMQ, Kafka, Hangfire, Elsa, GraphQL, DMS oder Microservices ohne ausdrückliche Nutzerfreigabe und belegten Use Case | DEVELOPMENT-GUIDE.md Abschnitt 11/16 |
| Paketverwaltung | Central Package Management (Directory.Packages.props), packageSourceMapping in NuGet.Config (u. a. Cohesion exklusiv auf internem Feed) |
docs/conventions-decisions.md N-01/N-02 |
| Build-Qualität | TreatWarningsAsErrors=true plus .NET-Analyzer; Restore/Build/Test müssen mit null Warnings/null Errors durchlaufen |
DEVELOPMENT-GUIDE.md Abschnitt 15.2 |
Diese Vorgaben sind in Architekturtests kodifiziert (z. B. Verbot CAF.Modules.* →
CAF.Applications.RAL.QualityMarks.HearingPortal.*, InteractiveServer-only, Cohesion-only-UI) — sie werden nicht
gelockert, nur Pfad-Literale bei struktureller Umbenennung nachgezogen.
2. Organisatorische Randbedingungen
Sprache
- UI-Texte: Deutsch (Produktsprache), über
IStringLocalizer<T>und paritätische DE-/EN-Localization-JSON je owning Modul — keine hartkodierten UI- oder Business-Exception-Texte. - Code-Identifier und Kommentare: Englisch.
- Logs: strukturiert und Englisch (
ILogger<T>mit Templates, nie String-Interpolation, nie direkte Serilog-Referenz).
Diese Dreiteilung gilt repositoryweit, siehe conventions/baseline.md.
Konventionen und Entscheidungsprozess
- Themenspezifische Coding-Konventionen liegen in
conventions/(C#, EF Core, Blazor, ABP, Tests, Git, Security, PowerShell, CI/CD, MSBuild/NuGet) und sind bewusst knapp gehalten. - Die repo-spezifische Adopt/Reject-Entscheidung je Konvention — inklusive Vor-/Nachteil — steht in
docs/conventions-decisions.md; das ist die maßgebliche Quelle, welche geteilte Konvention hier tatsächlich gilt (z. B. explizite Typen stattvarund das StyleCop-Set sind bewusst abgelehnt,IClock/IGuidGenerator,ConcurrencyStampund die Security-Baseline sind übernommen). - Quellenpriorität bei Konflikten: aktuelle Benutzeranweisung → nächstgelegene
AGENTS.md→ repositoryweite Arbeitsgrundlage → aktuelle Architektur/Entwicklungsleitfaden. Nur dieses verlinkte Kernset gibt Repository-Regeln vor; Code und ausführbare Nachweise bestimmen den Implementierungsstand. - Strukturelle Änderungen (Modulgrenze, Sicherheit, Persistenz, Abhängigkeit, Betrieb) ziehen Architektur, Arbeitsgrundlage und durchsetzende Tests im selben Änderungssatz mit; es wird keine parallele Entscheidungsdokumentation gepflegt.
Git-Workflow
- Conventional Commits:
feat|fix|refactor|docs|test|chore(scope): subject, imperativ, Subject ≤ 50 Zeichen. mainist geschützt: kein Direct-Push, kein Force-Push; Änderungen ausschließlich per Merge Request mit Review.- Branch-Namen kebab-case (
feature/…,fix/…,chore/…), nach Merge gelöscht. AGENT_CHANGELOG.mdwird von jedem Agent-Turn gepflegt (neuester Eintrag oben, ≤ 1000 Zeichen) — diese Pflicht gilt für Menschen und Agents gleichermaßen.- Qualitäts-Gate bei jedem Commit: versionierte Git-Hooks (
tools/git-hooks, aktiviert übersetup.cmd) führenInvoke-QualityGate.ps1ohne-IncludeBrowserGategegen die volleCAF.slnxaus und blockieren den Commit bei rotem Gate; übersprungene Pflichttests gelten als Fehlschlag.git commit --no-verifyist eine dokumentierte Ausnahme, kein Arbeitsweg. Details:DEVELOPMENT-GUIDE.mdAbschnitt 15.2 undGETTING-STARTED.md.
Agenten als Mitwirkende
- Claude Code und Codex arbeiten nach denselben Quellen wie menschliche Entwickler; es gibt
bewusst kein tool-spezifisches Regel-Duplikat. Claude Code liest
CLAUDE.md, einen reinen Zeiger aufAGENTS.md; Codex liest diese Root-AGENTS.mddirekt, weil seine Projekt-Discovery im Repositorywurzelverzeichnis beginnt. - Jeder Implementierungs- oder Prüfagent liest vor einer Codeänderung die repositoryweite
Arbeitsgrundlage, die aktuelle Architektur, den Entwicklungsleitfaden und die nächstgelegene
AGENTS.md.
3. Namens- und Strukturkonventionen (Randbedingung für jede Erweiterung)
- Anwendungsspezifische persistierte Identifier tragen den Präfix RALQM (Tabellen
RALQMHearings/RALQMInstitutions, Error CodesRALQM.<Feature>:, Permission-GruppeRALQM, RollenRALQMAdministrator/RALQMCaseOfficer/RALQMManagement). - Technische Slugs bleiben lowercase (
ral-qualitymarks). - Wiederverwendbare CAF-Module behalten den
CAF-Präfix (CAFDocuments*). src/Modules/*darf nicht von der Anwendung abhängen — durch Architekturtests erzwungen.- Umbenennungen sind stets datenerhaltend; historische EF-Migrationen behalten ihre alten Identifier bei.
Diese Randbedingungen sind Grundlage für die Lösungsstrategie und die Bausteinsicht; technische Details zu Setup und Betrieb stehen im Schnelleinstieg und im Entwicklungsleitfaden.
03. Kontextabgrenzung
Dieses Kapitel zieht die Systemgrenze um das eine fachliche Deployable
CAF.Applications.RAL.QualityMarks.HearingPortal (RAL Gütezeichen Anhörungsportal, „RALQM") und
benennt seine externen Nachbarn — fachlich (wer interagiert wozu) und technisch (über welche
Schnittstelle). Die Modul-interne Gliederung folgt in der Bausteinsicht;
die Begründung für „ein Deployable, eine Anwendungsdatenbank" steht in der
Lösungsstrategie.
3.1 Systemgrenze
Es gibt genau ein fachliches Anwendungs-Deployable: den RALQM-Host, eine ASP.NET-Core-/Blazor-
Web-App mit Interactive Server. Keycloak, SQL Server und der SMTP-Provider sind Infrastruktur —
keine weiteren GZ-Fachservices. Die Anwendung besitzt genau einen HearingPortalDbContext und
einen Migration-Host im Host-Projekt; Feature-Application-Services laufen im selben Prozess wie
die Blazor-UI (kein separater API-Tier). Details zum Ablauf eines Requests innerhalb der Grenze
stehen in der Laufzeitsicht.
flowchart LR
Staff["Interne Mitarbeitende\nAdministrator / CaseOfficer / Management"] --> App
Guest["Externe Teilnehmer\nKontozugang (Ziel), Gastlink (Übergang)"] --> App
ShareRecipient["Empfänger eines\ndirekten Dokumentenlinks"] --> App
App["RALQM HearingPortal\nBlazor Web App (Interactive Server)"] <-->|"OIDC Code + PKCE"| Keycloak["Keycloak\nIdentity Provider"]
App -->|"schmaler Admin-Port"| Keycloak
App --> GzDb[("RALQM-Anwendungsdatenbank\nSQL Server")]
Keycloak --> KcDb[("Keycloak-Datenbank\nSQL Server, separat")]
App --> Mail["SMTP-Provider\nlokal: Mailhog"]
Lokal liegen die RALQM- und die Keycloak-Datenbank auf derselben SQL-Server-Instanz; fachlich und
technisch bleiben es getrennte Datenbanken mit getrennten Principals (Keycloak-eigener Login vs.
dedizierter qualitymarks-Datenbankprincipal für die Anwendung).
3.2 Fachlicher Kontext
| Nachbar/Akteur | Rolle | Fachliche Interaktion |
|---|---|---|
Interne Mitarbeitende — Administrator (RALQMAdministrator) |
volle fachliche Administration | Stammdaten (Institutionen/Kontakte), Mitarbeitendenkonten, gesamter Anhörungsprozess, Vorlagen, Postausgang |
Interne Mitarbeitende — Sachbearbeitung (RALQMCaseOfficer) |
vollständige Fallbearbeitung ohne Freigabe/Bescheidung | Anhörungen anlegen/bearbeiten/einreichen, Institutionen/Kontakte pflegen, Dialog mit Teilnehmern |
Interne Mitarbeitende — Geschäftsführung (RALQMManagement) |
Lesezugriff plus Vier-Augen-Entscheidungen | Freigabe (Approve) und Bescheidung (Adjudicate); darf keine eigene Einreichung selbst freigeben |
| Institutionsvertreter — Kontozugang (Zielzustand) | angemeldeter, teilnahmegebundener Nutzer (/portal/zugang-uebernehmen) |
Einladung/Konto übernehmen, Dokumente einer Anhörung einsehen, eigene Stellungnahme anlegen/ändern, sieht Meine Teilnahmen; löst den Gastzugang ab (Bereich „Institutionszugang neu“, US-22 ff.) |
| Institutionsvertreter — Gastzugang (Übergangspfad, wird abgelöst) | unangemeldeter Verfahrensbeteiligter | Einladung einlösen, Dokumente einer Anhörung einsehen, eigene Stellungnahme anlegen/ändern; kein Rückfallweg nach vollständiger Ablösung |
| Empfänger eines direkten Dokumentenlinks | anonymer Empfänger eines CAF-Share-Tokens | Download eines einzelnen freigegebenen Dokuments über ein befristetes, widerrufbares Secret; kein Ersatz für einen Hearing-Gastlink |
| Keycloak | Identitätsquelle | authentifiziert alle Benutzer zentral; ABP projiziert lokale Benutzer/Rollen über den stabilen OIDC-sub |
Objektzugriff, Verfahrensstatus und Teilnehmerzuordnung werden zusätzlich je Feature geprüft; Details zu Rollen, Grants und den vier getrennten Dokument-Zugriffswegen stehen in den querschnittlichen Konzepten (Kapitel 8).
3.3 Technischer Kontext
| Nachbar | Schnittstelle/Protokoll | Zweck | Richtung |
|---|---|---|---|
| Endnutzer-Browser | HTTPS, Blazor-Interactive-Server-Circuit (SignalR) | UI-Rendering und Interaktion; JS-Interop nur nach interaktivem Rendern | eingehend |
| Keycloak (Login) | OIDC Authorization Code + PKCE über HTTPS | Authentifizierung, Token-Issuer für interne und kontogebundene externe Nutzer | bidirektional |
| Keycloak (Admin-API) | schmaler, kontrollierter Admin-Port (CAF.Modules.Authentication-Provisioning) |
kontrollierte Kontoprovisionierung für Mitarbeitende und Institutionskontakte; scheitert bei mehrdeutiger Identität geschlossen, vergibt nie automatisch eine Fachrolle | ausgehend |
| SQL Server — RALQM-Anwendungsdatenbank | Tabular Data Stream (ADO.NET/EF Core) | Persistenz über HearingPortalDbContext; Migrationen laufen ausschließlich im Host |
ausgehend |
| SQL Server — Keycloak-Datenbank | Tabular Data Stream | eigene Persistenz von Keycloak; lokal gleiche Instanz, getrennte Datenbank und Principals, keine GZ-Fachdaten | Keycloak-intern |
| SMTP-Provider | SMTP | Versand der Hearing-Outbox (Einladungen, Nachrichten je Verfahrensschritt) über ABP Emailing; lokal Mailhog als Development-Fänger | ausgehend |
| BLOB-Inhalt (Dokumente) | SQL-Server-BLOB-Provider (ABP BLOB Storing Database) | physischer Dateiinhalt liegt in derselben RALQM-Anwendungsdatenbank; keine zweite Dokumentablage | ausgehend |
Lokale Development-Endpunkte (siehe GETTING-STARTED):
Anwendung https://localhost:44356, Keycloak http://localhost:8080 (Realm caf), Mailhog UI
http://127.0.0.1:8025 / SMTP 127.0.0.1:1025, SQL Server 127.0.0.1:1433. Alle Compose-Ports
sind nur an Loopback gebunden (siehe etc/docker/docker-compose.yml); Produktionsendpunkte
und -zugangsdaten werden außerhalb dieser lokalen Dateien betrieben.
3.4 Bewusst keine weiteren Nachbarn
Die Systemgrenze umfasst keinen zweiten Anwendungsservice, kein externes DMS, keine Microservice-Nachbarn und keine zweite GZ-Datenbank — ein neuer externer Nachbar braucht zuerst einen konkreten, getesteten Anwendungsfall. Details zu bewusst nicht vorhandener Infrastruktur stehen in den Randbedingungen.
04. Lösungsstrategie
Dieses Kapitel bündelt die grundlegenden Entscheidungen, die den Lösungsraum aufspannen und in allen Folgekapiteln als gesetzt vorausgesetzt werden. Die begründete Langform je Entscheidung steht in den Architekturentscheidungen; die Abwägung gegen externe Konventionsvorlagen in docs/conventions-decisions.md.
4.1 Modularer Monolith statt Microservices
Genau ein Deployable (CAF.Applications.RAL.QualityMarks.HearingPortal) und eine
Anwendungsdatenbank bilden die gesamte Lösung. Microservices, ein zweites GZ-Datenbankschema,
Message-Broker (RabbitMQ/Kafka) oder ein API-Gateway sind bewusst nicht Teil der Architektur
(siehe Qualitätsanforderungen §10.3
und Risiken §Bewusst nicht als Schulden geführt).
Der Grund ist Betriebsökonomie für ein Fachportal dieser Größe: ein
Deploy-Artefakt, eine Migration Pipeline, ein Health-Check. Die fachliche und im Quellcode
erzwungene Modultrennung (siehe 4.3) hält die Option auf spätere Auskopplung offen, ohne sie
vorwegzunehmen — Cross-Modul-Kommunikation läuft bereits heute über schmale Contracts und IDs,
nicht über gemeinsame Entities.
4.2 „No-Layer" statt klassischer 8-Projekt-ABP-Schichtung
Die strikte ABP-Standardaufteilung (Domain.Shared/Domain/Application.Contracts/Application/ EntityFrameworkCore/HttpApi/…) ist für dieses Repo explizit N/A (conventions-decisions.md ABP-11). Domain, Application und Persistence bleiben logische Schichten, aber materialisiert in einer kompakten Runtime-Assembly je Modul statt in sieben eigenen Projekten. Eine weitere Assembly-Grenze wird erst gezogen, wenn sie unabhängig konsumiert, versioniert oder ausgetauscht werden muss (ADR-01). Das reduziert Projekt- und Verkabelungsaufwand, ohne die fachliche Kapselung aufzugeben — die Kapselung sitzt in Feature-Slices, nicht in Assembly-Zahl.
4.3 Feature-Slices: Contracts / Runtime / Blazor
Jedes Fachmodul zerfällt in bis zu drei Projekte mit einseitiger Abhängigkeitsrichtung:
<Modul>.Contracts DTOs, AppService-Interfaces, Permissions, Localization, öffentliche IDs
<Modul> Runtime: Domain, Application Services, EF-Mapping
<Modul>.Blazor optionale Feature-Seiten/Komponenten (konsumiert nur Contracts)
- Contracts kennt keine EF-Entities und keine Implementierungsdetails; sie ist die einzige Fläche, die andere Module referenzieren dürfen.
- Runtime implementiert die Contracts, besitzt die eigenen Tabellen und darf nie fremde Runtime, fremde Entities oder fremde DbSets referenzieren.
- Blazor referenziert die eigene und ggf. fremde Contracts sowie Shell-Contracts, niemals eine Runtime.
Diese Grenzen sind eine Compile-Time-Eigenschaft, durchgesetzt über Architekturtests, nicht
nur eine Ordnerkonvention (Abhängigkeitstabelle in
Bausteinsicht §5.2.4).
Cross-Modul-Referenzen erfolgen ausschließlich über IDs (z. B. InstitutionId, DocumentId), nie
über relationale Foreign Keys oder Navigation über Modulgrenzen hinweg
(Querschnittliche Konzepte §2).
4.4 Zwei Bausteinklassen: wiederverwendbare CAF-Module vs. GZ-Fachmodule
Der Quellcode trennt zwei fachlich unterschiedliche Verantwortungsebenen strikt:
| Klasse | Beispiele | Kennzeichen |
|---|---|---|
CAF.Modules.* |
CAF.Modules.Authentication, CAF.Modules.UI.Blazor, CAF.Modules.Documents(.Contracts) |
anwendungsneutral, keine Abhängigkeit auf CAF.Applications.RAL.QualityMarks.HearingPortal.*, Quellcode-Baustein (nicht packbar) mit verbindlicher VERTRAG.md |
| GZ-Fachmodule | Authorization, Institutions, Hearings (je mit .Contracts/.Blazor) |
RAL-Gütezeichen-spezifisch, referenzieren Framework-Contracts, tragen die fachlichen Regeln |
CAF.Modules.* darf laut ADR-06
keinen GZ-Namespace und keinen GZ-Fachbegriff referenzieren; die Regel gilt nie umgekehrt
(CAF.Modules.* → CAF.Applications.RAL.QualityMarks.HearingPortal.* ist verboten). Konkret sichtbar wird das an den Modul-READMEs:
src/Modules/Authentication/README.mdbeschreibt einen OIDC-/PKCE-Client ohne Produktrouten oder Berechtigungen der Anwendung.src/Modules/Documents/README.mdbeschreibt einen mandantenfähigen Dokument-Adapter über ABP File Management, ebenfalls ohne Abhängigkeit auf das Portal.
Beide Bausteine behalten dabei bewusst das Präfix CAF in ihren persistierten Bezeichnern
(CAFDocumentShareGrants), unabhängig vom RALQM-Präfix der Anwendung (siehe 4.5). Diese Trennung
macht die Framework-Module potenziell in einem anderen ABP-Host wiederverwendbar, ohne dass GZ-
Fachlogik mitgezogen wird.
4.5 RALQM-Präfixstrategie für persistierte Kennungen
Anwendungsspezifische Kennungen tragen konsequent das Präfix RALQM: Tabellen
(RALQMHearings, RALQMInstitutions), Fehlercode-Namespaces (RALQM.<Feature>:), die
Permission-Gruppe RALQM und die Rollen RALQMAdministrator/RALQMCaseOfficer/
RALQMManagement. Technische Slugs bleiben demgegenüber lowercase (ral-qualitymarks).
Wiederverwendbare CAF-Module behalten ihr eigenes CAF-Präfix (CAFDocuments*) und werden von der
Umbenennung nicht erfasst — der Präfix markiert Ownership, nicht Technologie. Renames sind
grundsätzlich datenerhaltend: Migrationen, die einen Rename umsetzen (z. B.
20260825174437_RenamePersistedIdentifiersToRALQM), erzeugen keinen Datenverlust, und historische
EF-Migrationen behalten ihre alten Identifiers, statt rückwirkend umgeschrieben zu werden
(conventions-decisions.md EF-03). Diese Strategie hält die Grenze zwischen „Framework" und
„Anwendung" auch im Datenbankschema sichtbar, ohne die Migrationshistorie zu verfälschen.
4.6 Cohesion als verbindliche Fach-UI
Eigene CAF-/GZ-Oberflächen rendern ausschließlich über Cohesion (Syncfusion); MudBlazor,
rohes Syncfusion oder Blazorise dürfen von Fachcode nicht direkt aufgerufen werden
(conventions-decisions.md BL-01,
Querschnittliche Konzepte §5).
Ausgenommen sind die unverändert
übernommenen ABP-Administrations- und LeptonX-Seiten, die ihren eigenen Vendor-UI-Stack behalten.
Träger dieser Entscheidung ist CAF.Modules.UI.Blazor: zentrale Cohesion-Registrierung,
Lizenzprüfung, Assets/Bundles und die Interactive-Server-Policy an einer Stelle. Das
Cohesion-Paket kommt aus dem GitLab-Gruppen-Feed; Quelle, Versionsbereich und Auth-Wege stehen in
Getting Started, Cohesion-Paketquelle. Feature-Seiten liegen im jeweils owning
.Blazor-Projekt, bleiben render-mode-neutral (kein @rendermode in Feature-Code) und erfüllen
WCAG 2.1 AA als Pflichtkriterium — fachlich relevant für ein Behördenportal.
4.7 Konsequenzen für Cross-Cutting-Themen
- Auth: Keycloak bleibt alleiniger Identity Provider;
CAF.Modules.Authenticationkapselt den OIDC-Client, GZ Authorization komponiert Rollen/Grants aus featureeigenen Permissions (Querschnittliche Konzepte §1). - Dokumente: ABP File Management/BLOB Storing bleibt Descriptor- und Inhalts-Owner;
CAF.Modules.Documentsliefert nur den neutralen Vertrag plus Share-Grants, Hearings trägt die fachliche Bedeutung einer Datei (Laufzeitsicht §6.4). - Persistenz: ein gemeinsamer
HearingPortalDbContext, aber jedes Modul liefert seine eigene Model-Konfiguration; Migrationen entstehen ausschließlich im Host. - Tests: die Modulgrenzen werden nicht nur dokumentiert, sondern durch Architekturtests erzwungen und sind damit Teil des Qualitätsnetzes (Querschnittliche Konzepte §7).
Siehe auch
- Randbedingungen — die gesetzten Vorgaben, die diese Strategie motivieren.
- Kontextabgrenzung — Systemgrenze und externe Nachbarn.
- Bausteinsicht — Detailschnitt der hier benannten Module.
- Querschnittliche Konzepte — Vertiefung von Auth, Dokumenten und Konfiguration.
- Architekturentscheidungen — ADR-Langform je Grundsatzentscheidung.
- docs/conventions-decisions.md — Konventions-Entscheidungsregister mit Status je Regel.
- DEVELOPMENT-GUIDE.md — verbindliche Arbeitsweise bei Änderungen.
05. Bausteinsicht
Dieses Kapitel zerlegt die statische Struktur des Systems in Bausteine: zuerst auf Ebene 1 (Module/Projekte), dann auf Ebene 2 (Feature-Slices der Anwendung). Motivation und Zielsetzung der No-Layer-Architektur stehen im Kontextabgrenzung und in der Lösungsstrategie; dieses Kapitel beschreibt nur, was existiert und **wer wen abhängen darf. Details zu Laufzeitabläufen liefert die Laufzeitsicht.
Quelle der Wahrheit für alle hier genannten Grenzen sind die Architekturtests (siehe ADR-06); dieses Kapitel fasst sie arc42-konform zusammen und verlinkt auf die READMEs der Bausteine.
5.1 Ebene 1: Gesamtsystem in Module und Anwendung
Das System ist ein modularer Monolith: ein Deployable
(CAF.Applications.RAL.QualityMarks.HearingPortal), eine gemeinsame SQL-Server-Datenbank,
aber strikt getrennte Bausteine im Quellcode und im Abhängigkeitsgraphen. Die .slnx gliedert
dies in vier Gruppen: 01 Anwendung, 02 CAF-Bausteine, 03 GZ-Fachmodule, 04 Tests.
flowchart TB
subgraph App["Anwendung: RAL Gütezeichen Anhörungsportal"]
Host["Host\nCAF.Applications.RAL.QualityMarks.HearingPortal"]
Shell["Shell\n…HearingPortal.Shell"]
AppContracts["Contracts\n…HearingPortal.Contracts"]
Auth["Features/Authorization"]
Inst["Features/Institutions"]
Hear["Features/Hearings"]
end
subgraph Modules["Wiederverwendbare CAF-Module (anwendungsneutral)"]
ModAuthn["CAF.Modules.Authentication"]
ModUI["CAF.Modules.UI.Blazor"]
ModDocC["CAF.Modules.Documents.Contracts"]
ModDoc["CAF.Modules.Documents"]
end
Host --> ModAuthn
Host --> ModUI
Host --> Shell
Host --> Auth
Host --> Inst
Host --> Hear
Auth --> Inst
Auth --> Hear
Auth --> ModDocC
Hear --> Inst
Hear --> ModDocC
ModDoc --> ModDocC
Pfeile bedeuten „referenziert/verwendet“. Die zentrale, architekturtestlich abgesicherte Regel:
die Anwendung hängt von den Modulen ab, nie umgekehrt. Kein CAF.Modules.*-Projekt darf ein
CAF.Applications.RAL.QualityMarks.HearingPortal.*-Projekt, einen GZ-Namespace oder einen
GZ-Fachbegriff referenzieren.
5.1.1 Anwendungs-Bausteine
| Baustein | Projekt | Verantwortung | Grenze |
|---|---|---|---|
| Host | CAF.Applications.RAL.QualityMarks.HearingPortal |
Start, Modulkomposition, Middleware, Routing, konkrete Konfiguration, gemeinsamer HearingPortalDbContext, SQL-Provider, Migration und Seed |
keine neuen Fachentities, Fachregeln oder Feature-Application-Services |
| Contracts | …HearingPortal.Contracts |
produktweite Shell-Localization (Ressource RALQM), globale Menünamen, von der Shell benötigte ABP-Contracts |
keine Feature-Entities, kein EF, keine Runtime-Abhängigkeiten |
| Shell | …HearingPortal.Shell |
serverorientierte Razor-Class-Library: Startseite, Anmeldeeinstieg, Pflichtseiten, Produktmenü-Komposition (kein WebAssembly-Client — Render Mode wählt der Host) | keine Fachpersistenz, keine Fachregeln, keine browserseitige WASM-Runtime |
| Features | Features/{Authorization,Hearings,Institutions} |
die drei fachlichen Slices der Anwendung, siehe 5.2 | siehe Abhängigkeitstabelle unten |
Details zur Anwendungsstruktur: ../../src/Applications/RAL/QualityMarks/HearingPortal/README.md.
5.1.2 Wiederverwendbare CAF-Module
Alle vier Bausteine liegen unter src/Modules/ und sind Quellcode-Bausteine, keine
NuGet-Pakete (IsPackable=false, keine PackageId/Version). Der Host konsumiert sie
ausschließlich über ProjectReference. Je Baustein gilt trotzdem die Vertragsdatei VERTRAG.md
als Quelltext-Vertrag — arch-getestet, siehe
ADR-03.
Die folgende Tabelle fasst zusammen, wie dieses Produkt die Bausteine nutzt. Maßgeblich für
Zweck, Abgrenzung und zugesicherte Fläche der Bausteine selbst ist
docs/CAF/ bzw. die VERTRAG.md am jeweiligen Baustein — diese
Tabelle ersetzt beides nicht.
| Modul | Projekt(e) | Verantwortung | Grenze |
|---|---|---|---|
| Authentication | CAF.Modules.Authentication |
OIDC-Client (Authorization Code + PKCE), Claim-Mapping, Cookie-/Session-Policy, lokale/remote Abmeldung, Security-Stamp-Revalidierung | keine Produktrouten, keine Berechtigungen der Anwendung, keine Realm-/GZ-Rollen-Policy |
| UI.Blazor | CAF.Modules.UI.Blazor |
zentrale Cohesion-Registrierung (AddCohesion), Lizenzprüfung, Bundle-Contributors für CSS/JS, CAFCohesionSmokePanel |
keine Produktroute, keine Fachkomponente, kein Render-Mode-Entscheid des Hosts; keine Cohesion-Fassade für Razor-UI-Assemblies (siehe 5.1.3) |
| Documents.Contracts | CAF.Modules.Documents.Contracts |
DocumentId, vendor-neutrale DTOs, Dokument-/Content-/Share-Interfaces, Permissions, Localization |
exportiert keine Volo.FileManagement-Typen |
| Documents | CAF.Modules.Documents (Runtime) |
dünner Adapter über ABP File Management: validierter Dateiinhalt, Metadatenzugriff, sichere direkte Freigaben; besitzt nur CAFDocumentShareGrants |
ABP besitzt Descriptor und BLOB-Inhalt |
Geplant (Bereich „Unterlagen Dateiablage“, US-15/US-15b/US-15c/US-16/US-16b, größtenteils offen):
eine von einzelnen Verfahren entkoppelte, persönliche/gemeinsame Dateiablage mit Ordnerstruktur,
Upload, Umbenennen/Verschieben/Kopieren und einem zweistufigen Papierkorb (Wiederherstellen/
endgültig löschen); eine Ablagedatei kann später unveränderlich als Version in ein Verfahren
übernommen werden. Wächst das Documents-Modul selbst um diesen Umfang, oder entsteht ein
eigener Baustein darauf, ist bei Umsetzungsbeginn zu entscheiden — dieses Kapitel ist dann
nachzuziehen.
Persistierte Kennungen der Module behalten das Präfix CAF (CAFDocuments*), unabhängig vom
RALQM-Präfix der Anwendung. Details je Modul:
Authentication,
Documents,
UI.Blazor.
5.1.3 Cohesion-Referenz-Ausnahme für Razor-UI-Assemblies
Die vier Razor-UI-Assemblies dieser Anwendung — die Produkt-Shell und die drei
Features/*/Blazor — referenzieren das Cohesion-Paket direkt per PackageReference und
gerade nicht über CAF.Modules.UI.Blazor. Sie erzwingen dazu RZ10012 als Fehler
(WarningsAsErrors), damit ein unaufgelöstes Cohesion-Tag den Build bricht statt still als leeres
HTML-Element zu rendern. Der Host bindet zusätzlich CAF.Modules.UI.Blazor für Registrierung,
Lizenzierung und Bundles ein.
Das ist eine bewusste, Razor-bedingte Ausnahme und keine aufzuräumende Redundanz — Regel,
Begründung (Compile-Time-Razor-Assets) und Rollenteilung stehen in
docs/CAF/ §02 Bausteine.
Verbindliche Arbeitsfassung:
src/Modules/AGENTS.md und
Shell/AGENTS.md.
5.2 Ebene 2: Feature-Slices der Anwendung
Jedes der drei fachlichen Features gliedert sich intern in bis zu drei Teilprojekte —
Contracts, Runtime und optional Blazor. Diese Dreiteilung besteht nur dort, wo die
Grenze echten Nutzen stiftet: Abhängigkeitsrichtung, serverseitige Interception und spätere
Auskopplung. Weitere Layerprojekte (z. B. separates Domain/Application) werden nicht
vorsorglich angelegt — Domain, Application und EF-Persistenz bleiben logische Schichten
innerhalb der Runtime-Assembly.
Features/<Feature>/
Contracts/ DTOs, AppService-Interfaces, Permissions, Localization, öffentliche IDs
Runtime/ Aggregate, Application Services, EF-Mapping (Domain/Application/EF in einer Assembly)
Blazor/ optionale Razor-Class-Library: Seiten, Komponenten, Navigation (nur bei eigener UI)
5.2.1 Authorization
| Teilprojekt | Verantwortung |
|---|---|
Authorization.Contracts |
globale GZ-Permissiongruppe RALQM, Rollenbezeichnungen (RALQMAdministrator, RALQMCaseOfficer, RALQMManagement), Kontoeinladungs-Contracts |
Authorization (Runtime) |
Rollen-/Grant-Komposition für die drei Rollen, lokale ABP-Benutzerprojektion, kontrollierter Keycloak-Admin-Adapter, Mitarbeitenden-Onboarding über bestätigtes Keycloak-Subject (sub) |
Authorization.Blazor |
eigener Kontenpfad: Anlage, Wiedereinladung, Produktrollenwechsel, lokales Sperren/Entsperren strict-sub-projizierter Konten |
Die Authorization-Runtime referenziert Feature-Contracts von Institutions und Hearings, um Rollen zu komponieren, besitzt aber keine Feature-Permissions und keine Hearing-Daten.
5.2.2 Institutions
| Teilprojekt | Verantwortung |
|---|---|
Institutions.Contracts |
Institution-/Kontakt-DTOs, AppService-Interfaces, Permissions, Localization, Menünamen |
Institutions (Runtime) |
Institution-/Kontakt-Aggregat, Aktivität, Typ/Pflichtpartner, Anschrift, CSV-Import/-Export, Nutzungsprüfung, EF-Mapping; besitzt RALQMInstitutions, RALQMInstitutionContacts |
Institutions.Blazor |
Institutionenliste, Bearbeitung, Kontakte, Kontaktsuche, berechtigte Kontoeinladung |
Geplant (US-07, US-13, US-41 — größtenteils offen): eine Institutionsgruppe als verfahrensübergreifend wiederverwendbarer, benannter Beteiligtenkreis (Mitglieder pflegen, umbenennen, auflösen) — bindbar als Ganzes in ein Verfahren (US-13) und adressierbar für Rundnachrichten (US-41), statt Institutionen je Verfahren einzeln zusammenzustellen. Siehe auch Glossar.
Institutions kennt Hearings nur über einen von außen implementierten Nutzungsport (keine Rückabhängigkeit).
5.2.3 Hearings
| Teilprojekt | Verantwortung |
|---|---|
Hearings.Contracts |
DTOs/Interfaces für Anhörungen, Teilnehmer, Dokumente, Freigabe, Versand, Portal, Rückmeldungen, Fristen, Historie, Einstellungen; Feature-Permissions/Localization |
Hearings (Runtime) |
Anhörungs-Aggregat und Lebenszyklus (siehe Laufzeitsicht), Referenznummern, Teilnehmer, Dokumentzuordnung/-versionen/-grants, Vier-Augen-Freigabe, Vorlagen, Postausgang, Einladungen, Kontozugang (Zielzustand) und Gastzugang (auslaufender Übergangspfad, siehe Einführung und Ziele), Stellungnahmen, fällige Aktionen, Dashboard, Historie; besitzt RALQMHearings und die RALQMHearing*-Tabellen |
Hearings.Blazor |
interne Prozessseiten, Dashboard, Vorlagen/Postausgang, externe Portaloberflächen |
5.2.4 Abhängigkeitsrichtung der Feature-Slices
| Consumer | Erlaubte eigene Projektabhängigkeiten |
|---|---|
| Institutions Contracts | Authorization Contracts |
| Hearings Contracts | Authorization Contracts, Documents Contracts |
| Institutions Runtime | Institutions Contracts |
| Authorization Runtime | Authorization-, Institutions-, Hearings- und Documents-Contracts |
| Hearings Runtime | Hearings-, Institutions- und Documents-Contracts |
| Institutions Blazor | Institutions-, Authorization-Contracts, Produkt-Shell-Contracts |
| Authorization Blazor | Authorization-Contracts, Produkt-Shell-Contracts |
| Hearings Blazor | Hearings-, Institutions-Contracts, Produkt-Shell-Contracts |
| Host | Client, Framework Authentication/Documents/UI, Authorization/Institutions/Hearings Runtime+Blazor |
Die Tabelle nennt Projektabhängigkeiten. Die drei *.Blazor-Projekte referenzieren zusätzlich
das Cohesion-Paket direkt und CAF.Modules.UI.Blazor gerade nicht — Begründung in
5.1.3.
Immer verboten: Contracts → Runtime/EF/Host, Feature-Blazor → Feature-Runtime oder eine
fremde Runtime, GZ-Runtime → fremde GZ-Runtime oder deren Entities/Repositories/Tabellen, sowie
jedes Modul → ausführbarer Host. Über Modulgrenzen (auch zwischen Features) werden nur IDs
gespeichert, keine Foreign Keys — siehe Datenmodell in der Verteilungssicht
und Persistenzregeln in Querschnittliche Konzepte §2.
Diese Trennung ist eine Compile-Time-Eigenschaft, durch Architekturtests abgesichert, keine bloße Ordnerkonvention.
5.3 Direkt verwendete ABP-Bausteine
Ergänzend zu den eigenen Modulen nutzt die Anwendung ABP-Vertikalen als Implementierungssubstrat, ohne sie zu eigenen CAF-Modulen zu machen: Account Public/Admin und Identity Pro (Login, lokale Benutzer/Rollen), Permission Management, Setting Management, Audit Logging, Text Template Management, Language Management/GDPR, File Management (Domain/EF/Application; keine generische UI/API) sowie BLOB Storing Database, Emailing/Background Workers und LeptonX/MudBlazor für Produktshell und ABP-Administrationsseiten.
5.4 Zusammenspiel im Request
Wie ein Blazor-Seitenaufruf durch Contract, Application Service, Aggregate und Persistenz läuft, beschreibt die Laufzeitsicht. Betriebs- und Deployment-Aspekte (ein Deployable, eine Datenbank, lokale Infrastruktur) stehen in der Verteilungssicht.
Weiterführend: Entwicklungsleitfaden, Getting Started, Konventionen.
06. Laufzeitsicht
Dieses Kapitel zeigt das dynamische Verhalten der in der Bausteinsicht beschriebenen Module anhand der vier wichtigsten Laufzeitszenarien: Anmeldung, Anhörungs-Lebenszyklus, externe Kontoprovisionierung und Dokumentfreigabe. Alle vier Abläufe sind im aktuellen Stand implementiert — mit den in Risiken und technische Schulden benannten offenen Nachweisen und der noch laufenden Ablösung des Gastzugangs. Details zu Modulgrenzen und Ownership stehen in der Bausteinsicht und den Querschnittlichen Konzepten, verbindliche Bauregeln im Entwicklungsleitfaden.
6.1 Login und Authentifizierung
Keycloak ist Token-Issuer und Identitätsquelle; CAF.Modules.Authentication
(src/Modules/Authentication) ist der anwendungsneutrale OIDC-Client. Er registriert Cookie- und
OIDC-Authentifizierung (CAFAuthenticationServiceCollectionExtensions), setzt eine Cookie-Session-
Policy (CAFApplicationCookieSessionPolicy, CAFApplicationCookieTokenPropagation) und hält die
Session über einen revalidierenden AuthenticationStateProvider
(CAFRevalidatingAuthenticationStateProvider, CAFSecurityStampRevalidationPolicy) periodisch
aktuell.
sequenceDiagram
actor U as Benutzer
participant App as Blazor Web App (Shell)
participant Auth as CAF.Modules.Authentication
participant Kc as Keycloak
participant Id as ABP Identity (lokal)
U->>App: Aufruf einer geschützten Seite
App->>Auth: Challenge auslösen
Auth->>Kc: Redirect Authorization Code + PKCE (code_challenge)
U->>Kc: Anmeldung (Credentials/2FA)
Kc-->>Auth: Redirect mit Code
Auth->>Kc: Code-Tausch inkl. code_verifier
Kc-->>Auth: ID-/Access-/Refresh-Token
Auth->>Auth: Pflicht-Claims prüfen (u. a. sub)
Auth->>Id: lokalen IdentityUser über sub finden/verknüpfen
Id-->>Auth: GZ-Rolle(n) und Grants
Auth-->>App: Cookie-Session gesetzt
App-->>U: Zielseite mit gewährten Permissions
Wesentliche Punkte:
- Die Verknüpfung zwischen Keycloak-Identität und lokalem
IdentityUserverwendet ausschließlich den stabilen OIDC-Subject-Identifiersub, niemals die E-Mail-Adresse. - Rollen (
RALQMAdministrator,RALQMCaseOfficer,RALQMManagement) sind lokale ABP-Grants; sie werden aus den Feature-Contracts komponiert (siehe Authorization und Autorisierung). - Die Cookie-Session wird nicht nur beim Login geprüft:
CAFRevalidatingAuthenticationStateProviderund die Security-Stamp-Policy erzwingen eine periodische Neuprüfung, sodass eine serverseitig gesperrte oder geänderte Identität in laufenden Circuits zeitnah wirkt. - Abmeldung läuft über
CAFAuthenticationSignOutlokal und bei Keycloak; Multi-Tenancy ist deaktiviert, Keycloak-Gruppen ersetzen keine GZ-Rolle oder ABP-Permission. - Jeder nachfolgende Application-Service-Aufruf prüft serverseitig erneut seine Permission — die Anmeldung allein gewährt keinen Fachzugriff.
6.2 Anhörungs-Lebenszyklus
Der Hauptstatus einer Anhörung (RALQMHearings, Feature Hearings) ist:
Draft → PendingApproval → Approved → Dispatched → Adjudication → Completed (zweistufig)
Draft → PendingApproval → Approved → Dispatched ─────────────→ Completed (einstufig)
└────────────────────── benannte Rücksprünge/Abbruch nach Fachregel ──────────────────────┘
Die Wahl der Abschlussvariante (US-33) entscheidet, ob Adjudication als eigener Status
durchlaufen wird — siehe Punkt 7 unten und Einführung und Ziele.
sequenceDiagram
actor R as Referent (RALQMCaseOfficer)
actor M as Geschäftsführung (RALQMManagement)
participant H as Hearings AppService
participant Out as Hearing-Outbox
participant W as ABP Background Worker
R->>H: Anhörung anlegen (Draft)
R->>H: Teilnehmer zuordnen (InstitutionId aus Institutions.Contracts)
R->>H: Dokumente zuordnen (DocumentId aus Documents.Contracts)
R->>H: Zur Freigabe einreichen (PendingApproval)
M->>H: Freigeben (Approved, andere Person als Einreichung, Composition Hash sperrt Stand)
H->>Out: Versand enqueuen (RALQMHearingOutboxMessages)
Out->>Out: Sendeversuche mit Retry und Idempotency Key (RALQMHearingSendAttempts)
H-->>H: Dispatched
Note over H: Adjudication-Fenster: nur Endfassungen neu/ersetzt
W->>H: Fristen als Due Actions verarbeiten (RALQMHearingDueActions)
H->>H: Rückmeldungen/Antworten der Teilnehmer (RALQMHearingResponses, ...Replies)
M->>H: Bescheidung versenden (Fenster schließt, Komposition fixiert)
H-->>H: Completed
- Anlegen: Draft entsteht über den owning Application Service; jede Statustransition ist eine
benannte Aggregate-Operation (kein
SetStatus). - Teilnehmer/Institution: Hearings referenziert Institutionen nur über die
InstitutionIdausInstitutions.Contracts— keine Cross-Module-Navigation oder -Foreign-Key. - Freigabe: Der Wechsel nach
Approvederzwingt eine andere handelnde Person als die Einreichung und sperrt die geprüfte Dokumentzusammensetzung über einen Composition Hash. - Versand: Kein "Best effort" im Request — eine persistente Hearing-Outbox mit Sendeversuchen, Retry und Idempotency Keys entkoppelt Versand von der HTTP-Antwort.
- Fristen/Antworten: Persistente Due Actions und ABP Background Workers lösen fällige Aktionen aus; Konto- und (Übergangsweise noch) Gastpfad legen ihre Stellungnahme innerhalb der wirksamen Frist an oder ändern sie mit dem zuletzt gelesenen Concurrency-Stand.
- Adjudication-Fenster (nur zweistufige Abschlussvariante): Ab Eintritt in
Adjudicationdürfen bis zum ersten Bescheidungsversand nur als Endfassung markierte Dokumente additiv angelegt, ersetzt oder freigegeben werden; die ursprünglich freigegebene Komposition bleibt nachvollziehbar. - Abschluss: Bei zweistufiger Variante schließt der Bescheidungsversand das Fenster, fixiert
die versendete Komposition samt Prüfsumme und überführt die Anhörung nach
Completed. Bei einstufiger Abschlussvariante (US-33) gehen Bescheidung und Endfassung in einem Vorgang hinaus, ohne denAdjudication-Status gesondert zu durchlaufen. - Neben ABP Audit Logging führt Hearings eine PII-arme fachliche Historie
(
RALQMHearingHistoryEntries); Freitexte, Dokumentinhalte und Zugangstokens erscheinen dort nie.
Details zu Datenownership und Tabellen stehen in Querschnittliche Konzepte §2.
6.3 Externe Kontoprovisionierung
Zwei getrennte, beide fail-closed arbeitende Pfade nutzen denselben kontrollierten
Keycloak-Admin-Port (IKeycloakAdminClient/KeycloakAdminHttpClient im Feature Authorization)
über den dedizierten vertraulichen Client caf-qualitymarks-provisioning (nur Rolle
manage-users, kein Browser-/Direktzugriffs-Flow).
sequenceDiagram
actor U as Berechtigter Benutzer
participant UI as Institutions/Authorization.Blazor
participant Auth as Authorization AppService
participant Kc as Keycloak Admin API
alt Kontakteinladung (aus Kontaktsuche)
U->>UI: Kontakt in institutionsübergreifender Suche wählen
UI->>Auth: stabile Kontakt-ID übergeben
Auth->>Kc: Konto per E-Mail suchen
alt kein/eindeutiges Match
Auth->>Kc: Konto anlegen bzw. Subject bestätigen
Kc-->>Auth: sub
Auth-->>UI: Einladung versendet (keine automatische Fachrolle)
else mehrdeutig
Auth-->>UI: Fehler AmbiguousEmail (geschlossen abgelehnt)
end
else Mitarbeitendenkonto (/administration/mitarbeitendenkonten)
U->>UI: Name, E-Mail, genau eine GZ-Produktrolle
UI->>Auth: Anlage-/Wiedereinladungsauftrag
Auth->>Kc: Subject strikt bestätigen
Kc-->>Auth: sub
Auth->>Auth: lokale ABP-Projektion + Rollen-Grant setzen
Auth-->>UI: Konto aktiv, ggf. Wiedereinladung nur bei ausdrücklicher Wahl
end
- Kontaktbasierte Einladung: ausgelöst aus der institutionsübergreifenden Kontaktsuche; die UI
übergibt nur die stabile Kontakt-ID an den Provisionierungsport. E-Mail-Gleichheit erteilt keine
Fachberechtigung — Identitäten werden über
subkorreliert. Ein bereits vorhandenes eindeutiges Konto erhält eine erneute Einladung statt eines Dublettenkontos; es wird keine automatische Hearing-Teilnahme oder Fachrolle vergeben. - Mitarbeitendenkonto: einziger Anlage-/Wiedereinladungseinstieg ist die GZ-Seite
/administration/mitarbeitendenkonten. Sie verlangt Name, E-Mail und genau eine Produktrolle, bestätigt das Keycloak-Subject strikt und weist die Rolle erst der lokalen ABP-Projektion zu. Wiederholte Aufrufe versenden ohne ausdrücklich gewählte Wiedereinladung keine neue E-Mail. Rollenwechsel, Sperren und Entsperren sind optimistisch concurrency-geschützte, auditierte GZ-Writes und wirken nur lokal (keine globale Keycloak-Sperre). - Fehlerfälle scheitern geschlossen und sind stabil kodiert:
ExternalAccountProvisioningErrorCodes(RALQM.Authorization:Provisioning:Disabled| AmbiguousEmail|SubjectConfirmationFailed|InvalidSubject) für den Kontaktpfad,StaffAccountErrorCodes(RALQM.Authorization:StaffAccounts:InvalidInput|InvalidRole| LocalUserNotFound|ProductRoleNotFound|NotExternallyLinked|ConcurrencyConflict) für Mitarbeitendenkonten. - Die ABP-eigene Benutzerseite ist für keinen der beiden Pfade ein unterstützter GZ-Weg und wird von den GZ-Seiten nicht verlinkt.
6.4 Dokumentfreigabe
ABP File Management besitzt Dateimetadaten und BLOB-Inhalt; CAF.Modules.Documents
(src/Modules/Documents) kapselt sie hinter dem neutralen, permission-freien Port
IDocumentContentPort. Permission-Freiheit bedeutet: Der aufrufende GZ-Use-Case muss die
Objektberechtigung vollständig geprüft haben, bevor er den Port aufruft — eine DocumentId, URL
oder ein Dateiname verleiht selbst nie Zugriff. Vier Zugriffswege bleiben strikt getrennt; der
Gastlink ist dabei der auslaufende Übergangspfad zur kontogebundenen Teilnahme als Zielzustand
(siehe Einführung und Ziele):
sequenceDiagram
actor P as Zugreifende Partei
participant F as Feature-Prüfung (Hearings/Institutions)
participant Grant as Hearing-Dokument + Teilnehmergrant
participant Port as IDocumentContentPort
participant Fm as ABP File Management + BLOB
alt Interner Benutzer
P->>F: Permission + Hearing-Objektprüfung
else Gastlink
P->>F: Einladung, Hearing-Status, aktiver Teilnehmer neu validieren
F->>Grant: Dokumentgrant prüfen
else Angemeldeter Teilnehmer
P->>F: aktive Kontobindung + Teilnehmerprüfung
F->>Grant: Dokumentgrant prüfen
else Direkter CAF-Share
P->>F: Token-Hash + Ablauf + Widerruf prüfen (DocumentShareGrantStore)
end
F->>Port: OpenAsync(DocumentId) — erst nach vollständiger Autorisierung
Port->>Fm: Descriptor + BLOB lesen
Fm-->>P: Inhalt (no-store, Rate Limiting bei anonymen Pfaden)
- Intern: ABP Permission plus Hearing-/Dokumentprüfung vor dem Aufruf des Content-Ports.
- Gastlink (Übergangspfad): Einladung, Ablauf/Widerruf, Hearing-Status, aktiver Teilnehmer und Dokumentgrant werden bei jedem Zugriff neu geprüft — der geschützte Browser-/Circuit-Kontext ist nur Transportmittel. Eine gültige Einladung darf in mehreren Sitzungen verwendet werden; parallele Einlösungen laufen in einer frischen, begrenzt wiederholten Transaktion mit vollständiger Revalidierung.
- Kontogebunden (Zielzustand): aktueller ABP-Benutzer, aktive Kontobindung und derselbe Objekt-/Grant-Pfad
werden erneut geprüft. Kanonischer Einstieg ist
/portal/zugang-uebernehmen(/portal/teilnahme-beanspruchenbleibt Kompatibilitätsalias). - Direkter CAF-Share: eigenständiger, bewusst anonymer Use Case über
CAFDocumentShareGrants/DocumentShareGrantStore— Secret wird nur gehasht gespeichert, läuft ab, ist einzeln widerrufbar und rate-limitiert. Er ersetzt nie den Hearing-Gastlink. - Stellungnahme-Anhänge folgen denselben drei Mustern (intern:
Responses.Read+ ID-Kette; Gast/ Konto: Revalidierung plus Beschränkung auf die eigene Rückmeldung), bevor die interneDocumentIdan den Content-Port übergeben wird. - Jeder abgelehnte Binärzugriff liefert eine nicht enumerierbare 404-Antwort mit
no-storeundnosniff; Descriptor-ID und Dateiname autorisieren nie.
Weiterführend: Sicherheitskonzepte, Bausteinsicht und die Betriebssicht für Kennzahlen/Korrelation in GETTING-STARTED.md §10a.
07. Verteilungssicht
1. Überblick
CAF.Applications.RAL.QualityMarks.HearingPortal (RALQM, "RAL Gütezeichen Anhörungsportal") läuft lokal als Zweiknoten-Deployment: ein nativer .NET-Prozess (der Blazor-Server-Host) und eine Docker-Compose-Infrastruktur mit SQL Server, Keycloak und Mailhog. Es gibt keine separate Web-/API-/Worker-Trennung — Host, Application Services, Blazor-UI, EF-Migrationen und Hintergrundarbeiter laufen im selben Prozess (siehe Bausteinsicht für die fachliche Modulaufteilung, Kontextabgrenzung für die Nachbarsysteme).
┌─────────────────────────────── Windows-Workstation ───────────────────────────────┐
│ │
│ dotnet-Prozess (Kestrel, Blazor Interactive Server) │
│ CAF.Applications.RAL.QualityMarks.HearingPortal (Host) │
│ https://localhost:44356 │
│ - Shell, Features/{Authorization,Hearings,Institutions}, Contracts │
│ - CAF.Modules.{Authentication, Documents, UI.Blazor} │
│ - EF Core (SQL Server Provider), ABP Background Worker │
│ │
│ │ SQL (TCP 127.0.0.1:1433) │ OIDC (HTTP 127.0.0.1:8080) │ SMTP │
│ ▼ ▼ ▼ │
│ ┌───────────────────────── Docker Compose: backend-Netzwerk ──────────────────┐ │
│ │ │ │
│ │ sqlserver keycloak mailhog │ │
│ │ mssql/server:2022 keycloak:26.7.0 mailhog:v1.0.1 │ │
│ │ DBs: RALQM, keycloak Realm "caf", start-dev SMTP-Fänger, In-Memory │ │
│ │ Volume: sqlserver_data (extern, persistent) │ │
│ │ │ │
│ │ sqlserver-init (init-Container, läuft einmalig) │ │
│ │ keycloak-provisioner (init-Container, läuft einmalig) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────┘
Alle Compose-Ports sind ausschließlich an 127.0.0.1 gebunden; es gibt keinen produktiven
Cloud- oder Cluster-Knoten in diesem Repository. Produktionsbetrieb, Backupstrategie und
Monitoring werden aus dieser lokalen Development-Umgebung nicht abgeleitet
(siehe DEVELOPMENT-GUIDE.md, Abschnitt "Docker-Compose-Umgebung ist
ausschließlich für lokale Development-Daten").
2. Knoten und Erreichbarkeit
| Knoten | Lokale Adresse | Zweck |
|---|---|---|
| Anwendung (Host-Prozess) | https://localhost:44356 |
Produkt- und Administrationsoberfläche (Blazor Interactive Server) |
| Keycloak | http://localhost:8080 |
OIDC-Login, Realm caf |
| Mailhog UI | http://127.0.0.1:8025 |
Development-Mails ansehen |
| Mailhog SMTP | 127.0.0.1:1025 |
Lokaler SMTP-Fänger für Versand aus Hearings und Keycloak |
| SQL Server | 127.0.0.1:1433 |
Datenbanken RALQM (Anwendung) und keycloak |
APP_URL, KEYCLOAK_PUBLIC_URL und SQLSERVER_PORT kommen aus etc/docker/.env; obige Werte
sind die mitgelieferten Development-Defaults. Die Mailhog-Ports liegen fest im Compose-File. Der
Host bindet Kestrel über launchSettings.json
(src/Applications/RAL/QualityMarks/HearingPortal/Host/Properties/launchSettings.json) an
denselben Port 44356, mit dem APP_URL übereinstimmen muss — beide Werte werden beim Start
gegeneinander geprüft (siehe GETTING-STARTED.md, Abschnitt 1 und 6).
3. Docker-Compose-Dienste
Definiert in etc/docker/docker-compose.yml
(Compose-Projekt ral-qualitymarks-hearing-portal):
| Dienst | Image | Rolle | Lebenszyklus |
|---|---|---|---|
sqlserver |
mcr.microsoft.com/mssql/server:2022-latest (Digest-gepinnt) |
Persistenter SQL-Server-Provider für Anwendung und Keycloak | restart: unless-stopped, Healthcheck via sqlcmd |
sqlserver-init |
dasselbe SQL-Server-Image | Einmaliger Init-Container: legt RALQM-Login (RALQM-login.sql) und keycloak-DB (keycloak-database.sql) an |
restart: "no", läuft nach sqlserver healthy, terminiert nach Erfolg |
keycloak |
quay.io/keycloak/keycloak:26.7.0 (Digest-gepinnt) |
OIDC-Provider, start-dev --import-realm, persistiert im mssql-Backend (DB keycloak) |
restart: unless-stopped, Healthcheck gegen /health/ready |
keycloak-provisioner |
dasselbe Keycloak-Image | Einmaliger Init-Container: reconciliiert Realm/Client/Mapper/Gruppen/Development-Benutzer/SMTP idempotent (reconcile.sh) |
restart: "no", läuft nach keycloak healthy |
mailhog |
mailhog/mailhog:v1.0.1 (Digest-gepinnt) |
Development-SMTP-Fänger, In-Memory, keine Volumes/Credentials | restart: unless-stopped, Healthcheck gegen die Web-UI |
Alle Dienste hängen am Bridge-Netzwerk ral-qualitymarks-hearing-portal-backend und schreiben
begrenztes JSON-File-Logging (max-size: 10m, max-file: 3). Die kausal abhängige Kette
SQL → Principal → Keycloak → Reconcile → Migration läuft laut
DEVELOPMENT-GUIDE.md (Abschnitt 13) nie parallel; lediglich Keycloak und
Mailhog starten beim Kaltstart gleichzeitig.
Persistentes Volume
Genau ein externes, benanntes Volume trägt den gesamten Datenbankzustand:
volumes:
sqlserver_data:
name: ral-qualitymarks-hearing-portal-sqlserver-data
external: true
external: true bedeutet: Compose legt es nicht implizit an — die Lifecycle-Skripte
(start.cmd/setup.cmd) erzeugen und besitzen es explizit. Es überlebt stop.cmd und
restart.cmd. Der einzige destruktive Standardpfad ist reset.cmd, strikt begrenzt auf
ENVIRONMENT_NAME=Development und exakt dieses Volume (siehe
GETTING-STARTED.md, Abschnitt 5).
.env-gesteuerte Konfiguration
Die einzige lokale, nicht versionierte Compose-Konfigurationsdatei ist etc/docker/.env
(Vorlage: etc/docker/.env.example). Sie legt Image-Digests,
Ports, SQLSERVER_SA_PASSWORD, RALQM_DB_PASSWORD, KEYCLOAK_ADMIN_PASSWORD,
KEYCLOAK_DB_PASSWORD, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_PROVISIONING_CLIENT_SECRET,
KEYCLOAK_DEV_ADMIN_PASSWORD, KEYCLOAK_DEV_USER_PASSWORD, APP_URL und TIME_ZONE fest.
setup.cmd erzeugt die Datei aus der Vorlage und ersetzt jeden CHANGEME-Platzhalter durch
einen zufälligen lokalen Wert, ohne bestehende Werte zu überschreiben. Secrets erscheinen nie im
Repository; Diagnosewege (doctor.cmd) melden nur SET/MISSING (siehe Abschnitt 13 im
DEVELOPMENT-GUIDE.md und Querschnittskonzepte
für die vollständige Secret-Regel).
4. Der Host-Prozess
Der ausführbare Knoten ist ein einzelner .NET-Prozess: das Projekt
src/Applications/RAL/QualityMarks/HearingPortal/Host/CAF.Applications.RAL.QualityMarks.HearingPortal.csproj,
gestartet nativ auf Windows (kein Container). Er bündelt in einem Prozess:
- Kestrel-Endpunkt mit Blazor Interactive Server Rendering (kein WASM-Bootstrap),
- die Composition Root, die
Shell,Contractsund alleFeatures/*- sowieCAF.Modules.*-Bausteine referenziert (siehe Bausteinsicht), entsprechend der No-Layer-Architektur aus dem Root-AGENTS.md, - den EF-Core-DbContext samt Migrations-Host
(
src/Applications/RAL/QualityMarks/HearingPortal/Host/Migrations), - ABP Background Worker für Postausgang/Fristenaktionen (Outbox, Due Actions).
Konfigurationsquellen werden in fester Priorität zusammengeführt (siehe DEVELOPMENT-GUIDE.md, Abschnitt 13):
versionierte appsettings-Dateien
< abgebildete etc/docker/.env (nur Development-Workstation)
< appsettings.local.secrets.json
< Umgebungsvariablen
< Kommandozeilenargumente
Host/Configuration/DevelopmentDockerEnvironmentConfiguration.cs liest dafür nur in
IsDevelopment() die Datei etc/docker/.env (relativ zum per CAF.slnx-Marker gefundenen
Repository-Root) und leitet daraus App:SelfUrl, App:RedirectAllowedUrls,
ConnectionStrings:Default (Server 127.0.0.1,<SQLSERVER_PORT>, Datenbank RALQM, Login
ralqm), Authentication:Authority (<KEYCLOAK_PUBLIC_URL>/realms/caf) sowie die
Keycloak-Provisioning-Werte ab. Damit liest F5 aus Visual Studio dieselbe Laufzeitkonfiguration
wie start.cmd, ohne einen vorgeschalteten PowerShell-Prozess zu benötigen. Fehlt .env oder ist
ein Pflichtwert noch CHANGEME, bricht der Start fail-closed mit einer expliziten Meldung ab.
5. Migration und Seed (--migrate-database)
Schema und Demodaten werden nicht implizit beim normalen Start ausgeführt, sondern über denselben Host-Prozess mit einem expliziten Kommandozeilenschalter:
dotnet run --project src/Applications/RAL/QualityMarks/HearingPortal/Host -- --migrate-database
Program.cs erkennt --migrate-database (IsMigrateDatabase), schaltet auf ein
Migrations-Logging-Profil um und ruft nach InitializeApplicationAsync() den
HearingPortalDbMigrationService auf. Dieser läuft in drei Schritten:
EnsureMigrationsExist()— derHearingPortalMigrationPathGuardsucht ab dem Startverzeichnis nach demCAF.slnx-Repository-Marker und verlangt, dasssrc/Applications/RAL/QualityMarks/HearingPortal/Host/Migrationsexistiert und mindestens eine echte Migrationsklasse enthält (kein.Designer.cs, keinModelSnapshot.cs) — ein Schutz gegen einen falsch aufgesetzten Arbeitsbaum.MigrateDatabaseSchemaAsync()— führt die EF-Core-Migrationen über denHearingPortalDbSchemaMigratorgegen dieRALQM-Datenbank aus.SeedDataAsync()— ruft ABPsIDataSeedermit einem zufällig erzeugten, deaktivierten Bootstrap-Administratorpasswort auf (kein festes Secret im Code).
Praktisch läuft dieser Schritt in den Lifecycle-Skripten mit einem separaten, nur für den
Bootstrap privilegierten SQL-Zugang; danach reconciliiert start.cmd den dauerhaften,
eingeschränkten qualitymarks-Datenbankprincipal (siehe
GETTING-STARTED.md, Abschnitt 6, Schritt 9). Migration und Seed sind
idempotent: ein wiederholter Lauf legt keine Duplikate an und überschreibt keine vorhandenen
Demodaten (setup.cmd -WithDemoData erzwingt den Abgleich bewusst erneut).
6. Lokaler Start- und Betriebsweg
Der schlanke, skriptlose Pfad — vorausgesetzt etc/docker/.env und
appsettings.local.secrets.json liegen bereits vor:
# 1. Infrastruktur (SQL Server, Keycloak, Mailhog)
docker compose -f etc/docker/docker-compose.yml up -d
# 2. Schema/Seed (nur beim ersten Start bzw. nach neuen Migrationen)
dotnet run --project src/Applications/RAL/QualityMarks/HearingPortal/Host -- --migrate-database
# 3. Anwendung starten
dotnet run --project src/Applications/RAL/QualityMarks/HearingPortal/Host
Der komfortable, empfohlene Weg sind die Root-.cmd-Wrapper (setup.cmd, start.cmd,
status.cmd, stop.cmd, restart.cmd, reset.cmd, doctor.cmd, logs.cmd), dünne Aufrufer der
kanonischen PowerShell-Skripte unter tools/scripts. start.cmd führt deterministisch aus:
SDK-/Zertifikatsprüfung → Restore → Docker-/Volume-Prüfung → SQL Server + Init-Container →
Keycloak + Mailhog parallel → Keycloak-Reconciliation → Migration/Seed →
qualitymarks-Principal-Reconciliation → Start des Host-Prozesses als versteckter
Windows-Prozess (PID-Datei unter %LOCALAPPDATA%\CAF.Applications.RAL.QualityMarks.HearingPortal).
Details, Idempotenz-Garantien und alle Kommandovarianten stehen in
GETTING-STARTED.md, Abschnitte 4–6.
7. Zuordnung Bausteine → Infrastruktur
| Baustein | Läuft in | Persistiert/kommuniziert über |
|---|---|---|
Features/Authorization, Features/Hearings, Features/Institutions |
Host-Prozess (in-process) | EF Core → SQL Server (RALQM, Tabellenpräfix RALQM…) |
CAF.Modules.Documents(.Contracts) |
Host-Prozess | ABP File Management → SQL Server (RALQM) |
CAF.Modules.Authentication |
Host-Prozess | OpenIddict-Client gegen Keycloak (http://localhost:8080, Realm caf) |
CAF.Modules.UI.Blazor |
Host-Prozess (Blazor Interactive Server) | Cohesion-Assets aus wwwroot, kein externer Dienst |
| Shell, Contracts | Host-Prozess | keine eigene Persistenz |
Hearing-Versand (IEmailSender) |
Host-Prozess (Background Worker) | SMTP → Mailhog (127.0.0.1:1025) |
| Keycloak-Provisioning (Mitarbeitenden-/Gastkonten) | Host-Prozess | Keycloak Admin-REST via dediziertem Service-Client caf-qualitymarks-provisioning |
| EF-Migrationen | Host-Prozess (--migrate-database) |
SQL Server (RALQM) |
Es gibt keine eigenständig deploybaren Knoten je Feature-Modul: die No-Layer-Architektur
(Root-AGENTS.md) bündelt alle Fachmodule im selben Host-Prozess; nur die
Infrastrukturdienste (SQL Server, Keycloak, Mailhog) sind eigene Container. Ein produktiver
Mehrknotenbetrieb, Load Balancing oder eine Cloud-Zielumgebung sind nicht Bestandteil dieses
Repositories.
8. Siehe auch
- Bausteinsicht — fachliche und technische Modulaufteilung innerhalb des Host-Prozesses.
- Laufzeitsicht — Ablauf einzelner Anwendungsfälle über diese Knoten hinweg.
- Querschnittskonzepte — Secret-Handling, Konfigurationspriorität, Auditierung.
- GETTING-STARTED.md — vollständiger Einstieg inkl. Lifecycle-Skripten, Diagnose und Kennzahlen.
- DEVELOPMENT-GUIDE.md — Konfigurationsregeln (Abschnitt 13) und Qualitätsgate (Abschnitt 15).
etc/docker/docker-compose.yml— normative Dienstdefinition.
08. Querschnittliche Konzepte
Dieses Kapitel bündelt Konzepte, die mehrere Module gleichermaßen betreffen: Authentifizierung/ Autorisierung, Persistenz, persistierte Kennungen, Lokalisierung, UI-Grenzen, Fehlerbehandlung und Teststrategie. Die Modulgrenzen selbst stehen in Bausteinsicht, der konkrete Ablauf eines Requests in Laufzeitsicht.
1. Authentifizierung und Autorisierung
Keycloak (OIDC, Authorization Code + PKCE)
→ CAF.Modules.Authentication (Cookie-Session, Claim-Mapping, Revalidierung)
→ lokaler ABP IdentityUser, gebunden über den stabilen OIDC-Subject "sub"
→ RALQM-Rolle (Administrator/CaseOfficer/Management)
→ ABP Permission (RALQM.<Feature>…)
→ Application Service + Objektprüfung
- Keycloak ist alleiniger Token-Issuer und Identitätsquelle; die Anwendung betreibt keinen lokalen OpenIddict-Server.
CAF.Modules.Authenticationist der wiederverwendbare, anwendungsneutrale OIDC-Client (Authorization Code mit PKCE, Cookie-Session-Policy, Token-Propagierung, revalidierenderAuthenticationStateProvider, Sign-out). Er kennt weder GZ-Rollen noch Permissions — siehesrc/Modules/Authentication/README.md.- ABP Identity speichert das lokale Benutzerabbild. Die Verknüpfung verwendet ausschließlich
den stabilen
sub, niemals die E-Mail-Adresse. HearingPortal.Authorizationdefiniert die produktweiten Rollen (HearingPortalRoles:RALQMAdministrator,RALQMCaseOfficer,RALQMManagement) und komponiert deren Grants aus den Permissions der Feature-Contracts. Ein neues Feature-Recht wird nicht zentral in Authorization deklariert, sondern im owning{Feature}.Contracts-Projekt.- Die Permission-Gruppe heißt
RALQM(HearingPortalPermissions.GroupName); Feature-Permissions hängen sich darunter an, zum BeispielRALQM.Hearings.ApproveoderRALQM.Accounts.Invite. - Jeder Application Service prüft seine Permission serverseitig; Objektzugriff, Status und Teilnehmerzuordnung werden zusätzlich im owning Feature geprüft. Ein versteckter Button ist keine Autorisierung.
Kontoprovisionierung
Zwei getrennte, kontrollierte Wege binden Menschen an Keycloak-Konten — beide über denselben schmalen, fail-closed Provisionierungsport, nie über E-Mail-Gleichheit:
- Institutionskontakte: aus der institutionsübergreifenden Kontaktsuche ausgelöst, korreliert
über
sub; ein vorhandenes Konto erhält eine erneute Einladung statt eines Dublettenkontos, Mehrdeutigkeit scheitert geschlossen (ExternalAccountProvisioningErrorCodes). - Mitarbeitendenkonten: ausschließlich über
/administration/mitarbeitendenkonten, mit Name, E-Mail und genau einer Produktrolle; Rollenwechsel sowie Aktivieren/Sperren/Entsperren sind optimistisch concurrency-geschützte, auditierte lokale Writes (StaffAccountErrorCodes). Die ABP-Benutzerseite ist kein unterstützter GZ-Kontenpfad.
Details zu Dokument- und Portalzugriffswegen (intern, Kontobindung als Zielzustand, Gastlink als auslaufender Übergangspfad, Direkt-Share) stehen in Laufzeitsicht; Multi-Tenancy ist deaktiviert, Keycloak-Gruppen ersetzen keine GZ-Rollen oder ABP-Permissions.
2. Persistenz und EF Core
- SQL Server ist der einzige relationale Provider; es gibt genau einen
HearingPortalDbContextund einen Migration Host im ProjektHearingPortal(Host). - Jedes Modul definiert seine Entities und eine eigene
Configure{Feature}()-ModelBuilder- Erweiterung in seiner Runtime; der Host ruft diese Erweiterungen nur auf und besitzt keine fachliche Konfiguration. - Migrationen werden ausschließlich im Host erzeugt und ausgeführt
(
HearingPortal/Migrations), gegen eine echte, disposable SQL-Server-Datenbank getestet und mitdotnet ef migrations has-pending-model-changesals Pending-Model-frei nachgewiesen. - Beziehungen innerhalb eines Moduls verwenden echte Foreign Keys und Indizes. Über Modulgrenzen
werden ausschließlich IDs gespeichert (
InstitutionId,DocumentId) — keine Cross-Module- Navigation, kein Cross-Module-Foreign-Key. - Mutable Aggregate tragen optimistische Concurrency (
ConcurrencyStamp); stale Schreibzugriffe scheitern deterministisch statt still zu überschreiben. Zeitpunkte sind UTC über ABPsIClock. - Fachliche/auditrelevante Daten werden nicht hart gelöscht: Institutionen erst nach Nutzungsprüfung, der Hearing-Prozess über benannte Zustände (Abbruch, Widerruf, Zurückziehen).
Data-preserving Renames
Die laufende RALQM-Umbenennung zeigt das verbindliche Muster für Identifier-Änderungen: Die
Migration RenamePersistedIdentifiersToRALQM
(HearingPortal/Host/Migrations/20260825174437_RenamePersistedIdentifiersToRALQM.cs) benennt
Tabellen (z. B. QualityMarksHearings → RALQMHearings, QualityMarksInstitutions →
RALQMInstitutions, CafDocumentShareGrants → CAFDocumentShareGrants) und Indizes per
RenameTable/RenameIndex um, statt Daten zu löschen und neu anzulegen. Bereits ausgerollte
Migrationen behalten dabei ihre historischen Bezeichner (z. B. Constraint-Namen wie
FK_GzHearings_… bleiben in älteren Migrationsschritten unverändert); nur der aktuelle
Modellstand und neue Migrationen verwenden das neue Präfix. Diese Regel gilt für jede künftige
Umbenennung persistierter Kennungen.
3. Persistierte Kennungen
Zwei getrennte Präfixräume, konsequent nach Ownership:
| Präfix | Geltungsbereich | Beispiele |
|---|---|---|
RALQM |
anwendungsspezifisch (Gütezeichen-Anhörungsportal) | Tabellen RALQMHearings, RALQMInstitutions; Permission-Gruppe RALQM; Fehlercode-Namespace RALQM.<Feature>:…; Rollen RALQMAdministrator, RALQMCaseOfficer, RALQMManagement; Lokalisierungsressourcen RALQMHearings, RALQMInstitutions |
CAF |
wiederverwendbare, anwendungsneutrale CAF-Module | Tabelle CAFDocumentShareGrants (CAF.Modules.Documents) |
Technische Slugs (URLs, Docker-/Config-Bezeichner) bleiben bewusst kleingeschrieben, zum Beispiel
ral-qualitymarks, und folgen nicht dem Groß-Präfix der persistierten Kennungen. Projekt- und
Namespace-Namen (CAF.Applications.RAL.QualityMarks.HearingPortal.*) sind von dieser Konvention
unabhängig und ändern sich durch die Umbenennung nicht.
Diese Trennung ist mehr als Namenskosmetik: Sie macht Ownership beim Lesen einer Tabelle, eines
Fehlercodes oder einer Permission sofort erkennbar, ohne den Quellcode zu öffnen, und verhindert,
dass ein wiederverwendbares CAF-Modul unbemerkt anwendungsspezifisches Wissen aufnimmt (siehe
Bausteinsicht zur Modulgrenze CAF.* → HearingPortal.* verboten).
4. Lokalisierung
- UI-Texte sind Deutsch (
de-DEals führende Kultur), Code-Identifier und Kommentare Englisch, Logs strukturiert/Englisch. - Jedes Feature-Contracts-Projekt besitzt seine eigene Lokalisierungsressource und DE/EN-JSON,
zum Beispiel
HearingPortal.Authorization.Contracts/Localization/Authorization/{de-DE,en}.json. DE- und EN-Schlüssel müssen in beide Richtungen übereinstimmen; hart codierte UI- oder Business-Exception-Texte sind verboten. - Fehlercode-Namespaces folgen demselben
RALQM.<Feature>:-Schema wie die Fehlercodes selbst und werden perAbpExceptionLocalizationOptions.MapCodeNamespacean die Ressource des jeweiligen Feature-Contracts-Projekts gebunden (sieheHearingPortalAuthorizationContractsModule.ConfigureServices:MapCodeNamespace("RALQM.Authorization", typeof(HearingPortalAuthorizationResource))). Ein Fehlercode wieRALQM.Authorization:StaffAccounts:ConcurrencyConflictlöst damit automatisch den passenden lokalisierten Text auf. - ABP Language Management liefert den datenbankgestützten Sprach-/Textprovider im Betrieb; GDPR ergänzt den Cookie-Consent-Pfad.
5. UI- und Cohesion-Grenzen
- Eigene CAF-/GZ-Oberflächen rendern ausschließlich Cohesion (Syncfusion); MudBlazor, Syncfusion-Rohkomponenten oder Blazorise dürfen von Fachcode nicht direkt verwendet werden. Ausgenommen sind die unverändert übernommenen ABP-Administrations-, Account- und LeptonX-Seiten.
CAF.Modules.UI.Blazorregistriert Cohesion, Lizenzprüfung, Assets/Bundles und die Interactive-Server-Policy zentral; es besitzt selbst keine Produktroute.- Feature-Komponenten setzen kein
@rendermodeund bleiben render-mode-neutral; der Host wählt Interactive Server. JavaScript-Interop läuft prerender-sicher erst nach interaktivem Rendern. - Jede datengebundene Oberfläche behandelt, soweit fachlich relevant: Loading, Empty, Error/Retry, Forbidden, Validation, Saving und Stale Concurrency mit Neu-Laden-Option.
- WCAG 2.1 AA ist Mindestziel: semantisches HTML, sichtbarer Tastaturfokus, zugeordnete Labels und nicht ausschließlich farbliche Statusanzeige.
6. Fehlerbehandlung
- User-facing Fehler verwenden stabile, im owning Feature definierte Error Codes nach dem Schema
RALQM.<Feature>:<Bereich>:<Grund>(z. B.RALQM.Authorization:Provisioning:AmbiguousEmail,RALQM.Authorization:StaffAccounts:InvalidRole) statt Klartext oder generischer Exceptions. - Jeder Fehlercode ist über den Code-Namespace des Feature-Contracts-Projekts lokalisiert (siehe Abschnitt 4) — ein neuer Fehlercode ohne DE/EN-Paritätstext gilt als unvollständig.
- Geschützte Application Services unterscheiden deterministisch zwischen Validation, Not Found, Forbidden und Concurrency, soweit diese Unterscheidung keine Information an anonyme Nutzer leakt. Bei Ablehnung bleibt die Persistenz unverändert.
- Für Gastzugriffe (Portal, Dokument-Downloads, Direkt-Share) gilt bewusst das Gegenteil:
unterschiedliche interne Ablehnungsgründe werden auf dieselbe äußere Antwort abgebildet (nicht
enumerierbare Fehlerform,
no-store,nosniff), damit IDs oder Tokens nicht erraten werden können. - Externe Calls (Keycloak-Admin-Port,
IEmailSender) klassifizieren Fehler als Validation, Not Found, Authorization, Conflict oder Unavailable und verwenden Idempotency Keys statt stiller Wiederholung mit Nebenwirkung.
7. Teststrategie
Die Lösung trennt Nachweise nach Risiko statt einer einzigen Testebene zu vertrauen:
| Ebene | Prüft |
|---|---|
| Architekturtests | Projekt-, Paket-, Modul- und Quellcodegrenzen (z. B. CAF.* → HearingPortal.* verboten) |
| Modul-/Application-Tests | Invarianten, Permissions (positiv/negativ über den realen ABP-Proxy), Fehler, Idempotenz |
| SQL-Server-Integrationstests | Mapping, Constraints, Concurrency, echte Rundreisen, from-empty |
| bUnit-Tests | gerenderte Zustände, Fokusverhalten, Cohesion-Provider-Scan |
| BrowserGate (separat, exklusiv) | realer Stack, Anmeldung, Hauptprozess, Permission-Denial, Assets, responsive Bedienbarkeit, axe/WCAG |
Der zentrale Einstieg ist tools/scripts/Invoke-QualityGate.ps1: Preflight, Restore, Build ohne
Warnings/Errors, alle Solutiontests und dotnet ef migrations has-pending-model-changes.
Übersprungene Pflichttests gelten als Fehlschlag; vor Übergabe oder Merge ist der vollständige
Lauf inklusive -IncludeBrowserGate verbindlich. Versionierte Git-Hooks (tools/git-hooks)
führen das Gate zusätzlich automatisch bei jedem Commit aus (ohne BrowserGate) und heften die
Schrittzusammenfassung als Git-Note refs/notes/quality-gate an den Commit.
Die konkrete Nachweismatrix je Änderungsart (Contract/Permission, Domainregel, geschützter Read/Write, Concurrency, EF-Migration, Dokument-/Portalpfad, Blazor-Komponente, Skript, Dokumentation) steht im Entwicklungsleitfaden §15.
Weiterführend
- Bausteinsicht — Modulgrenzen und Abhängigkeitsrichtung
- Laufzeitsicht — Requestablauf, Dokumentzugriffswege, Hearing-Lebenszyklus
- Architekturentscheidungen — begründete Grundsatzentscheidungen
- ../DEVELOPMENT-GUIDE.md — normative Regeln je Änderungsart
- ../GETTING-STARTED.md — lokaler Stack und Betriebseinstieg
- ../../conventions/ — themenspezifische Coding-Konventionen
09. Architekturentscheidungen
Dieses Kapitel hält die wesentlichen, dauerhaft wirksamen Architekturentscheidungen in
ADR-Kurzform fest: Titel, Kontext, Entscheidung, Konsequenz. Details zu Bausteinen stehen in der
Bausteinsicht, zu Qualitätsanforderungen in der
Qualitätsanforderungen bzw. dem
Qualitätsbaum. Die vollständige, laufend gepflegte
Konventions-Bewertung inkl. Vor-/Nachteilsabwägung steht in
docs/conventions-decisions.md.
ADR-01 · No-Layer-ABP statt klassischer 8-Projekt-Schichtung
- Kontext: ABP-Projekte werden üblicherweise in Domain, Application, EntityFrameworkCore, HttpApi, Blazor usw. je Modul zerlegt. Für einen modularen Monolithen mit überschaubarer Modulzahl erzeugt das viel Projekt-/Verkabelungs-Overhead ohne echten Nutzen.
- Entscheidung: Jedes Fachmodul besteht im Normalfall aus höchstens drei Assemblies:
*.Contracts(DTOs, Interfaces, Permissions, Localization, öffentliche IDs), einer Runtime (Domain, Application, EF-Mapping in einer Assembly) und optional*.Blazor(Feature-UI). Domain, Application und Persistence bleiben logische, aber nicht separat materialisierte Schichten. Eine weitere Assembly entsteht erst, wenn sie unabhängig konsumiert, paketiert, ersetzt oder betrieben werden muss. - Konsequenz: Weniger Projekte/Referenzen, schnellere Builds, geringere Einstiegshürde. Die
Abhängigkeitsrichtung (Contracts ↔ Runtime ↔ Blazor, keine Rückkanten) bleibt trotzdem eine
Compile-Time-Eigenschaft, durchgesetzt durch Architekturtests statt durch Projektgrenzen
allein (siehe ADR-05). Konvention ABP-11 in
conventions-decisions.mdmarkiert die klassische 8-Schichtung entsprechend als N/A für dieses Repository.
ADR-02 · Monorepo mit Host + wiederverwendbaren CAF-Modulen + einer RAL-Anwendung
- Kontext:
CAF.Modules.*(Authentication, Documents[.Contracts], UI.Blazor) sind anwendungsneutrale Bausteine;HearingPortalist die einzige heute existierende Anwendung, die sie konsumiert. Getrennte Repositories hätten Versions-Ping-Pong zwischen Framework und Anwendung erzwungen, obwohl beide im selben Entwicklungstakt reifen. - Entscheidung: Ein Repository (
CAF.slnx) bündeltsrc/Modules/(CAF-Bausteine) undsrc/Applications/RAL/QualityMarks/HearingPortal/(Host, Shell, Contracts, Features). Die Solution gliedert dies in der IDE als vier Gruppen (Anwendung, CAF-Bausteine, Fachmodule, Tests). - Konsequenz: Ein Commit kann Framework und Anwendung atomar ändern; Refactorings über die
Grenze sind ohne Cross-Repo-Release möglich. Im Gegenzug muss die Modulgrenze
CAF.Modules.* → Applications.*verboten bleiben, sonst verwischt das Monorepo den eigentlichen Zweck der Module (wiederverwendbar, anwendungsneutral) — durchgesetzt in ADR-05. Die vier CAF-Bausteine bleiben dabei bewusst Quellcode-Bausteine und werden nicht paketiert (ADR-03); der Host konsumiert sie ausschließlich perProjectReference.
ADR-03 · CAF-Bausteine als Quellcode-Bausteine mit Vertragsdatei (VERTRAG.md)
- Kontext:
CAF.Modules.Authentication,CAF.Modules.UI.Blazor,CAF.Modules.DocumentsundCAF.Modules.Documents.Contractssollen wiederverwendbar bleiben, ohne dass Konsumenten sich auf undokumentierte interne Details verlassen. Eine frühere Fassung dieser Entscheidung sah echte NuGet-Pakete vor (IsPackable=true, eigenePackageId/Version, Pack-Skript). Das wurde zurückgenommen: es gab keinen Konsumenten außerhalb dieses Repositorys, also war es Paketinfrastruktur ohne Abnehmer — mit Versionspflege, Feed-Frage undNU5104-Rauschen als laufenden Kosten. - Entscheidung: Die Bausteine sind Quellcode-Bausteine, keine NuGet-Pakete. Jedes der vier
csproj setzt
IsPackable=falseund trägt keinePackageId, keineVersionund keinPackageReadmeFile; der Host konsumiert sie ausschließlich perProjectReference. Die verbindliche Vertragsgrenze bleibt die deutschsprachigeVERTRAG.mdim Wurzelverzeichnis des jeweiligen Projekts — als Quelltext-Vertrag: zugesicherte öffentliche Fläche, ausdrückliche Nicht-Zusicherungen, Versionierungsregel für den Fall einer späteren Paketierung. Ein Pack-Skript existiert nicht. - Konsequenz: Kein Versions-Ping-Pong und kein Feed-Zwang für die tägliche Entwicklung; die
Wiederverwendbarkeit ist eine Eigenschaft des Abhängigkeitsgraphen
(ADR-06), nicht
einer Paketgrenze. Der Vertrag ist trotzdem geschrieben und maschinell verankert:
FrameworkPackagingTestspinnt genau vier CAF.Modules-Projekte, deren Nicht-Packbarkeit samt fehlender Paketidentität, und dass jedeVERTRAG.mdexistiert, nicht leer ist und beide Pflichtabschnitte trägt. Ein versehentlichesIsPackable=trueoder eine gelöschte Vertragsdatei bricht den Build. Preis: Ein Konsument außerhalb dieses Repositorys ist heute nicht bedienbar — er bräuchte zuerst eine erneute Paket- und Feed-Entscheidung. Ein zweites Produkt im Repository funktioniert ohne Änderung.
ADR-04 · RALQM als data-preserving Umbenennung persistierter Kennungen
- Kontext: Das Produkt hieß im Code zunächst
Guetezeichen, dannQualityMarks; die verbindliche Kurzform für persistierte, anwendungsspezifische Kennungen ist inzwischenRALQM(TabellenRALQMHearings/RALQMInstitutions, Fehlercode-PräfixRALQM.<Feature>:, Permission-GruppeRALQM, RollenRALQMAdministrator/RALQMCaseOfficer/RALQMManagement). Technische Slugs (z. B. Docker-/Keycloak-Client-IDs wieral-qualitymarks) bleiben bewusst lowercase und wurden nicht mitgezogen. - Entscheidung: Jede Umbenennung persistierter Bezeichner erfolgt ausschließlich über eine
data-preserving EF-Migration (
RenamePersistedIdentifiersToRALQM, siehesrc/Applications/RAL/QualityMarks/HearingPortal/Host/Migrations/) mit korrekt gespiegeltemDown(); historische Migrationen behalten ihre alten, zum jeweiligen Zeitpunkt gültigen Bezeichner (z. B.AddCafDocumentShareGrants) und werden nicht nachträglich editiert (Konvention EF-03). Bestehende Dev-Datenbanken werden über ein eigenständiges, nicht-destruktives Skript migriert (tools/scripts/Migrate-DevDatabaseToRalqm.ps1: klont das Docker-Volume, benennt DB/Login auf der Kopie um, Quelle bleibt Rollback-fähig). - Konsequenz: Rename-Operationen sind reversibel und laufen ohne Datenverlust, auch auf Anwendungsdatenbanken mit Bestand (verifiziert per Round-Trip apply→revert→re-apply sowie gegen echte Dev-Daten: Tabellen-/User-/Grant-Zahlen vor/nach Migration identisch). Der Preis ist zusätzliche Migrations- und Skript-Pflege statt einer einfachen Such-Ersetzen-Operation im Quellcode.
ADR-05 · Cohesion als verbindliche UI-Bibliothek für Fachcode
- Kontext: ABP liefert LeptonX/MudBlazor für Account-, Administrations- und Themenseiten mit. Für die eigene Gütezeichen-Fachoberfläche soll es eine einheitliche, kapselnde Komponentenbasis geben statt direkter Vendor-Kopplung in jeder Feature-Komponente.
- Entscheidung: Eigene CAF-/RALQM-Seiten verwenden ausschließlich Cohesion, zentral über
CAF.Modules.UI.Blazorregistriert (Lizenzprüfung, Assets/Bundles; Paket aus dem GitLab-Gruppen-Feed, zentrale Floating-Version3.*-*). MudBlazor, Syncfusion oder Blazorise dürfen von Fachcode nicht direkt gerendert werden; ABP-gelieferte Account-/Administrations-/LeptonX-Seiten sind davon ausgenommen und behalten ihren eigenen UI-Stack. Nur Interactive Server wird unterstützt, kein WebAssembly; Feature-Komponenten bleiben render-mode-neutral (kein@rendermodeim Feature-UI). - Konsequenz: Vendor-Details sind in Wrappern gekapselt, ein späterer UI-Bibliothekswechsel
bliebe lokal begrenzt. Fehlende generische UI-Primitives gehören in die separat versionierte
Cohesion-Bibliothek, nicht in Fachcode-Workarounds. Die Regel ist kein Stilhinweis, sondern
arch-test-erzwungen (BL-01/BL-02 in
conventions-decisions.md).
ADR-06 · Strikte Modulgrenzen durch Architekturtests statt Konvention allein
- Kontext: Ordnerstruktur und Namensschema allein verhindern in der Praxis keine Querabhängigkeiten — insbesondere unter Zeitdruck oder bei automatisiertem/agentischem Edit. Der No-Layer-Ansatz (ADR-01) und das Monorepo (ADR-02) erhöhen dieses Risiko zusätzlich, weil alle Module im selben Prozess und Repository liegen.
- Entscheidung: Die Abhängigkeitsrichtung ist explizit tabelliert (siehe
Bausteinsicht) und über
Architekturtests erzwungen, u. a.:
CAF.Modules.* → Applications.*verboten, Contracts referenzieren nie Runtime/EF/Host, Feature-Blazor referenziert nie die eigene oder eine fremde Runtime, GZ-Runtime referenziert nie fremde Runtime/Entities/Repositories/DbSets/Tabellen, kein Modul referenziert den ausführbaren Host, keine neue Fachlogik/Fachentity im Host. Über Modulgrenzen werden nur IDs gespeichert, nie Cross-Module-Foreign-Keys oder -Navigation (z. B.InstitutionId/DocumentIdstatt Entity-Referenz). - Konsequenz: Grenzverletzungen sind Build-Fehler, nicht Review-Findings. Renames und
Restrukturierungen (siehe AGENT_CHANGELOG, z. B. die Gruppierung der Feature-Slices unter
src/Applications/RAL/QualityMarks/HearingPortal/Features/<Feature>/oder die RALQM-Umbenennung) dürfen laut Projektregel die Grenzregeln selbst nie lockern, sondern ausschließlich Pfad-Literale in den Tests nachziehen. Das hält die Compile-Time-Eigenschaft auch über viele aufeinanderfolgende Strukturänderungen hinweg stabil.
Referenzen
- Laufendes ADR-Register mit Vor-/Nachteil je einzelner Konvention:
docs/conventions-decisions.md - Gesamtarchitektur inkl. Modulverzeichnis und Abhängigkeitstabelle: Bausteinsicht; Qualitätsnetz: Querschnittliche Konzepte §7
- Nachvollziehbarkeit der Entscheidungsumsetzung je Arbeitsschritt:
AGENT_CHANGELOG.md - Vertiefung Bausteine/Schnitt: Bausteinsicht; Vorgaben für neue Features:
Entwicklungsleitfaden,
conventions/
10. Qualitätsanforderungen
Dieses Kapitel benennt, welche Qualitätsmerkmale für das RAL Gütezeichen Anhörungsportal verbindlich sind, und macht sie an konkreten, prüfbaren Szenarien fest. Architekturentscheidungen in Bausteinsicht und Querschnittliche Konzepte werden gegen genau diese Anforderungen getroffen; Risiken und technische Schulden benennt, wo sie aktuell nur teilweise erfüllt sind.
10.1 Qualitätsbaum
Der ausschlaggebende Werttreiber ist ein rechtssicherer, nachvollziehbarer Anhörungsprozess auf einer Plattform, die als modularer Monolith langfristig ohne Umbau der Kernarchitektur weiterentwickelbar bleibt (vgl. Einführung und Ziele). Daraus leiten sich fünf gleichrangige Qualitätsäste ab, die im Projekt jeweils durch Architektur und durchsetzende Tests abgesichert sind statt nur dokumentiert zu werden (CLAUDE.md):
Qualität RAL-Gütezeichen-Anhörungsportal
├─ Wartbarkeit
│ ├─ No-Layer-Modulgrenzen (Contracts vs. Runtime vs. Blazor)
│ ├─ Compile-Time durchgesetzte Abhängigkeitsrichtung
│ └─ Proportionalität (keine vorsorglichen Abstraktionen ohne Consumer)
├─ Sicherheit
│ ├─ OIDC-Authentifizierung über Keycloak, lokale ABP-Autorisierung
│ ├─ Objektberechtigung je Zugriffsweg (intern, Gast, Konto, Direktfreigabe)
│ └─ Least-Privilege-Infrastruktur (DB-Principal, Keycloak-Provisioning-Client)
├─ Barrierefreiheit
│ ├─ WCAG 2.1 AA als Mindestziel
│ ├─ Cohesion als einzige Fach-UI-Bibliothek
│ └─ automatisierte axe-Prüfung im BrowserGate
├─ Testbarkeit und Verlässlichkeit
│ ├─ Nachweismatrix je Änderungsart (Unit/Integration/SQL/Browser)
│ ├─ Quality Gate je Commit (Git-Hooks)
│ └─ zuverlässige Nebenwirkungen (Outbox, Idempotenz, Retry)
└─ Datenerhalt
├─ Data-preserving Migrationen bei Renames
├─ optimistische Concurrency auf mutablen Aggregaten
└─ kein Hard Delete fachlicher Prozessdaten
10.2 Qualitätsszenarien
Jedes Szenario folgt dem Muster Stimulus → Reaktion → Nachweis. Die ID-Präfixe entsprechen den Ästen aus 10.1.
Wartbarkeit (WB)
| ID | Szenario | Erwartete Reaktion | Nachweis |
|---|---|---|---|
| WB-01 | Ein Entwickler ergänzt in CAF.Applications.RAL.QualityMarks.HearingPortal.Hearings einen ProjectReference auf die Institutions-Runtime statt auf Institutions.Contracts. |
Der Build schlägt architektonisch fehl; die verbotene Kante GZ-Runtime → fremde GZ-Runtime ist untersagt (siehe Bausteinsicht §5.2.4). |
Architekturtests auf Projekt-/Paketkanten, Teil des Quality Gate |
| WB-02 | Ein Framework-Baustein (CAF.Modules.*) referenziert versehentlich einen CAF.Applications.RAL.QualityMarks.HearingPortal.*-Namespace oder ein GZ-Fachwort. |
Referenz wird als Verstoß gegen die Framework-Neutralität erkannt und blockiert. | Architekturtests (Namespace-/Kantenscan), Vertragsdatei VERTRAG.md je Baustein |
| WB-03 | Ein neues Feld wird an Hearing ergänzt, obwohl UI/Consumer es nicht benötigen. |
Contract-DTO bleibt unverändert; Änderung erfolgt zuerst am Aggregate mit benannter Operation (DEVELOPMENT-GUIDE.md §14). | Code-Review anhand der Nachweismatrix, EF-Mapping- und Migrationstest |
| WB-04 | Ein neues Modul wird mit dem Namen Common oder Utils angelegt. |
Ablehnung im Review; Ownership-Frage aus dem Entscheidungsbaum ist nicht beantwortet. | Konvention in DEVELOPMENT-GUIDE.md §2 |
| WB-05 | Ein Paket-/API-Element wird als „nicht mehr benötigt“ entfernt. | Entfernung gilt erst nach Restore-/Publish-/Endpoint-Scan als vollständig; reine Quelltextlöschung genügt nicht. | Nachweismatrix „Paket-/API-Entfernung“ (DEVELOPMENT-GUIDE.md §15.1) |
Sicherheit (SI)
| ID | Szenario | Erwartete Reaktion | Nachweis |
|---|---|---|---|
| SI-01 | Ein Benutzer ruft eine geschützte Anhörungsseite ohne gültige Keycloak-Session auf. | OIDC Authorization Code mit PKCE erzwingt Anmeldung; ohne gültige Session kein Zugriff auf die Server-UI. | Authentifizierungstests in CAF.Modules.Authentication, BrowserGate-Login/Abmeldung |
| SI-02 | Ein angemeldeter Referent versucht eine Aktion, für die seine Rolle keine Permission besitzt (z. B. Verfahrensabbruch, den nur Administrator/Geschäftsführung dürfen). | Application Service lehnt serverseitig ab; ein ausgeblendeter Button gilt explizit nicht als Autorisierung. | Permission-Definition-/-Vergabe-/-Ablehnungstests, Architekturtest „jeder Endpunkt prüft Permission“ |
| SI-03 | Ein Gastlink-Inhaber (Übergangspfad, wird durch Kontozugang abgelöst) versucht, ein Dokument eines anderen Teilnehmers über eine erratene DocumentId abzurufen. |
Zugriff wird verweigert; jeder Aufruf revalidiert Einladung, Hearing-Status, aktiven Teilnehmer und konkreten Dokumentgrant neu – eine ID/URL allein verleiht nie Zugriff (siehe Laufzeitsicht §6.4). | Autorisierungsmatrix-Tests (positiv/negativ), Leakage-Scan, HTTP-Tests gegen den realen Hostpfad |
| SI-04 | Zwei gleichzeitige Einlösungen derselben Gasteinladung treffen in getrennten Transaktionen ein. | Jede Transaktion prüft Einladung und Objektzustand erneut; nur optimistische Schreibkonflikte werden begrenzt wiederholt, ein zwischenzeitlicher Widerruf bleibt wirksam. | Concurrency-/Parallelsitzungstests, BrowserGate-Parallelfall |
| SI-05 | Die Anwendungsdatenbank wird kompromittiert oder ein Entwicklungsrechner verbindet sich versehentlich mit dem SA-Zugang. | Der Host verwendet im Regelbetrieb ausschließlich den dedizierten, nicht privilegierten qualitymarks-Principal statt eines SA-/Admin-Logins; SA- und Keycloak-Admin-Zugang bleiben beim Setup/Reconciler (Verteilungssicht §3, DEVELOPMENT-GUIDE.md §13). |
doctor.cmd-Kategorienprüfung, Migrations-Fast-Path-Principal-Check |
| SI-06 | Die kontrollierte Kontoprovisionierung ruft Keycloak für eine mehrdeutige Identität auf. | Der Admin-Port scheitert geschlossen (fail-closed) und vergibt keine Fachrolle automatisch; der Keycloak-Provisioning-Client besitzt ausschließlich die Rolle manage-users, keine weitere Realm-Administration. |
Adapter-, Mehrdeutigkeits- und Berechtigungstests, Test-KeycloakProvisioningClient.ps1 |
| SI-07 | Ein Client verbindet sich außerhalb der Entwicklungsumgebung unverschlüsselt. | HSTS und HTTPS-Redirect laufen vor Routing/Authentifizierung; Health-Endpunkte sind bewusst nicht ausgenommen. | Quelltext-Sentinel TransportSecurityPipelineTests |
| SI-08 | Ein direkter CAF-Dokument-Share-Link wird mehrfach erraten oder nach Ablauf verwendet. | Secret ist nur gehasht gespeichert, rate-limitiert, läuft ab und ist einzeln widerrufbar. | Sicherheits-/Delegations-/HTTP-Tests von CAF Documents |
Barrierefreiheit (BF)
| ID | Szenario | Erwartete Reaktion | Nachweis |
|---|---|---|---|
| BF-01 | Eine Fachseite (z. B. Verfahrensliste, Freigabe, Postausgang) wird gegen WCAG-2.1-AA-Kriterien geprüft. | Keine serious/critical axe-Verstöße (Kontrast, Alternativtexte, Rollen) außerhalb einer versionierten, begründeten Ausnahmeliste. |
axe-Läufe im BrowserGate über alle Fach- und Portalseiten, tests/.../BrowserGate/a11y-ausnahmen.json |
| BF-02 | Eine Tabelle mit sortierbaren Spalten (Institutionenliste, Anhörungsliste) wird ausschließlich per Tastatur bedient. | Spaltenköpfe sind fokussierbar, aria-sort wird gesetzt und per Screenreader angesagt. |
Tastatur-/Fokus-Komponententests (bUnit), BrowserGate-Tastaturpfad |
| BF-03 | Ein Statuswert (z. B. Freigabe abgelehnt, Frist überschritten) wird dargestellt. | Status ist nicht ausschließlich farblich codiert, sondern zusätzlich textuell/symbolisch erkennbar. | UI-Review anhand Querschnittliche Konzepte §5, bUnit-Rendertests |
| BF-04 | Eine datengebundene Seite befindet sich im Zustand „Laden“, „Leer“, „Fehler“, „Verboten“, „Validierung“, „Speichern“ oder „veralteter Concurrency-Stand“. | Jeder dieser Zustände ist sichtbar und bedienbar abgebildet, kein stiller Fehlzustand. | bUnit-Zustandstests je Seite, BrowserGate-Fehler-/Retry-Pfade |
| BF-05 | Fachcode versucht, eine MudBlazor-, Syncfusion- oder Blazorise-Komponente direkt zu rendern statt Cohesion zu verwenden. | Verstoß wird erkannt; nur ABP-Administrations-/LeptonX-Seiten sind ausgenommen. | Architektur-/Quelltextprüfung auf UI-Bibliotheksnutzung |
Testbarkeit und Verlässlichkeit (TV)
| ID | Szenario | Erwartete Reaktion | Nachweis |
|---|---|---|---|
| TV-01 | Ein Entwickler committet eine Änderung lokal. | Der pre-commit-Hook führt Preflight, Restore, Build (0 Warnings/Errors), alle Solutiontests und den Pending-Model-Check automatisch aus; bei Rot wird der Commit blockiert. | tools/git-hooks, Invoke-QualityGate.ps1, Git-Note refs/notes/quality-gate |
| TV-02 | Ein Pflichttest wird versehentlich übersprungen (Skip-Attribut) statt zu bestehen. |
Das gilt als Fehlschlag des Gates; es gibt keine Ausnahmeliste. | QualityGateAutomationTests (Skip-Attribut-Verbot in Testquellen) |
| TV-03 | Ein sichtbarer End-to-End-Prozess (Login, Freigabe, Portalzugang) wird geändert. | Vor Übergabe/Merge ist der vollständige lokale Gate-Lauf inklusive exklusivem BrowserGate verbindlich; der Clean Room baut ihn nur, startet ihn aber nicht. | Invoke-QualityGate.ps1 -IncludeBrowserGate, Qualitätsbaum |
| TV-04 | Der Postausgang versucht, eine E-Mail zuzustellen, und der SMTP-Provider ist vorübergehend nicht erreichbar. | Der Sendeversuch wird über die persistente Hearing-Outbox mit Idempotency-Key erneut versucht, ohne Duplikate zu erzeugen. | Retry-/Idempotenz-/Restart-Tests, Kennzahlen „Sendeversuche nach Fehlercode“ |
| TV-05 | Eine EF-Migration wird gegen eine leere Datenbank ausgeführt und anschließend dotnet ef migrations has-pending-model-changes geprüft. |
Migration läuft vollständig „from-empty“ durch; das Modell hat keine ausstehenden Änderungen. | SQL-Server-Integrationstests, Pending-Model-Check im Gate |
| TV-06 | Eine neue Fachregel/Statusübergang wird implementiert. | Positive und negative Tests decken alle relevanten Statuszellen ab, nicht nur den Erfolgspfad. | Nachweismatrix „Domainregel/Transition“ (DEVELOPMENT-GUIDE.md §15.1) |
Datenerhalt (DE)
| ID | Szenario | Erwartete Reaktion | Nachweis |
|---|---|---|---|
| DE-01 | Ein Rename benannter Identifikatoren (z. B. das RALQM-Präfix-Rework) wird auf eine Datenbank mit vorhandenen Anhörungs- und Institutionsdaten angewendet. | Die Migration ist data-preserving: bestehende Zeilen, Foreign Keys und Werte bleiben erhalten, nur Bezeichner ändern sich; historische EF-Migrationen behalten ihre alten Identifiers. | HearingPortalDbMigrationServiceTests, SQL-Server-Round-Trip-Tests, DocumentsMigrationBaselineSqlServerTests |
| DE-02 | Zwei Benutzer bearbeiten dieselbe Anhörung gleichzeitig und speichern nacheinander. | Optimistische Concurrency verhindert einen stillen Teil-Overwrite; der zweite Schreibversuch scheitert nachvollziehbar am veralteten Stand. | Concurrency-Tests gegen den realen Persistenzpfad (DEVELOPMENT-GUIDE.md §15.1) |
| DE-03 | Eine Institution soll gelöscht werden, obwohl sie noch in Teilnehmer-Snapshots oder Portal-Kontobindungen referenziert wird. | Löschung wird über den Usage-Provider verweigert; die UI erklärt den Deaktivieren-Hinweis statt eines harten Fehlers. | Kontakt-/Institutions-Löschguard-Tests |
| DE-04 | Ein laufendes Verfahren wird abgebrochen statt hart gelöscht. | Der Zustand wechselt in einen benannten Endzustand (Aborted), Einladungen/Grants/Due Actions werden widerrufen, die fachliche Historie bleibt lesbar. |
Fach-, Berechtigungs- und Historientests „Verfahrensabbruch“ |
| DE-05 | Zeitstempel werden über verschiedene Server-Instanzen hinweg verglichen (z. B. Fristberechnung). | Alle Zeitpunkte werden konsistent in UTC über ABP-Clock-Abstraktionen verarbeitet, keine lokale Serverzeit. | Zeitgrenzen-/Fristvorschlagstests |
10.3 Nicht angestrebte Qualitätsmerkmale
Entsprechend den bewusst nicht vorhandenen Nachbarn in Kontextabgrenzung §3.4 werden horizontale Skalierung über Microservices, eine zweite Datenbank, generische Workflow-/DMS-Fähigkeiten und Multi-Tenancy bewusst nicht als Qualitätsziele verfolgt. Das schont Komplexitätsbudget zugunsten der oben genannten fünf Äste; ein neuer Bedarf verlangt zuerst einen konkreten, getesteten Anwendungsfall, keine vorsorgliche Infrastruktur.
Siehe auch
- Bausteinsicht – wo die Modulgrenzen aus 10.1 technisch verankert sind
- Querschnittliche Konzepte – Sicherheits- und UI-Mechanismen im Detail
- Risiken und technische Schulden – aktuell noch offene Nachweise (z. B. integrierter BrowserGate-Lauf)
- DEVELOPMENT-GUIDE.md §15 – vollständige Nachweismatrix und Gate-Mechanik
- GETTING-STARTED.md – lokale Ausführung von Gate und BrowserGate
- conventions/security.md, conventions/testing.md – repo-weite Konventionsbaseline
- Risiken und technische Schulden – Status und Nachweistiefe je offener Fachfunktion
11. Risiken und technische Schulden
Dieses Kapitel listet bekannte Risiken und technische Schulden des aktuellen Standes. Es ersetzt
keine laufende Nachverfolgung; verbindliche Quelle für den jeweils aktuellen Stand bleibt
AGENT_CHANGELOG.md. Die noch offenen Restschritte des Monorepo-Umbaus — insbesondere alles, was
vor dem ersten Push erledigt sein muss — führt
docs/VOR-ERSTEM-PUSH.md.
Übergreifender Kontext: Monorepo-Umbau lokal vollzogen, Remote-Anbindung offen
Der Umbau zum eigenständigen Repository caf ist lokal durchgeführt: Dieses Repository liegt
unter D:\source\repos\caf, trägt eine neue Historie ohne den alten Verlauf (ein Initialcommit
chore: initialize CAF monorepo) und besitzt die Branches main und development.
Noch offen ist die Remote-Anbindung: Es ist kein Git-Remote konfiguriert, das GitLab-Projekt
ist damit nicht angelegt und der Schutz von main (kein Direct-/Force-Push, Änderungen nur per
Merge Request) ist organisatorisch noch nicht wirksam — obwohl die
Randbedingungen ihn als gesetzt beschreiben. Bis dahin ist der lokale
Arbeitsbaum die einzige Kopie des Stands; ein Verlust ist nicht durch ein Remote abgedeckt.
Mehrere der unten aufgeführten Risiken sind Folgen dieses Umbaus (RALQM-Umbenennung, offene
Nachweise). Den verifizierten Erledigt-/Offen-Stand führt
docs/VOR-ERSTEM-PUSH.md; das ursprüngliche
Übergabeprotokoll des Quellrepositorys liegt
deprecated unter docs/history/.
Risiko- und Schuldenübersicht
| Thema | Beschreibung | Auswirkung | Gegenmaßnahme |
|---|---|---|---|
BrowserGate: hearing-workflow-Schritt create-form |
Der End-to-End-BrowserGate läuft bis tief in die Fachworkflows grün (Keycloak-Login, RALQM-Schema/-Seed, Shell/Menü/CSS, a11y-Scans, Institutions-Workflows), bricht aber im Schritt create-form des Hearing-Workflows (UI-Interaktion „Anhörung anlegen") mit einem Timeout ab. Kein Rename-Rest, sondern ein eigenständiges UI-Thema. |
Der volle Quality-Gate-Lauf ist für diesen einen Schritt nicht grün; die Anhörungs-Erstellung ist über den BrowserGate aktuell nicht automatisiert nachgewiesen. | Als eigenständiges Ticket isoliert reproduzieren (Timeout-Ursache: Ladezeit, Selektor oder Formularverhalten) und beheben, bevor der BrowserGate für die Übergabe als vollständig grün gilt. |
| Lokaler Keycloak-Port 8080 kollidiert | Der lokale Entwicklungsstack bindet Keycloak per Default auf Port 8080. Auf mindestens einer Entwicklungsumgebung belegt ein unabhängiger guacamole-Container denselben Port, wodurch Keycloak nicht binden kann und der BrowserGate-Lauf umgebungsblockiert abbricht. |
Kein Projektfehler, aber ein reproduzierbarer lokaler Blocker für Setup/BrowserGate auf betroffenen Workstations. | Vor dem Start Portbelegung prüfen; bei Kollision temporär auf einen freien Port ausweichen (z. B. testweise 18080) und danach auf den Standardport zurücksetzen, oder den kollidierenden Fremd-Container stoppen. Keine dauerhafte Portänderung im Repository ohne Abstimmung. |
| Secret-Rotation der alten Remote-URL | Das frühere origin-Remote enthielt einen Zugriffstoken in der URL und wurde entfernt. Falls dieser Token noch nicht widerrufen/rotiert wurde, ist das eine offene externe Sicherheitslücke außerhalb des Repository-Inhalts. |
Ein potenziell noch gültiges Secret außerhalb der Kontrolle des Repositorys. | Rotation/Widerruf des Tokens außerhalb des Repositorys durchführen (GitLab-Zugriffstoken-Verwaltung); im Repository selbst ist kein Secret mehr enthalten und nichts weiter zu tun. |
| Immutabilität historischer Migrationen | Historische EF-Migrationen behalten bewusst ihre alten Bezeichner (Caf*, QualityMarks*, Gz*), auch nachdem Live-Code und -Typen auf CAF*/RALQM umbenannt wurden. Tests wie DocumentsMigrationBaselineSqlServerTests erwarten explizit die alte Migrations-Id/-Quelle (AddCafDocumentShareGrants/CafDocumentShareGrants). |
Bewusster, dauerhafter Bruch zwischen aktueller Namenskonvention und historischem Migrationsverlauf; erschwert oberflächliches „alles muss RALQM/CAF heißen"-Refactoring und verlangt Disziplin bei neuen Migrationen. | Kein Nacheditieren bestehender Migrationen. Neue Umbenennungen erfolgen ausschließlich über zusätzliche, datenbewahrende Rename-Migrationen (siehe 20260825174437_RenamePersistedIdentifiersToRALQM); Tests, die historische Bezeichner erwarten, bleiben bestehen. |
| RALQM-Datenmigration gegen reale Upgrade-Pfade unvalidiert | Die manuelle SQL-Migration 20260825174604_MigrateApplicationIdentifiersToRALQM (Permissions, Grants, Settings, Text Templates, Lokalisierung, Rollen) war laut Übergabeprotokoll noch nicht vollständig gegen einen echten SQL-Server-Upgradepfad geprüft; zwischenzeitlich wurde sie gegen eine migrierte Dev-Datenbank verifiziert (AGENT_CHANGELOG.md, R8/R5-Einträge), ein vollständiger Produktions-Upgradepfad-Nachweis steht aber weiterhin aus. |
Restrisiko bei einem realen Upgrade eines bestehenden Produktivbestands auf das RALQM-Schema. | Vor jedem produktionsnahen Einsatz erneut gegen eine repräsentative Kopie eines bestehenden Bestands migrieren und die in Qualitätsanforderungen beschriebenen SQL-Integrationstests plus Pending-Model-Check ausführen. |
| Kontozugang löst Gastzugang noch nicht vollständig ab | Der Bereich „Institutionszugang neu" (US-22, US-22a, US-22b, US-22c) ist laut Backlog durchgehend nur teilweise umgesetzt; der ablaufende Gastlink existiert weiterhin parallel zum Zielzustand kontogebundene Teilnahme (siehe Einführung und Ziele, Laufzeitsicht §6.4). | Bis zur vollständigen Ablösung bleibt ein anonym erreichbarer Zugangsweg bestehen; das Sicherheitsziel „kein Rückfallweg, sofortiger Entzug" ist noch nicht erreicht. | Fortschritt an US-22 ff. verfolgen; arc42 (dieses Kapitel sowie Kapitel 1/3/6/8/10/12) bei vollständiger Ablösung auf reinen Kontozugang nachziehen. |
| Vollständige Verifikationskette nach Umbau offen | Root- und App-Build, vollständige Architekturtests, dotnet test CAF.slnx, Pending-Model-Check, SQL-Integrationstests und das komplette Quality Gate inklusive BrowserGate/Clean Room müssen nach den letzten Änderungen noch einmal vollständig und am Stück laufen, bevor der Umbau als abgeschlossen gilt (Punkt 1 in docs/VOR-ERSTEM-PUSH.md). |
Ohne diesen Gesamtlauf ist „grün" nur stückweise für Teilmengen nachgewiesen, nicht als Gesamtsystemzustand. | Vor jedem Merge/Release den vollständigen Quality Gate gemäß Entwicklungsleitfaden laufen lassen; Ergebnis nicht aus Einzelläufen extrapolieren. |
GitLab-Remote und main-Schutz fehlen |
Das lokale Repository caf existiert mit neuer Historie und den Branches main/development, aber es ist kein Git-Remote konfiguriert. Das GitLab-Projekt ist folglich nicht angelegt und der in den Randbedingungen als gesetzt beschriebene main-Schutz (kein Direct-/Force-Push, nur Merge Requests) ist nicht wirksam. |
Der lokale Arbeitsbaum ist die einzige Kopie — kein Backup, keine Review-Pflicht, keine serverseitige Durchsetzung des Branch-Schutzes. Die dokumentierte Git-Randbedingung ist derzeit eine Absicht, keine Kontrolle. | GitLab-Projekt anlegen, als origin verbinden und main sowie development pushen; danach main serverseitig schützen und die Merge-Request-Pflicht aktivieren. Beim Übertrag Host/Logs/ und lokale Artefakte ausschliessen. |
| Ein Cohesion-Publish kann den Build röten, ohne Änderung hier | Cohesion wird mitentwickelt und deshalb bewusst nicht gepinnt, sondern über die Floating-Version 3.*-* aus dem GitLab-Gruppen-Feed bezogen (Directory.Packages.props) — höchste verfügbare 3.x inklusive Prereleases. Zwei Restores desselben Commits können unterschiedliche Versionen auflösen. |
Angenommener Trade, keine Schuld: dieser Build ist damit ein fortlaufender Integrationstest gegen Cohesion — Regressionen fallen früh auf statt beim nächsten bewussten Upgrade. Preis: ein CI-Lauf ist nicht allein durch den Commit bestimmt, und ein roter Build kann eine fremde Ursache haben. | Nicht sperren (ein Lockfile verschöbe die Entdeckung), sondern sichtbar machen: aufgelöste Cohesion-Version in start.cmd/doctor.cmd und im CI-Log ausgeben. Bei unerwartet rotem Build zuerst die Version prüfen, dann eigenen Code. Siehe UI-Rework-Plan, Stage 0. |
| Feed-Zugang ist neue Voraussetzung für jeden Checkout | Cohesion liegt nicht mehr als .nupkg im Repository. Ohne Zugangsdaten für die Quelle cohesion schlägt bereits dotnet restore fehl. |
Ein frischer Checkout ist ohne Gruppen-Deploy-Token (lokal) bzw. Job-Token-Allowlist (CI) nicht baubar; Offline-Builds sind nicht mehr möglich. | Onboarding-Schritt in Getting Started und doctor.cmd-Prüfung (SET/MISSING); start.cmd bricht mit dem exakten Konfigurationsbefehl ab statt mit einem NuGet-401. |
Bewusst nicht als Schulden geführt
Die folgenden Punkte sind laut Qualitätsanforderungen §10.3 und Kontextabgrenzung §3.4 bewusste Auslassungen, keine offenen Risiken: kein lokaler OpenIddict-Server, keine Microservices, keine zweite GZ-Datenbank, kein PostgreSQL/Redis/RabbitMQ/Kafka/Elsa/GraphQL/Kubernetes, kein externes DMS, keine Dokumentbearbeitung/OCR/Volltextsuche/Legal-Archiv/generische Dateibrowser-UI. Ein neuer Bedarf in diese Richtung erfordert zuerst einen konkreten, getesteten Anwendungsfall, keine vorsorgliche Abstraktion.
Verwandte Kapitel
- Qualitätsanforderungen — Testarten und Gate-Aufbau, gegen die die hier gelisteten Lücken laufen.
- Bausteinsicht — Modulgrenzen und Paketierung, insbesondere zum Cohesion-Pin.
- Entwurfsentscheidungen — Begründung der RALQM-/CAF-Präfixregel und der datenbewahrenden Migrationsstrategie.
- Entwicklungsleitfaden und Getting Started — operative Schritte für lokale Infrastruktur und Quality Gate.
12. Glossar
Dieses Kapitel bündelt die verbindlichen Begriffe der Lösung. Fachbegriffe stammen aus dem
RAL-Gütezeichen-Anhörungsprozess, technische Begriffe aus der No-Layer-/ABP-Architektur des
Monorepos. Details zu den referenzierten Bausteinen stehen in der Bausteinsicht,
Randbedingungen und Namenskonventionen in den Randbedingungen; das
normative Regelwerk bleibt DEVELOPMENT-GUIDE.md und
conventions/.
Fachliche Begriffe
| Begriff | Definition |
|---|---|
| Anhörung (Hearing) | Das zentrale Verfahren: eine Institution wird zu einer geplanten Gütezeichen-Entscheidung angehört. Durchläuft die benannte Statuskette Draft → PendingApproval → Approved → Dispatched → Adjudication → Completed mit definierten Rücksprüngen/Abbruch. Eigenes Aggregat im Modul Hearings, Tabellen RALQMHearings/RALQMHearing*. |
| Antragsteller | Die Institution oder Person, deren Gütezeichen-/Zulassungsantrag Gegenstand der Anhörung ist. Wird bei Anlage der Anhörung über eine InstitutionId referenziert, nicht dupliziert. |
| Frist | Zeitliche Grenze im Anhörungsprozess: erste und zweite Rückmeldefrist (mit konfigurierbaren Regelwerten, Auslieferung 56/28 Tage) sowie daraus abgeleitete fällige Aktionen (Due Actions). Fristüberwachung läuft über persistente Due Actions und ABP Background Worker, nicht über Timer im Request. |
| Gütezeichen (Quality Mark) | Das RAL-Gütezeichen, dessen Verleihung/Verlängerung/Entzug den fachlichen Anlass für eine Anhörung bildet. Gibt der Anwendung ihren Domänennamen (QualityMarks) und den Kürzel RALQM. |
| Institution | Der fachliche Träger von Kontakten, Anhörungen und Pflichtpartnerbeziehungen. Eigenes Aggregat im Modul Institutions mit Anschrift, Typ, Pflichtpartnerstatus und mehreren Kontakten. Tabellen RALQMInstitutions/RALQMInstitutionContacts. |
| Institutionsgruppe (geplant, US-07/US-13/US-41) | Ein verfahrensübergreifend wiederverwendbarer, benannter Beteiligtenkreis aus mehreren Institutionen; als Ganzes in ein Verfahren einbindbar und für Rundnachrichten adressierbar. Siehe Bausteinsicht §5.2.2. |
| Referent | Produktrolle RALQMCaseOfficer: bearbeitet Anhörungen operativ (anlegen, einreichen, Teilnehmer/Dokumente pflegen, Postausgang, Vorlagen), darf aber z. B. nicht die von ihm eingereichte Freigabe selbst erteilen (Vier-Augen-Prinzip) und besitzt kein Abbruchrecht für laufende Verfahren. |
| Rolle | Produktweite GZ-Rolle, die Grants aus den Permissions der Feature-Contracts komponiert. Drei Rollen: RALQMAdministrator, RALQMCaseOfficer (Referent), RALQMManagement (Geschäftsführung). Rollen- und Grant-Komposition liegt ausschließlich im Modul Authorization. |
| Geschäftsführung (Management) | Produktrolle RALQMManagement: erteilt u. a. die Freigabe (andere Person als die Einreichung) und darf laufende Verfahren abbrechen; besitzt kein Referentenrecht auf Abbruch. |
| Teilnehmer (Participant) | Ein zur Anhörung hinzugefügter Kontakt einer Institution samt Snapshot der Kontaktdaten zum Versandzeitpunkt. Kann zurückgezogen und wiederaufgenommen werden, ohne die Historie zu verlieren. |
| Vier-Augen-Freigabe | Fachregel: Einreichung und Freigabe einer Anhörung müssen von unterschiedlichen Personen (Akteurstrennung) erfolgen; die geprüfte Zusammensetzung wird über eine Kompositionsprüfsumme gesperrt. |
| Abschlussvariante (US-33) | Je Verfahren gewählt: zweistufig durchläuft Bescheidung als eigenen Status vor dem Abschluss; einstufig versendet Bescheidung und Endfassung in einem Vorgang und überspringt den Adjudication-Status. Siehe Einführung und Ziele. |
| Bescheidung (Adjudication) | Prozessphase nach den Rückmeldungen: Endfassungen werden in einem begrenzten, additiven Fenster zusammengestellt und individualisiert an aktive Teilnehmer versendet; das Fenster schließt mit dem ersten Bescheidungsversand. Entfällt als eigener Status bei einstufiger Abschlussvariante (siehe Abschlussvariante). |
| Stellungnahme (Response) | Die fachliche Rückmeldung eines Teilnehmers im Konto- oder (Übergangspfad) Gastportal, ggf. mit Anhängen; kann innerhalb der Frist mit dem zuletzt gelesenen Concurrency-Stand geändert werden. |
| Gastzugang (Übergangspfad, wird abgelöst) | Zugriffsweg über einen ablaufenden, teilnehmerbezogenen Einladungslink ohne internes Konto; jede Operation revalidiert Einladung, Hearing-Status, Teilnehmer und Dokumentgrant erneut. Zielzustand ist die vollständige Ablösung durch die Kontogebundene Teilnahme ohne anonym erreichbaren Rückfallweg (Bereich „Institutionszugang neu“, US-22 ff.). |
| Kontogebundene Teilnahme (Zielzustand) | Zugriffsweg, bei dem eine Einladung explizit an ein Keycloak-Konto (sub) gebunden wird; kanonischer Einstieg /portal/zugang-uebernehmen. Löst den Gastzugang ab. |
Technische Begriffe
| Begriff | Definition |
|---|---|
| RALQM | Verbindlicher Präfix für alle anwendungsspezifisch persistierten Identifikatoren der HearingPortal-Anwendung: Tabellen (RALQMHearings, RALQMInstitutions, …), Error-Code-Namespace (RALQM.<Feature>:), Permission-Gruppe RALQM und Rollen RALQMAdministrator/RALQMCaseOfficer/RALQMManagement. Technische, lowercase Slugs (z. B. ral-qualitymarks) bleiben davon unabhängig. Siehe Randbedingungen. |
| CAF | Präfix der wiederverwendbaren, anwendungsneutralen Framework-Bausteine (CAF.Modules.Authentication, CAF.Modules.Documents[.Contracts], CAF.Modules.UI.Blazor). Kein CAF.*-Projekt darf ein HearingPortal.*-Projekt oder GZ-Fachwissen referenzieren. |
| No-Layer (app-nolayers) | Architekturstil dieses Monorepos: Domain, Application und Persistence bleiben logische Schichten, werden aber pro Modul in einer kompakten Runtime-Assembly gebündelt statt in sieben ABP-Standardprojekten. Eine weitere Assembly (z. B. eigenes .Blazor-Projekt) entsteht erst bei echtem Bedarf (unabhängige Paketierung, austauschbarer Provider, gemessene Build-Isolation). Siehe Bausteinsicht. |
| ABP (ABP Framework) | Das zugrunde liegende .NET-Applikationsframework (Module, Application Services, dynamische Proxies für Authorization/Unit-of-Work/Audit/Validation, Identity, Permission-, Setting-, Audit-, Text-Template- und File-Management-Module). Der Host lädt ABP-Module als Composition Root; Fachcode nutzt vorhandene ABP-Bordmittel, bevor neue Infrastruktur entsteht. |
| Cohesion | Die verbindliche Syncfusion-basierte UI-Komponentenbibliothek für alle eigenen CAF-/GZ-Fachseiten (aus dem GitLab-Gruppen-Feed, zentrale Floating-Version 3.*-*). MudBlazor/Syncfusion/Blazorise dürfen von Fachcode nicht direkt gerendert werden; ausgenommen sind die unverändert übernommenen ABP-Administrations- und LeptonX-Seiten. Registrierung, Lizenzprüfung und Assets liegen in CAF.Modules.UI.Blazor. |
| Feature-Slice | Die Standardform eines Fachmoduls: ein schmales .Contracts-Projekt (DTOs, AppService-Interfaces, IDs, Permissions, Localization) plus eine Runtime (Domain, Application, EF Core) und optional eine .Blazor-Razor-Class-Library für Feature-Seiten. Vertikal nach Capability geschnitten (Authorization, Institutions, Hearings), nicht horizontal nach technischer Schicht. |
| Modulgrenze | Die durch Architekturtests erzwungene Compile-Time-Grenze zwischen Modulen: Consumer verwenden nur Contracts, nie eine fremde Runtime, Entity, Tabelle oder Repository; CAF.Modules.* → HearingPortal.* und Cross-Module-EF-Navigation/-Foreign-Keys sind verboten. Über Modulgrenzen werden ausschließlich IDs referenziert. Siehe Bausteinsicht, Querschnittliche Konzepte. |
| VERTRAG.md | Die verbindliche, deutschsprachige Vertragsdatei je CAF.Modules-Baustein (Wurzelverzeichnis des Projekts, z. B. src/Modules/Authentication/VERTRAG.md). Benennt zugesicherte öffentliche Fläche, ausdrückliche Nicht-Zusicherungen, Versionierungsregel und Feed-Voraussetzungen. Ein Quelltext-Vertrag, kein Paket-Readme: die Bausteine sind bewusst nicht packbar. FrameworkPackagingTests verlangt Existenz, nicht-leeren Inhalt und beide Pflichtabschnitte. Jede Änderung an der zugesicherten Fläche aktualisiert VERTRAG.md im selben Änderungssatz. |
| Seed | Der initiale/aktualisierende Datenbestand, der beim Migrations-/Startvorgang eingespielt wird (z. B. Rollen-Grants nach neuen Feature-Permissions). Läuft über HearingPortalDbMigrationService im Host; erfordert nach Rechteänderungen einen erneuten Seed-Lauf, aber keine Schemamigration. |
| Data-preserving Migration | Eine EF-Core-Migration, die Strukturänderungen (z. B. Umbenennung von Tabellen/Spalten beim RALQM-Rename) ohne Datenverlust umsetzt, typischerweise über RenameTable/RenameColumn statt Drop/Create. Historische Migrationen behalten dabei ihre alten Identifiers; nur neue Migrationen verwenden die aktuelle Namenskonvention. Pflichtnachweis: realer Round-Trip auf leerer SQL-Server-Datenbank plus has-pending-model-changes grün. |
| Aggregate | Ein fachliches Objekt, das seine Invarianten selbst über benannte Operationen (z. B. Submit, Approve, Cancel) schützt, keine öffentlichen Setter auf invarianten-relevanten Properties besitzt und optimistische Concurrency trägt. Beispiel: die Anhörung mit Teilnehmern, Dokumenten und Freigaben. |
| Application Service | Der Einstiegspunkt eines Anwendungsfalls: prüft Permission und Objektzugriff, lädt das Aggregate, ruft eine benannte Domänenoperation auf und persistiert innerhalb der ABP Unit of Work. Wird nicht sealed, damit ABP die nötige dynamische Proxy-Interception anwenden kann. |
| Contracts | Das schmale, öffentliche Projekt eines Moduls: DTOs, AppService-Interfaces, stabile IDs, Permission-Definitionen und DE/EN-Localization. Enthält keine EF-Typen, Entities, Repositories oder Implementierungen. |
| Host (Composition Root) | Das einzige startbare Projekt (HearingPortal/Host), das Module lädt, Middleware/Routing verkabelt, den gemeinsamen HearingPortalDbContext besitzt und Migrationen erzeugt/ausführt. Enthält bewusst keine neue Fachlogik oder Fachentities. |
| Interactive Server | Der von der Blazor-Web-App gewählte Render-Modus (kein WebAssembly-Client). Feature-Komponenten bleiben render-mode-neutral und setzen kein @rendermode; der Host entscheidet zentral. |
| BrowserGate | Der separate, exklusive End-to-End-Nachweis über einen echten Browser (Login, Hauptprozesse, Permission-Denial, Assets, Responsive-Verhalten, axe/WCAG). Läuft als Konsolenprogramm ohne Test-SDK, ist Teil der Solution, aber nicht Teil des normalen dotnet test-Laufs. |
| Quality Gate | Der zentrale Nachweis-Einstieg tools/scripts/Invoke-QualityGate.ps1: Preflight, Restore, Build ohne Warnungen, alle Solutiontests und has-pending-model-changes; übersprungene Pflichttests gelten als Fehlschlag. Läuft automatisch bei jedem Commit über versionierte Git-Hooks (ohne BrowserGate); vor Übergabe/Merge ist der vollständige Lauf mit -IncludeBrowserGate verbindlich. Details im Entwicklungsleitfaden. |