Architectuur
Dit onderdeel beschrijft de vastgelegde hoofdarchitectuur van OefenHub. De architectuurdocumentatie maakt zichtbaar hoe de applicatie op hoofdlijnen is opgebouwd, waar de systeemgrens ligt, welke externe afhankelijkheden bestaan en welke technische keuzes als uitgangspunt gelden voor verdere uitwerking.
Doel van dit onderdeel
De architectuurdocumentatie heeft drie hoofddoelen:
- het vastleggen van de gekozen hoofdarchitectuur van OefenHub;
- het zichtbaar maken van de systeemgrens, externe systemen, containers en belangrijkste componentgroepen;
- het bieden van een stabiele basis voor het latere Technisch Ontwerp.
De architectuurdocumentatie vervangt het Functioneel Ontwerp, de Software Requirements Specification of de database-informatielaag niet. Zij beschrijft de technische structuur en samenhang waarmee de functionele baseline en requirements technisch kunnen worden geplaatst.
Inhoudsopgave
Opbouw
De architectuurdocumentatie gebruikt het C4-model als ordeningsprincipe:
- C4 niveau 1 beschrijft de systeemcontext van OefenHub.
- C4 niveau 2 beschrijft de hoofdcontainers en externe technische afhankelijkheden.
- C4 niveau 3 beschrijft de belangrijkste componentgroepen binnen de OefenHub-webapplicatie.
- C4 niveau 4 beschrijft interne bouwblokken op hoog-over niveau, zonder code-implementatie of klassendiagrammen als normatieve bron te maken.
Diagrammen worden in de documentatie tekstueel en/of via DSL-bronnen beschreven. Gerenderde afbeeldingen kunnen als apart publicatie-artefact worden beheerd en zijn niet de primaire bron van architectuurwaarheid.
Scope en afbakening
De architectuurdocumentatie beschrijft de structuur, systeemgrenzen, technische hoofdkeuzes en onderlinge samenhang van OefenHub. De volgende afbakening geldt:
- Het Functioneel Ontwerp is leidend voor functioneel gedrag, rollen, domeinregels, flows en schermcontext.
- De Software Requirements Specification is leidend voor centrale requirements, prioriteiten, acceptatiecriteria en traceability.
- De database-informatielaag beschrijft de relationele gegevensstructuur en vormt input voor het Technisch Ontwerp.
- Het Technisch Ontwerp werkt later implementatiedetails uit, zoals services, queries, indexes, caching, background jobs, deployment, monitoring en technische teststrategie.
- Deze architectuurdocumentatie bevat geen volledige ERD, geen database-DDL, geen uitgewerkte API-specificatie en geen codeontwerp op class-niveau.
Brondocumenten
- Functioneel Ontwerp: functionele baseline, domeinregels, rollen, lifecycle en schermcontext.
- Software Requirements Specification: centrale requirements, acceptatiecriteria, prioriteiten en traceability.
- Database-informatielaag: gegevensstructuur, tabellen, relaties, enums en technische datagrondslag.
- Oefenmodule-documentatie: moduleplatform, modulecontract en concrete module-eisen.
Architectuuruitgangspunten
- OefenHub wordt als middel-zware modulaire monoliet opgezet.
- Authenticatie, registratie, sessies en credential-lifecycle liggen bij Keycloak als externe identity provider.
- OefenHub gebruikt een eigen interne identiteit naast een externe identity-koppelsleutel.
- Desktop en tablet zijn leidend; smartphonegebruik is geen primair ondersteund gebruiksscenario.
- De database is niet internet-facing en wordt alleen via de backend/webserver benaderd.
- Er is geen publieke functionele API-laag voor externe clients of mobiele apps; primaire interactie verloopt via de webapp.
- MudBlazor is de gekozen componentenbibliotheek voor de interactieve Blazor Web UI, maar OefenHub hanteert een eigen herbruikbare componenten- en themelaag bovenop MudBlazor.
- Realtime communicatie verloopt centraal via SignalR.
- Background jobs, cleanup en tijdsafhankelijke afhandeling verlopen binnen de modulaire monoliet via TickerQ.
- Gestructureerde logging verloopt via Serilog; Seq wordt ingezet als centrale logviewer.
- QuestPDF is de gekozen oplossing voor document- en PDF-generatie.
- Oefenmodules worden via een strategy/plugin-achtig model aangesloten en via beheer gecontroleerd beschikbaar gemaakt.
- Relationele kerngegevens worden gecombineerd met modulespecifieke JSON/base64-payloads voor oefenconfiguratie, vragen en voortgang.
Betekenis van middel-zware modulaire monoliet
Met middel-zware modulaire monoliet wordt bedoeld dat OefenHub als één deploybare .NET/Blazor-webapplicatie wordt gebouwd, maar intern niet als één ongestructureerd codeblok wordt georganiseerd. Functionele domeinen zoals identiteit, autorisatie, relaties, catalogus, oefenen, communicatie, support, live meekijken, beheer, rapportage en scheduling krijgen duidelijke project- en modulegrenzen.
Deze keuze betekent concreet:
- er is één applicatieproces en één primaire relationele database in de eerste baseline;
- modulegrenzen worden afgedwongen via projectstructuur, publieke contracten,
internalimplementaties, architecture tests en module-eigen DbContexts/schema's; - domeinmodules gebruiken elkaars interne entities, DbContexts en implementatieclasses niet rechtstreeks;
- cross-module gegevensverwijzingen gebruiken standaard publieke contracten, queryservices, soft links of snapshots;
- de architectuur vermijdt de operationele complexiteit van microservices, maar houdt de codebase voorbereid op onderhoud, testbaarheid en latere afsplitsing wanneer dat ooit nodig wordt.
De term monoliet beschrijft dus deployment en runtime. De term modulair beschrijft de interne structuur en eigenaarschapgrenzen.
Beslissingenregister
| ID | Beslissing | Reden | Niet gekozen alternatief |
|---|---|---|---|
| ADR-001 | Middel-zware modulaire monoliet in de eerste productfase | Beperkte operationele complexiteit, één team/context, expliciete interne modulegrenzen en geen harde schaal- of beschikbaarheidseis voor microservices. | Microservices of één ongestructureerde monoliet |
| ADR-002 | Keycloak als externe identity provider | Auth, registratie, sessies en credentialbeheer buiten OefenHub houden. | Eigen auth-systeem in OefenHub |
| ADR-003 | Geen smartphonegerichte ondersteuning als primair gebruiksscenario | Desktop en tablet zijn leidend; mobiele weergave is functioneel niet geschikt voor de primaire oefenervaring. | Volledige mobiele optimalisatie |
| ADR-004 | Directe anonimisatie bij accountverwijdering | Dataminimalisatie, consistente lifecycle en behoud van domeinhistorie. | Tussenstatus 'verwijderd maar nog niet geanonimiseerd' |
| ADR-005 | Database niet internet-facing | Kleiner aanvalsoppervlak en duidelijke infrastructuurscheiding. | Directe publieke databaseblootstelling |
| ADR-006 | Geen publieke functionele API-laag | De primaire interactie loopt via de webapp; dit houdt de systeemgrens en integratiestrategie bewust compact. | Publieke functionele API voor externe clients of mobiele app |
| ADR-007 | Hybride persistentiestijl voor oefeningen en runs | Uniforme, querybare metadata blijven relationeel; modulespecifieke configuratie en toestand blijven flexibel in JSON/base64. | Volledig relationeel model of volledig blob-only opslagmodel |
| ADR-008 | Technische oefenmodules via strategy/plugin-achtig model en databasegestuurd vrijgeven | Houdt generieke platformlogica gescheiden van modulespecifieke implementaties en maakt gecontroleerde modulevrijgave mogelijk. | Hardcoded oefenlogica of automatische runtime-discovery zonder beheervrijgave |
| ADR-009 | SignalR als centrale realtime-mechaniek | Ondersteunt live meekijken, online-status en directe voortgangsupdates via één consistent realtimekanaal. | Polling of versnipperde realtime-oplossingen per domein |
| ADR-010 | TickerQ voor background jobs en cleanup | Maakt tijdsafhankelijke afhandeling, retries en opschoning beheersbaar binnen de modulaire monolithische webapp. | Ad-hoc timers, handmatige cleanup of losse scheduler zonder centrale governancelaag |
| ADR-011 | Frontend-context runtime samengesteld en layout codegedreven | Multi-role context, frontpage-opbouw en schermkeuze blijven in code beheersbaar; inhoud is wel databasegestuurd. | Persistent contextmodel per rolcombinatie of vrije pagebuilder/CMS-opbouw |
| ADR-012 | Browserlokale client-state waar passend | Ondersteunt pre-login toegankelijkheid en once-per-browser notificatiegedrag zonder onnodig server-side readmodel. | Alles gebruikersgebonden server-side opslaan |
| ADR-013 | Communicatiedomeinen expliciet gescheiden | Mailboxgerichte systeemberichten, site-notificaties, privéberichten en tickets volgen verschillende lifecycle- en deliverypatronen. | Eén generiek communicatiemodel voor alle berichtsoorten |
| ADR-014 | UTC intern, lokale tijd in presentatie | Voorkomt inconsistentie in schedulerlogica, historie, meldingen en audit. | Lokale tijd opslaan of vergelijken in backendlogica |
| ADR-015 | Blazor Web App als gekozen webmodel | Sluit aan op de gekozen modulaire monoliet-aanpak en de gewenste rijke, maar gecontroleerde interactielaag. | Klassieke server-rendered webapp als primair model |
| ADR-016 | Serilog + Seq voor gestructureerde logging | Vergroot herleidbaarheid en vereenvoudigt troubleshooting door structurele logging en centrale inzage. | Ongestructureerde logging of uitsluitend lokale/logfile-gebaseerde inzage |
| ADR-017 | QuestPDF voor document- en PDF-generatie | Biedt een vaste, programmeerbare en reproduceerbare route voor PDF-uitvoer vanuit historisch consistente brondata. | Browser print/HTML-export of losse documentgeneratie zonder vaste library |
| ADR-018 | Profielavatars via vaste set | Beperkt moderatie- en beveiligingslast en houdt profielweergave uniform. | Vrije uploads van profielafbeeldingen |
| ADR-019 | Live-view audit sessiegebaseerd | Legt meekijken herleidbaar vast met beheersbaar datavolume en duidelijke start/eindsemantiek. | Per realtime event volledig auditen |
| ADR-020 | Docent-testruns tijdelijk en opschoonbaar | Houdt testgedrag gescheiden van reguliere leerlinghistorie en beperkt blijvende vervuiling van domeindata. | Permanente opslag in dezelfde lifecycle als reguliere runs |
| ADR-021 | Aspire in development voor config/secrets-pariteit | Verkleint verschillen tussen development en productie door uniforme configuratie- en secretsafhandeling. | Losse, afwijkende developmentconfiguratie zonder centrale uniformering |
| ADR-022 | MudBlazor als gecontroleerde UI-componentbasis | Versnelt realisatie van consistente Blazor UI-patronen, terwijl OefenHub via eigen thema, wrappercomponenten en componentencatalogus visueel en technisch herbruikbaar blijft. | Volledig custom UI-framework of per pagina zelfstandig opgebouwde HTML/CSS zonder herbruikbare componenten |
Verdere technische uitwerking
De volgende onderwerpen zijn bewust niet volledig uitgewerkt in de architectuurbaseline en horen in het Technisch Ontwerp of beheerbeleid:
- concrete deploymentinrichting en hostingtopologie;
- operationele observability rond Serilog, Seq, monitoring en alerting;
- concrete mailprovider en technische mailinrichting;
- database-DDL, indexes, queryvormen en migratiestrategie;
- caching, materialisatie en readmodel-implementatie;
- loadtestopzet, performanceprofielen en technische teststrategie;
- eventuele deploymentdiagrammen, sequence diagrams of security views;
- detailinrichting van de OefenHub-componentencatalogus, themetokens en componenttestaanpak bovenop MudBlazor;
- verdere technische detaillering van C4 niveau 4 waar implementatie-inzichten dat nodig maken.
Begrippenlijst
| Begrip | Betekenis |
|---|---|
| Middel-zware modulaire monoliet | Eén deploybare applicatie met één primaire database, maar intern georganiseerd in duidelijke moduleprojecten met eigen verantwoordelijkheden, contracten, boundaries, DbContexts/schema's waar persistentie nodig is en architecture tests voor dependencyregels. |
| C4-model | Manier om softwarearchitectuur op vier niveaus te beschrijven: context, containers, componenten en code. |
| System Context | Niveau 1 van het C4-model; toont het systeem in zijn omgeving. |
| Container | Een zelfstandig uitvoerbaar of opslaand hoofdonderdeel, zoals een webapplicatie, database of extern platform. |
| Component | Een logisch onderdeel binnen een container, zoals een service, module of subdomein. |
| Identity provider | Extern systeem dat authenticatie en credentialbeheer uitvoert, in dit geval Keycloak. |
| Collaborator | Docent met expliciet niveau-bewerkrecht op een niveau dat eigendom heeft van een andere docent. |
| Exercise run | Een concrete, unieke uitvoering van een oefening door een gebruiker. |
| Container Diagram | Niveau 2 van het C4-model; toont de belangrijkste containers binnen het systeem en hun onderlinge relaties. |
| Webapp | De primaire OefenHub webapplicatiecontainer waarin gebruikersinteractie, businesslogica en integraties samenkomen. |
| Component Diagram | Niveau 3 van het C4-model; toont de belangrijkste logische componenten binnen één gekozen container en hun onderlinge samenhang. |
| SignalR | De in OefenHub gekozen realtime-mechaniek voor live meekijken, presence en directe updates tussen browser en webapp. |
| TickerQ | De gekozen scheduler-/jobtechnologie voor background jobs, cleanup en tijdsafhankelijke afhandeling binnen de monoliet. |
| SystemMessage | Mailboxgericht systeembericht binnen OefenHub, gescheiden van site-notificaties en privéberichten. |
| SiteNotification | Doelgroepgerichte UI-notificatie die in de webapp wordt getoond en browserlokaal once-per-browser gedrag kan hebben. |
| Hybride persistentiestijl | Opslagprincipe waarbij relationele kerngegevens worden gecombineerd met modulespecifieke JSON/base64-payloads. |
| MudBlazor | De gekozen Blazor-componentenbibliotheek voor generieke interactieve UI-primitives binnen OefenHub.Web; MudBlazor is geen bron voor domeinlogica, requirements of visuele OefenHub-identiteit. |
| OefenHub-componentenlaag | Herbruikbare set OefenHub-wrappercomponenten, layoutcomponenten, themetokens en UI-patronen die MudBlazor gecontroleerd toepast en schermen technisch consistent opbouwt. |
| HTML-mockup | Bespreek- en toetsartefact voor schermopbouw, navigatie, informatiestructuur en visuele richting; geen implementatieskelet en geen bron voor productie-HTML/CSS/JavaScript. |
| Oefenmodule | Technische module die specifieke oefenlogica, configuratievalidatie, rendering en vraag-/antwoordverwerking levert binnen het generieke OefenHub-platform. |