Skip to main content

Frontend, Blazor, routing, state en componentopbouw

24.1 Doel en positie binnen het Technisch Ontwerp

Dit hoofdstuk beschrijft de technische opbouw van de Blazor-frontend van OefenHub. De frontend vormt de gebruikersgerichte applicatieschil, verzorgt routing, layouts, componentcompositie, formulieren, validatiefoutweergave, loading states, lege toestanden en presentatie van rolgebonden schermen.

Voor de interactieve Blazor Web UI gebruikt OefenHub MudBlazor als primaire componentenbibliotheek. Deze keuze betekent niet dat schermen als losse MudBlazor- of HTML-pagina's worden opgebouwd. De implementatie gebruikt MudBlazor achter een eigen OefenHub-componentenlaag met herbruikbare wrappercomponenten, themetokens, layoutpatronen en testbare componentcontracten.

De frontend is nadrukkelijk geen eigenaar van autorisatie, domeinregels, datatoegang of bronmutaties. De UI mag informatie tonen, acties starten en viewmodels samenstellen, maar de uiteindelijke toegangscontrole, validatie en opslag blijven server-side belegd bij de betreffende modulecontracten en domeinservices.

De eerste technische baseline richt de gebruikersinteractie in via de Blazor-webapp. Er wordt geen publieke functionele API-laag voor externe clients of mobiele apps ontworpen. Desktop en tablet zijn leidend voor schermopbouw, interactiedichtheid en oefenervaring; smartphonegebruik mag technisch niet bewust worden geblokkeerd wanneer responsive basisgedrag dit toestaat, maar is geen primair ondersteund of geoptimaliseerd scenario.

Belangrijke inputbronnen zijn:

24.2 Begrippen: Razor-componenten versus Razor Pages

OefenHub gebruikt Blazor/Razor-componenten voor de webinterface. In dit hoofdstuk wordt met een page of pagina een routeerbare Blazor-component bedoeld. Dat is niet hetzelfde als het klassieke ASP.NET Core Razor Pages-model met .cshtml-pagina's als primair applicatiepatroon.

Klassieke Razor Pages worden in de eerste technische baseline niet als hoofdstructuur voor functionele schermen gebruikt. Als in een toekomstige uitbreiding een technische reden ontstaat om voor een zeer beperkte infrastructuurpagina of fallbackroute toch een Razor Page toe te voegen, moet die keuze expliciet worden vastgelegd en mag dat geen tweede frontendarchitectuur naast Blazor introduceren.

BegripBetekenis binnen OefenHub
Razor-componentHerbruikbare Blazor-component voor UI, layout of interactie.
Routeerbare componentBlazor-component met eigen route en paginafunctie.
PageFunctionele pagina in de Blazor-applicatie, technisch een routeerbare component.
Razor PageKlassiek ASP.NET Core .cshtml-patroon; niet het standaardpatroon voor OefenHub-schermen.

24.3 Basisprincipes

Voor de frontend gelden de volgende ontwerpprincipes.

PrincipeRegel
Server-side autorisatie is leidendRouteparameters, zichtbare knoppen, browsergeschiedenis, clientstate en componentstate mogen nooit toegang afdwingen of verruimen.
Webapp is primair kanaalOefenHub optimaliseert de interactie voor de Blazor-webapp; publieke externe clients of mobiele apps vallen buiten de eerste baseline.
Desktop en tablet leidendLayouts, navigatie en oefeninteractie worden primair voor desktop en tablet ontworpen; smartphoneweergave is best-effort en geen aparte acceptatiebasis.
UI is compositie, geen domeinlaagOefenHub.Web bevat geen domeinlogica, geen EF Core DbContexts en geen directe toegang tot module-interne entities.
Modulecontracten zijn de ingangDe UI roept publieke contracten, command-services, query-services en readers van modules aan.
Viewmodels zijn UI-specifiekWeb-viewmodels zijn afgestemd op schermopbouw en mogen geen database-entiteiten vervangen of lekken.
State is tijdelijk tenzij expliciet toegestaanUI-state is standaard tijdelijk en wordt alleen persistent wanneer het functioneel en privacytechnisch is toegestaan.
Responsiviteit is presentatiegedragEen compacte header of menu-indeling verandert geen autorisatie, rolcontext of zichtbare dataset.
Foutafhandeling is veiligTechnische details, identifiers en payloads worden niet aan eindgebruikers getoond.
Toegankelijkheid is vanaf de eerste render relevantToegankelijkheidskeuzes mogen vóór login technisch worden toegepast, maar bevatten geen persoonsgegevens of autorisatie-informatie.
MudBlazor is componentbasis, niet de applicatiestijlMudBlazor levert technische UI-primitives; OefenHub bepaalt de visuele identiteit via eigen thema, CSS en wrappercomponenten.
Herbruikbaarheid gaat vóór pagina-opbouwTerugkerende UI-patronen worden als herbruikbare componenten gebouwd; pagina's componeren die componenten en bevatten geen gekopieerde mockupstructuren.
HTML-mockups zijn geen implementatiebasisMockups helpen schermopbouw, navigatie en verwachtingen toetsen, maar productie-UI wordt opnieuw opgebouwd vanuit de OefenHub-componentenlaag.

24.4 Projectverantwoordelijkheid van OefenHub.Web

OefenHub.Web is het enige project dat de Blazor-applicatie host en gebruikersinteractie afhandelt. Het project is verantwoordelijk voor:

  • routing en routecompositie;
  • layouts en applicatieschil;
  • header, footer en navigatie;
  • routeerbare pagina's;
  • herbruikbare componenten;
  • MudBlazor-registratie, MudBlazor-themeconfiguratie en OefenHub-wrappercomponenten;
  • formulieren en client-side validatiefeedback;
  • viewmodels voor schermweergave;
  • page composition over meerdere modulequery's;
  • browsergerichte state;
  • toepassing van toegankelijkheidsvoorkeuren;
  • veilige loading-, empty- en error states;
  • SignalR-clientintegratie voor realtime weergave;
  • aanroepen van publieke modulecontracten.

OefenHub.Web is niet verantwoordelijk voor:

  • EF Core datatoegang;
  • migrations;
  • database-entiteiten;
  • businessregels;
  • module-interne services;
  • directe autorisatiebeslissingen;
  • het bepalen van bron-van-waarheid voor resultaten, berichten, meldingen of relaties;
  • het bewaren van server-side domeintoestand in browserstorage;
  • het kopiëren van HTML-, CSS- of JavaScriptstructuren uit mockups als productie-implementatie.

24.5 Voorgestelde mappenstructuur van OefenHub.Web

De volgende structuur geldt als technische baseline voor het webproject. Mappen worden pas aangemaakt wanneer daar daadwerkelijk inhoud voor bestaat; de structuur is bedoeld als ordeningskader en niet als verplicht lege mapset.

OefenHub.Web/
Components/
Shared/
Shell/
Feedback/
DataDisplay/
Forms/
Student/
Teacher/
Guardian/
Admin/
Messages/
Tickets/
Practice/
Catalog/

Pages/
Public/
Account/
Profile/
Accessibility/
Preferences/
Student/
Teacher/
Guardian/
Admin/
Messages/
Tickets/
Practice/
Relationships/

Layouts/

Navigation/

State/

Forms/

Validation/

Theming/

ViewModels/

PageComposition/

Extensions/

wwwroot/

24.5.1 Betekenis per map

MapInhoud
ComponentsHerbruikbare UI-componenten zonder eigen hoofdroute; generieke OefenHub-wrappercomponenten staan bij voorkeur onder Shared, Shell, Feedback, DataDisplay of Forms.
PagesRouteerbare pagina's, gegroepeerd per rol of functioneel domein.
LayoutsApplicatielayouts zoals publieke layout, ingelogde layout en oefenlayout.
NavigationMenu-opbouw, navigatiemodellen en presentatie van rolgebonden menu's.
StateUI-statecontainers en browserstate-adapters zonder autoriserende waarde.
FormsFormuliermodellen en formuliergerichte componenten.
ValidationClient-side validatiehulpen en mapping van servervalidatie naar UI.
ThemingMudBlazor-themeconfiguratie, OefenHub-designtokens en gedeelde stylingafspraken voor het webproject.
ViewModelsSchermspecifieke viewmodels voor rendering.
PageCompositionSamenstelling van pagina-viewmodels uit meerdere modulequery's.
ExtensionsRegistratie- en configuratie-extensies voor het webproject.
wwwrootStatische assets, CSS, JavaScript-hulpen en publieke webbestanden.

24.6 Components

Components bevat herbruikbare UI-bouwstenen. Een component mag lokale interactiestate hebben, maar mag geen domeinmutatie uitvoeren zonder expliciet aangeroepen modulecontract.

Voorbeelden:

Components/Shared/LoadingPanel.razor
Components/Shared/EmptyState.razor
Components/Shared/ConfirmDialog.razor
Components/Shared/PagedTable.razor
Components/Practice/ExerciseQuestionPanel.razor
Components/Practice/ExerciseProgressBar.razor
Components/Messages/UnreadBadge.razor
Components/Tickets/TicketStatusBadge.razor

Componenten worden bij voorkeur klein gehouden. Wanneer een component veel domeincontext nodig heeft, hoort de dataopbouw in PageComposition of in een pagina, niet in een diep geneste UI-component.

24.6.1 Componentregels

RegelToelichting
Componenten accepteren parametersData wordt via parameters of viewmodels aangeleverd.
Componenten lekken geen entitiesDatabase-entiteiten worden niet als componentparameter gebruikt.
Componenten voeren geen verborgen autorisatie uitAutorisatie gebeurt server-side via modulecontracten.
Componenten mogen events teruggevenBijvoorbeeld OnSave, OnCancel, OnSelected.
Componenten mogen loading en empty states tonenDe betekenis van de lege toestand komt uit de aanroepende pagina/viewmodel.

24.6.2 MudBlazor als gecontroleerde componentbasis

MudBlazor is de primaire componentenbibliotheek voor de interactieve Blazor Web UI. OefenHub gebruikt MudBlazor voor generieke UI-primitives zoals knoppen, invoervelden, selecties, tabs, dialogs, overlays, kaarten, menu's, snackbars, tooltips, progress-indicatoren, chips, badges en tabulaire weergaven.

De keuze voor MudBlazor is technisch begrensd:

  • MudBlazor wordt alleen gebruikt binnen OefenHub.Web en eventueel webspecifieke testprojecten.
  • Domeinmodules, applicatieservices, databaseprojecten, oefenmodulecontracten en readmodelcontracten krijgen geen afhankelijkheid op MudBlazor.
  • MudBlazor-types worden niet gebruikt in publieke modulecontracten, command/query-contracten, readmodels of domeinmodellen.
  • MudBlazor bepaalt niet welke acties zijn toegestaan, welke data zichtbaar is of welke businessregel geldt.
  • Server-side modulecontracten blijven bron voor autorisatie, validatie, mutaties en dataselectie.
  • MudBlazor is geen bron van functionele requirements en vervangt geen schermdocumentatie, Functioneel Ontwerp of Software Requirements Specification.

MudBlazor mag rechtstreeks in een routeerbare pagina worden gebruikt wanneer het gaat om eenvoudige, eenmalige compositie zonder herbruikbaar patroon. Zodra een UI-patroon terugkeert, roloverschrijdend is of eigen gedrag heeft, wordt het patroon als OefenHub-component of wrappercomponent geïsoleerd.

24.6.3 OefenHub-componentenlaag bovenop MudBlazor

OefenHub krijgt een eigen componentenlaag bovenop MudBlazor. Deze laag bevat herbruikbare componenten, layoutpatronen, themetoepassing en uniforme interactieafspraken. Pagina's gebruiken bij voorkeur deze OefenHub-componenten in plaats van losse combinaties van MudBlazor-componenten en pagina-eigen HTML.

Voorbeelden van herbruikbare componenten en patronen:

Component/patroonDoelVoorbeelden van gebruik
OefenHubPageHeaderTitel, context, primaire acties en secundaire navigatie.Frontpages, beheerpagina's, detailpagina's.
OefenHubCardStandaard kaartweergave met titel, inhoud, metadata en acties.Dashboardtegels, leerlingkaarten, modulekaarten.
OefenHubActionBarConsistente plaatsing van primaire, secundaire en destructieve acties.Opslaan, annuleren, exporteren, terugkeren.
OefenHubFilterBarFilters, zoekveld, sortering en resetactie.Geschiedenis, tickets, accounts, modules.
OefenHubDataList / OefenHubDataTableLijst- of tabelweergave met lege toestand, loading state en paginering.Accounts, meldingen, resultaten, relaties.
OefenHubStatusBadgeUniforme statusweergave zonder domeinlogica.Ticketstatus, online-status, modulevrijgave, runstatus.
OefenHubEmptyStateConsistente lege toestand met uitleg en optionele actie.Geen berichten, geen kinderen, geen resultaten.
OefenHubLoadingPanelConsistente laadweergave en skeleton/loadinggedrag.Pagina's, kaarten, tabellen, modals.
OefenHubConfirmDialogBevestiging voor risicovolle of definitieve acties.Verwijderen, ontkoppelen, overdragen, anonimiseren.
OefenHubFormSectionHerbruikbare formuliersectie met label-, hint- en validatiestructuur.Profiel, voorkeuren, beheerinstellingen.
OefenHubMessagePanelVeilige melding-, waarschuwing- en foutweergave.Servervalidatie, access denied, fallbackgedrag.
OefenHubLiveIndicatorConsistente realtime statusweergave zonder brondata te dupliceren.Online-overzicht, live meekijken.

De concrete namen mogen tijdens implementatie worden verfijnd, maar het principe is verplicht: terugkerende UI wordt niet per pagina opnieuw gebouwd.

24.6.4 Hergebruikregels

Voor hergebruik gelden de volgende regels:

RegelToelichting
Twee keer is componentkandidaatKomt dezelfde opbouw of interactie op twee schermen terug, dan wordt een herbruikbare component overwogen.
Roloverschrijdend is sharedPatronen die bij leerling, docent, ouder/voogd en beheerder terugkomen, horen in Components/Shared of een generieke submap.
Domeinspecifiek gedrag blijft gescheidenEen algemene kaart mag geen docent-, leerling- of supportbusinessregels bevatten. Domeinspecifieke varianten gebruiken parameters of aparte domeincomponenten.
Pagina's componerenRouteerbare pagina's halen data op via page composition en combineren componenten; zij bevatten zo min mogelijk visuele detailopbouw.
Styling is token- of themegedrevenKleuren, spacing, radius, typografie en states worden centraal beheerd via theme/CSS-variabelen, niet per pagina hardcoded.
Wrapper vóór herhalingTerugkerende combinaties van MudBlazor-componenten worden als OefenHub-wrapper vastgelegd voordat zij op meerdere plaatsen worden gekopieerd.
Oefenmodules blijven vrij binnen contractConcrete oefenmodule-rendering mag eigen interactieve componenten hebben, maar gebruikt gedeelde shell-, feedback-, status- en formuliercomponenten waar passend.

24.6.5 Gebruik van HTML-mockups

De bestaande HTML-mockups zijn ontwerp- en afstemmingsartefacten. Zij mogen worden gebruikt om schermopbouw, navigatie, inhoudsvolgorde, zichtbare labels, informatiestructuur, lege toestanden en interactie-intentie te begrijpen.

De HTML-mockups zijn geen technische implementatiebasis. Voor implementatie gelden daarom expliciet de volgende verboden:

  • mockup-HTML kopiëren naar .razor-pagina's als productiestructuur;
  • mockup-CSS kopiëren als pagina-eigen stylinglaag zonder vertaling naar OefenHub-theme of componentstijl;
  • mockup-JavaScript gebruiken als interactielogica in de Blazor-applicatie;
  • per scherm een zelfstandig opgebouwde componentstructuur maken wanneer hetzelfde patroon al elders voorkomt;
  • visuele overeenkomst boven componenthergebruik, toegankelijkheid, autorisatiegrenzen of testbaarheid plaatsen.

De juiste werkwijze is:

  1. bepaal uit schermdocumentatie en mockup welke functie, flow en visuele intentie nodig zijn;
  2. controleer of een bestaand OefenHub-componentpatroon dit dekt;
  3. breid waar nodig de componentencatalogus uit;
  4. bouw de pagina opnieuw op met Blazor, MudBlazor en OefenHub-wrappercomponenten;
  5. vergelijk het resultaat visueel en functioneel met schermdocumentatie en mockup;
  6. leg afwijkingen terug in schermdocumentatie, Functioneel Ontwerp of Technisch Ontwerp wanneer zij inhoudelijk relevant zijn.

24.6.6 Theming en visuele identiteit

De OefenHub-visuele identiteit wordt centraal ingericht. MudBlazor levert de componentbasis, maar niet automatisch de uiteindelijke stijl.

Minimale afspraken:

  • er komt één centrale MudBlazor-themeconfiguratie voor kleuren, typografie, spacing, radius, elevation en states;
  • OefenHub-specifieke CSS-variabelen of tokens worden gebruikt voor merkstijl, rolaccenten en kindvriendelijke visuele accenten;
  • pagina's gebruiken geen willekeurige inline styling voor kleuren, spacing of typografie;
  • toegankelijkheidsvarianten zoals contrast, lettergrootte en dyslexievriendelijke weergave worden via centrale theming of gecontroleerde CSS-classes toegepast;
  • publieke pagina's, ingelogde app, beheeromgeving en oefencontext mogen eigen layoutaccenten hebben, maar delen dezelfde technische componentbasis.

24.6.7 Review- en acceptatiecriteria voor frontendopbouw

Bij iedere nieuwe frontendpagina of substantiële wijziging wordt gecontroleerd:

  • of bestaande OefenHub-componenten zijn hergebruikt;
  • of nieuwe patronen kandidaat zijn voor de componentencatalogus;
  • of MudBlazor niet buiten OefenHub.Web lekt;
  • of mockup-HTML/CSS/JavaScript niet als productiecode is overgenomen;
  • of styling via theme, tokens of gedeelde componentstijl loopt;
  • of componenten toegankelijk, testbaar en parameteriseerbaar zijn;
  • of server-side autorisatie en validatie leidend blijven.

24.6.8 Validatie-uitkomst MudBlazor- en OefenHub-componentenstrategie

De MudBlazor- en OefenHub-componentenstrategie is voldoende afgebakend voor de V1.0-baseline. De keuze is geen open ontwerpvraag meer; tijdens implementatie moet alleen nog de concrete packageversie via centraal packagebeheer worden vastgepind.

OnderdeelBaselineafspraakValidatie
ComponentbibliotheekMudBlazor is de primaire componentenbibliotheek voor interactieve Blazor UI.MudBlazor wordt alleen vanuit OefenHub.Web en webspecifieke tests gebruikt.
PackagebeheerDe MudBlazor-packageversie wordt centraal beheerd en niet per project los vastgelegd.Package-updates lopen via review van componentcatalogus, theming, CSP en regressietests.
Services en providersMudBlazor-services, providers en globale configuratie worden éénmalig in de Web-startup/layoutlaag geregistreerd.Routeerbare pagina's registreren geen eigen globale MudBlazor-configuratie.
ThemeEr komt één centrale OefenHub-themeconfiguratie bovenop MudBlazor.Kleuren, typografie, spacing, radius, elevation, states en toegankelijkheidsvarianten lopen via theme/tokens/gedeelde CSS.
ComponentencatalogusTerugkerende UI-patronen worden als OefenHub-componenten of wrappercomponenten vastgelegd.Nieuwe pagina's hergebruiken bestaande componenten voordat pagina-eigen opbouw wordt toegevoegd.
IndelingHerbruikbare componenten worden logisch gegroepeerd, bijvoorbeeld onder Components/Shared, Components/Layout, Components/Forms, Components/DataDisplay, Components/Feedback en domeinspecifieke submappen.Routeerbare pagina's blijven compositielaag en bevatten zo min mogelijk visuele detailopbouw.
MockupconversieHTML-mockups zijn ontwerpinput en geen productiecode.Review controleert dat mockup-HTML, mockup-CSS en mockup-JavaScript niet naar .razor-pagina's zijn gekopieerd.
DomeingrensMudBlazor- en componenttypes lekken niet naar modulecontracten, readmodels, commands, queries of domeinmodellen.Architecture tests en review bewaken dat UI-techniek binnen de Web-grens blijft.
TestaanpakComponentgedrag, parameters, events, empty/loading/error states en wrappercomponenten krijgen bUnit-componenttests.End-to-end smoke- en regressietests gebruiken Playwright .NET.

Daarmee is het open punt over de MudBlazor- en OefenHub-componentenstrategie opgelost. Een latere overstap naar een andere componentbibliotheek, een tweede componentbibliotheek als structurele basis of het loslaten van de OefenHub-componentenlaag vereist een nieuw Technisch Ontwerp-besluit.

24.7 Pages

Pages bevat routeerbare Blazor-componenten. Pagina's zijn verantwoordelijk voor:

  • ophalen van schermdata via page composition of query-services;
  • tonen van de juiste componenten;
  • starten van gebruikersacties via command-services;
  • verwerken van loading-, empty-, validation- en error states;
  • doorgeven van routeparameters aan server-side contracten zonder die zelf als autorisatiebewijs te behandelen.

Voorbeelden:

Pages/Public/LandingPage.razor
Pages/Account/LoginCallback.razor
Pages/Profile/ProfilePage.razor
Pages/Student/StudentFrontpage.razor
Pages/Practice/ExerciseStartPage.razor
Pages/Practice/ActiveExerciseRunPage.razor
Pages/Teacher/StudentsPage.razor
Pages/Guardian/ChildrenPage.razor
Pages/Messages/MessageOverviewPage.razor
Pages/Tickets/MyTicketsPage.razor
Pages/Admin/SiteSettingsPage.razor

24.7.1 Routeparameters

Routeparameters zijn alleen technische invoer voor de server-side controle. Zij zijn geen bewijs van toegang.

Voorbeeld:

/practice/history/{runId}

De pagina mag runId doorgeven aan een publieke reader, maar de reader moet opnieuw controleren:

  • bestaat de run;
  • is de run afgerond wanneer dat voor de pagina vereist is;
  • hoort de run bij de actuele gebruiker of toegestane rolcontext;
  • is de gevraagde actie toegestaan;
  • mag de response detaildata bevatten.

24.8 Layouts

Layouts bepalen de globale schermstructuur. De exacte styling kan binnen de schermdocumentatie en implementatie worden verfijnd; de technische verantwoordelijkheden liggen vast.

LayoutGebruik
PublicLayoutNiet-ingelogde pagina's zoals landingspagina en vaste publieke pagina's.
AppLayoutIngelogde hoofdapplicatie met header, footer, navigatie en profielmenu.
ExerciseLayoutAfleidingsvrije leerling-oefencontext.
AdminLayoutBeheerpagina's met beheercontext en beheerstructuur, indien de hoofdapplicatielayout onvoldoende is.
ErrorLayoutVeilige 40x/50x-foutpagina's.

De layout bepaalt visuele structuur, maar niet de autorisatie. Als een menu-item verborgen is, blijft dat alleen presentatie. De onderliggende route en moduleactie moeten server-side beschermd blijven.

24.9 Navigation

Navigation bevat modellen en helpers voor het opbouwen van zichtbare navigatie. Navigatie wordt samengesteld op basis van de server-side bepaalde sessie-, rol- en contextinformatie die via publieke contracten wordt opgehaald.

Navigatie mag:

  • menu-items tonen of verbergen;
  • items groeperen onder rolkoppen;
  • responsief inklappen;
  • badgewaarden tonen wanneer die zichtbaar mogen zijn;
  • de actieve route markeren.

Navigatie mag niet:

  • zelf rollen toekennen;
  • zelf bepalen dat een route toegestaan is;
  • autorisatie afleiden uit browserstate;
  • datasets verruimen omdat een menu-item zichtbaar is;
  • leerling-oefencontext verstoren met badges of overlays.

24.9.1 Responsieve navigatie

De header blijft in de basis één regel hoog. Wanneer items niet passen, worden zij gegroepeerd volgens de functionele regels uit het Functioneel Ontwerp en de schermdocumentatie. Deze groepering is uitsluitend presentatiegedrag.

SituatieUI-gedragAutorisatiegevolg
Leerlingcategorieën passen nietGroeperen onder Categorieën.Geen wijziging.
Rolmenu-items passen nietGroeperen onder Beheer, Docent, Ouder/Voogd.Geen wijziging.
Header wordt extra smalVerdere groepering onder Menu.Geen wijziging.
Actieve oefenrunBadges en notificatie-indicaties visueel uitstellen.Server-side status blijft behouden.

24.10 State

State wordt in OefenHub bewust beperkt. De frontend mag geen tweede bron van waarheid worden naast de server-side modules.

24.10.1 Statecategorieën

StatecategorieVoorbeeldenToegestaan persistent?Bron van waarheid
Lokale componentstateopen/dicht, geselecteerde tab, tijdelijke invoerNee, tenzij explicietComponent
Pagina-stateactieve filter, paginanummer, sorteringSoms via querystring of gebruikersvoorkeurModulequery of gebruikersinstelling
Rolcontextweergaveactieve frontpagecontext, menuweergaveNee als autorisatiebronServer-side context
Toegankelijkheidsstate vóór logincontrast, dyslexielettertype, lettergrootteJa, technisch beperktCookie/browserwaarde tot login
Gebruikersvoorkeuren na loginnaamweergave, sortering, verborgen waarschuwingJa, server-sideUserSettings binnen OefenHub.Identity
Oefenvoortganghuidige vraag, antwoordstatus, totalenNee als browserbronPractice-module
Live meekijkstatebrowse-modus, geselecteerde vraag in viewerLokale UI-stateLive/progressbron blijft server-side
Notificatie- en badge-stateongelezen teller, wacht-op-mijNee als bronCommunication/Support-query's

24.10.2 Browser storage

Browser storage en cookies mogen alleen gegevens bevatten die geen persoonsgegevens, autorisatiedata, rolcontext, identity-providerdata, tokens of gevoelige payloads bevatten.

Toegestaan, mits veilig en beperkt:

  • toegankelijkheidskeuzes vóór login;
  • niet-autoriserende UI-voorkeuren;
  • gesloten/uitgestelde systeemnotificatie voor OncePerBrowser, voor zover functioneel zo bedoeld;
  • tijdelijke lokale UI-keuzes zonder domeinwaarde.

Niet toegestaan:

  • rollen;
  • rechten;
  • gebruikers-ID's als autorisatiebron;
  • kind-, leerling- of runcontext als toegangsbewijs;
  • tokens of identity-providergegevens;
  • antwoorden, vraagpayloads of resultaatdetails;
  • systeemberichtinhoud of privéberichtinhoud.

24.11 PageComposition

PageComposition is de plek waar OefenHub.Web schermspecifieke viewmodels samenstelt uit publieke query-services van modules. Deze laag is bedoeld om te voorkomen dat Blazor-pagina's zelf veel coördinatielogica krijgen.

Voorbeeld:

PageComposition/StudentFrontpageComposer.cs
PageComposition/TeacherFrontpageComposer.cs
PageComposition/GuardianFrontpageComposer.cs
PageComposition/AdminFrontpageComposer.cs

Een composer mag meerdere query-services aanroepen:

StudentFrontpageComposer
→ Practice-query voor recent geoefend
→ Catalog-query voor beschikbare categorieën
→ Communication-query voor badges, tenzij oefencontext actief is

Een composer mag niet:

  • rechtstreeks DbContexts gebruiken;
  • entities uit Data/Entities gebruiken;
  • mutaties uitvoeren;
  • autorisatiebeslissingen nemen buiten modulecontracten;
  • technische exceptions aan de UI doorgeven.

24.12 ViewModels

Viewmodels zijn UI-specifieke modellen. Zij mogen worden afgestemd op rendering, labels, lege toestanden, knopstatussen en lokale UI-samenstelling.

Voorbeeld:

ViewModels/Student/StudentFrontpageViewModel.cs
ViewModels/Practice/ExerciseStartPageViewModel.cs
ViewModels/Messages/MessageOverviewViewModel.cs
ViewModels/Tickets/TicketDetailViewModel.cs
ViewModels/Admin/SiteSettingsHubViewModel.cs

Viewmodels bevatten geen EF Core tracking, geen database-entiteiten en geen domeinmutatiemethoden.

24.12.1 Verschil tussen DTO, readmodel en viewmodel

TypeEigenaarDoel
Contract-DTOModuleproject onder Contracts/Models.Data-uitwisseling via publieke modulecontracten.
ReadmodelModuleproject onder Models/ReadModels.Module-eigen geoptimaliseerde leesvorm.
ViewmodelOefenHub.Web/ViewModels.Schermspecifieke rendering en UI-compositie.

24.13 Forms en Validation

Formulieren mogen client-side gebruiksvriendelijk valideren, maar server-side validatie blijft leidend voor opslag en mutaties.

24.13.1 Formulieropbouw

OnderdeelPlaats
UI-formuliermodelOefenHub.Web/Forms of ViewModels wanneer schermspecifiek.
Client-side validatiehulpenOefenHub.Web/Validation.
DomeinvalidatieBetreffende module.
Servervalidatie-resultaatPubliek contractresultaat van moduleactie.
FoutweergaveWebcomponent of formulierpagina.

Voorbeeldflow:

1. Gebruiker vult formulier in.
2. Web voert directe UI-validatie uit voor verplichte velden en invoerformaat.
3. Web stuurt command naar modulecontract.
4. Module voert autorisatie, domeinvalidatie en opslagcontrole uit.
5. Module retourneert success, validation errors of veilige foutstatus.
6. Web toont veldfouten, algemene fout of succesweergave.

Client-side validatie mag nooit worden gebruikt als enige bescherming tegen foutieve of ongeautoriseerde mutaties.

24.14 Routing en routebescherming

Routing wordt centraal beheerd binnen OefenHub.Web. Routebescherming bestaat uit twee lagen:

  1. grove toegang op basis van ingelogde/niet-ingelogde status en basiscontext;
  2. server-side object- en actiecontrole in de modulecontracten.

De eerste laag voorkomt onnodige schermtoegang. De tweede laag is leidend.

24.14.1 Routecategorieën

RoutecategorieVoorbeeldenControle
Publieklandingspagina, vaste publieke pagina'sGeen gebruikerssessie vereist.
Account/provisioninglogin-callback, accountfout, profielaanvullingIdentity-context.
Leerlingoefenen, geschiedenis, resultatenStudent-context + Practice/Catalog-controle.
Docentoefenaanbod, leerlingen, onlineTeacher-context + modulecontrole.
Ouder/voogdkinderen, geschiedenis, onlineGuardianStudent-relatiecontrole.
Beheerdersite-instellingen, content, accountsAdmin-context + beheerautorisatie.

24.14.2 Routeparameters als onbetrouwbare invoer

Routeparameters worden altijd behandeld als onbetrouwbare invoer. Een pagina mag een routeparameter alleen doorgeven aan een modulecontract dat de context opnieuw valideert.

Voorbeeld:

/guardian/children/{childId}/history

De UI mag childId niet gebruiken om zelf resultaatdata te selecteren. De ouder-/voogdmodule of practice-reader moet server-side controleren of de actuele gebruiker op dat moment een actieve ouder-/voogdrelatie met het kind heeft.

24.15 Autorisatie en zichtbare acties

Zichtbare knoppen zijn geen autorisatiebewijs. Een knop kan verborgen of disabled zijn om de gebruiker te helpen, maar de actie erachter moet altijd opnieuw server-side worden gecontroleerd.

Voorbeeld:

UI-elementServer-side controle
Start nieuweLeerlingcontext, oefening actief, niveau/categorie/oefening toegankelijk.
Verder gaanNiet-afgeronde run binnen dezelfde oefening en actieve niveaucontext.
Kijk live meeActieve relatie/context, actieve run, livebeschikbaarheid.
Download PDFRun bestaat, afgerond, viewer heeft toegang.
Accepteer uitnodigingUitnodiging bestaat, pending, niet verlopen, rolcontext toegestaan.
Melding heropenenActuele sluiting, heropentermijn, eigenaar van melding.

24.16 Actieve rolcontext in de UI

De actieve rolcontext wordt server-side bepaald. De UI mag deze context tonen en gebruiken voor presentatie, maar mag haar niet zelfstandig wijzigen zonder server-side bevestiging.

Bij combinatierollen toont de frontend de samengestelde frontpage volgens de vastgelegde prioriteit. De onderliggende blokken blijven afkomstig uit afzonderlijke modulequeries en rolcontexten.

Voorbeelden:

Beheerder + Docent + Ouder/Voogd
→ beheerderblokken
→ docentblokken
→ ouder-/voogdblokken
Docent + Ouder/Voogd
→ docentblokken
→ ouder-/voogdblokken

Een gebruiker krijgt door zichtbaarheid van een blok geen extra toegang tot detailroutes. Detailroutes blijven opnieuw gecontroleerd.

De applicatieschil bestaat uit header, hoofdcontent, footer, profielmenu en algemene notificatie-/popupvoorzieningen.

24.17.1 Header

De header bevat afhankelijk van context:

  • logo en begroeting;
  • hoofdnavigatie;
  • categorie- of rolmenu's;
  • berichtenicoon en badge;
  • meldingenindicatie;
  • profielmenu;
  • responsieve groepering.

Tijdens actieve leerling-oefenruns worden afleidende signaleringen verborgen of uitgesteld. De onderliggende server-side status blijft intact.

De footer volgt de functionele responsiviteitsregels. De technische footercomponent mag beheerbare content ophalen via publieke beheer-/contentcontracten, maar de layoutstructuur blijft codegedreven.

24.17.3 Profielmenu

Het profielmenu toont alleen acties die voor de actuele sessiecontext relevant zijn. Toch blijft iedere achterliggende route server-side beschermd.

24.17.1 Categorie- en oefeningmenu-interactie

De Blazor-implementatie van categorie- en oefeningmenu's volgt de functionele interactieregels uit het Functioneel Ontwerp. De UI mag hover-, klik- en touchgedrag technisch verschillend afhandelen, maar mag geen afwijkende autorisatie- of datasemantiek introduceren.

ContextTechnische afspraak
Desktop hoverMouse-over mag het menu openen, maar de component moet voorkomen dat dezelfde gebruikersintentie dubbel wordt verwerkt wanneer direct daarna een click-event volgt.
Desktop klikLinker muisklik mag het menu openen of de actieve categorie wisselen, tenzij de openactie al door de voorafgaande hoveractie is verwerkt.
Tablet/touchTouch/tap is de primaire trigger voor openen en wisselen van categorie. Hovergedrag mag niet nodig zijn om een oefening te bereiken.
BuitenklikEen globale click-away/touch-away handler sluit het submenu wanneer buiten het menu of de categorieknoppen wordt geactiveerd.
WisselenDe navigatiestate bevat maximaal één open categorie of oefeningmenu tegelijk. Openen van een andere categorie sluit de vorige.
Hover verlatenPointer-leave buiten het submenu sluit niet automatisch zolang geen andere categorie wordt geactiveerd; dit voorkomt onverwacht verdwijnen tijdens navigatie.

De componentstate voor een open menu is tijdelijk UI-state. De lijst met categorieën en oefeningen wordt altijd opgebouwd uit server-side geautoriseerde data en mag niet uit browserstate of alleen uit eerder gerenderde markup worden afgeleid.

24.18 Afleidingsvrije oefencontext

De actieve leerling-oefenrun krijgt een eigen visuele context. De leerling moet niet worden afgeleid door communicatie- of systeemsignalen terwijl de oefening actief is.

Technisch betekent dit:

OnderdeelGedrag tijdens actieve oefenrun
BerichtenbadgeNiet zichtbaar actualiseren.
MeldingenindicatieUitstellen of verbergen.
Systeemnotificatie-overlayNiet tonen boven actieve oefening.
Popup voor niet-oefenkritieke feedbackUitstellen tot na verlaten/afronden.
OefenvoortgangDirect server-side opslaan via Practice-module.
Live meekijkupdatesBlijven server-side en via realtime transport beschikbaar.

De UI mag deze signalen visueel onderdrukken, maar mag de onderliggende ongelezenstatus, ticketstatus, notificatiegegevens of livevoortgang niet verliezen.

24.19 SignalR en realtime UI

SignalR wordt gebruikt als transport voor realtime UI-updates, zoals live meekijken, badges en online-status. SignalR is geen bron van waarheid.

Voor de frontend gelden de volgende regels:

  • realtime updates worden alleen toegepast wanneer zij bij de actuele server-side context passen;
  • reconnects mogen geen autorisatie overslaan;
  • na reconnect wordt relevante status opnieuw opgehaald of gevalideerd;
  • live meekijken blijft read-only;
  • browse-modus in live meekijken is lokale UI-state;
  • terugkeren naar live gebruikt de server-side actuele vraag/progressie.

Wanneer SignalR wegvalt, toont de UI een veilige fout- of disconnectstatus en mag de gebruiker geen onjuiste indruk krijgen dat de livegegevens nog actueel zijn.

Reconnect-UI volgt de baseline uit hoofdstuk 15: automatische pogingen na 0, 2, 10, 30 en 60 seconden, daarna een definitief verbroken status met een expliciete actie om opnieuw te verbinden of te vernieuwen. Tijdens reconnect worden livegegevens als tijdelijk niet-actueel gemarkeerd.

24.20 Popups, systeemnotificaties en foutweergave

Popups en systeemnotificaties hebben verschillende doelen en mogen technisch niet door elkaar lopen.

TypeDoelBron
Popupregister-popupGerichte feedback, bevestiging of foutmelding bij een actie.Admin/contentconfiguratie of vaste popupkey.
Systeemnotificatie-overlaySitebrede of doelgroepgerichte notificatie na frontpageload.Admin-notificatiebeheer.
Mailbox-systeemberichtBericht in berichtenoverzicht met eventuele domeinverwijzing.Communication-module.
FormuliervalidatieVeld- of formulierfouten.Web + modulevalidatie.
Veilige foutpagina40x/50x-foutafhandeling.Web/errorhandling.

De UI toont geen technische stacktraces, SQL-fouten, tokens, payloads of interne identifiers aan eindgebruikers.

24.21 Loading states en lege toestanden

Loading states en lege toestanden zijn normale UI-toestanden en mogen niet automatisch als fout worden behandeld.

Voorbeelden van geldige lege toestanden:

  • geen gekoppelde kinderen;
  • geen afgeronde runs;
  • geen online leerlingen;
  • geen open meldingen;
  • geen ongelezen berichten;
  • geen filterresultaten;
  • geen beschikbare vervolgactie.

Een lege toestand wordt pas een autorisatiefout wanneer de server-side module aangeeft dat de actuele gebruiker de gevraagde dataset of actie niet mag raadplegen.

24.22 Toegankelijkheid en voorkeuren

Toegankelijkheidskeuzes moeten technisch vroeg genoeg beschikbaar zijn om de UI vóór login bruikbaar te renderen. Tegelijk mag deze browserwaarde geen persoons- of autorisatiegegevens bevatten.

24.22.1 Vóór login

Toegestaan in browserwaarde:

  • verhoogd contrast;
  • dyslexielettertype;
  • basis lettergrootte-instelling.

Niet toegestaan:

  • gebruikers-ID;
  • naam;
  • rolcontext;
  • niveaucontext;
  • relatiecontext;
  • identity-providergegevens;
  • tokens.

24.22.2 Na login

Na login zijn server-side profielinstellingen en UserSettings binnen OefenHub.Identity leidend. De technische browserwaarde mag worden bijgewerkt zodat rendergedrag vóór een volgende login consistent blijft, maar de server-side instelling blijft de bron van waarheid.

24.22.3 Profielavatarselectie

De profielavatarselectie gebruikt alleen vooraf gedefinieerde avatars uit ProfileAvatars. OefenHub.Web toont de beschikbare avataropties via een publieke identity-query of viewmodel en stuurt bij opslaan alleen de gekozen avatar-id naar de identity-service.

UI-onderdeelRegel
AvataroverzichtToont alleen actieve en selecteerbare avatars.
AvataropslagWeb schrijft niet rechtstreeks naar Users.ProfileAvatarId, maar gebruikt een publiek identity-contract.
UploadveldenNiet tonen en server-side weigeren; vrije upload, externe URL's en vrije bestandsnamen zijn niet toegestaan.
FallbackweergaveBij ontbrekende, inactieve of niet langer selecteerbare avatar toont Web een standaardavatar zonder de opgeslagen waarde stilzwijgend te wijzigen.
Cache/readmodelHeader, profielmenu en profielpagina verversen na succesvolle wijziging via normale UI-state of readmodelinvalidatie.

24.23 Frontend en modulecontracten

OefenHub.Web gebruikt publieke modulecontracten. De concrete implementaties blijven in moduleprojecten.

Voorbeeld:

OefenHub.Web
→ OefenHub.Practice.Contracts
→ OefenHub.Catalog.Contracts
→ OefenHub.Communication.Contracts
→ OefenHub.Support.Contracts

In de gekozen projectopbouw staan publieke contracts in het moduleproject zelf. Web mag dus het moduleproject referencen, maar niet de interne implementaties gebruiken.

Niet toegestaan:

OefenHub.Web
→ PracticeDbContext
→ ExerciseRun entity
→ direct SQL op practice.ExerciseRuns

Wel toegestaan:

OefenHub.Web
→ IExerciseRunResultReader
→ ExerciseRunResultViewDto

24.24 Dependency injection in Web

Het webproject registreert modules via extension methods, maar kent geen interne implementatiedetails.

Voorbeeldstructuur:

Extensions/
ServiceCollectionExtensions.cs
WebApplicationExtensions.cs

Voorbeeld van gewenste richting:

services.AddOefenHubIdentity(...)
services.AddOefenHubAuthorization(...)
services.AddOefenHubCatalog(...)
services.AddOefenHubPractice(...)
services.AddOefenHubCommunication(...)
services.AddOefenHubSupport(...)
services.AddOefenHubLiveMonitoring(...)
services.AddOefenHubAdmin(...)
services.AddOefenHubReporting(...)
services.AddOefenHubScheduling(...)

De exacte methode- en parameternaamgeving wordt tijdens implementatie bepaald, maar de richting is dat modules zichzelf registreren via publieke extensiepunten.

24.25 Webspecifieke validatie van projectgrenzen

Voor OefenHub.Web gelden aanvullende projectgrenzen.

Verboden patroonReden
Directe referentie naar Data/Entities van modulesLekt database-entiteiten naar UI.
Direct gebruik van module-DbContextsDoorbreekt modulegrenzen en autorisatielaag.
Businessregels in .razor-componentenMaakt domeinlogica ontestbaar en versnipperd.
Autorisatie op basis van alleen zichtbare knoppenClientstate is manipuleerbaar.
Persoons- of autorisatiedata in local storagePrivacy- en securityrisico.
Technische foutdetails in UIInformatielek.
Gekopieerde mockup-HTML als .razor-structuurMaakt pagina's zelfstandig opgebouwd, slecht herbruikbaar en los van de OefenHub-componentenlaag.
Per pagina eigen CSS voor generieke patronenDoorbreekt theming, consistentie, toegankelijkheid en onderhoudbaarheid.
MudBlazor-types in modulecontracten of domeinlagenLekt Web-/UI-techniek buiten de frontendgrens.

24.26 Teststrategie voor frontend

De teststrategie voor de frontend sluit aan op het algemene testhoofdstuk.

TesttypeDoel
ComponenttestsControleren van componentrendering, parameters, events, lege toestanden en OefenHub-wrappercomponenten rond MudBlazor.
Componentcatalogus-regressietestsControleren dat gedeelde OefenHub-componenten visueel en functioneel stabiel blijven wanneer MudBlazor of theming wijzigt.
Page composition testsControleren dat viewmodels correct uit modulequery's worden samengesteld.
Routing testsControleren dat routes basiscontext afdwingen en veilige fallback tonen.
Authorization UI testsControleren dat verboden acties niet zichtbaar zijn, zonder server-side tests te vervangen.
Integration testsControleren dat Web via publieke modulecontracten werkt.
Accessibility testsControleren van contrast, toetsenbordgebruik, labels en voorkeurstoepassing.
Realtime UI testsControleren van SignalR-updategedrag, reconnect en disconnectweergave.
Error state testsControleren dat veilige foutmeldingen worden getoond.

Architecture tests moeten aanvullend bewaken dat OefenHub.Web geen module-interne entities of DbContexts gebruikt.

De toolkeuze voor frontendtests is:

TestlijnToolingGebruik
ComponenttestsbUnitRendering, parameters, events, validatie, OefenHub-wrappercomponenten en page composition.
End-to-endtestsPlaywright .NETBeperkte browsergedreven smoke- en regressietests voor primaire flows.

Nieuwe herbruikbare OefenHub-componenten krijgen bij voorkeur eerst bUnit-tests. Nieuwe pagina's krijgen alleen E2E-dekking wanneer zij onderdeel zijn van een primaire flow of een eerder gevonden regressierisico afdekken.

24.27 Implementatiechecklist

Bij het toevoegen of wijzigen van een frontendpagina moet minimaal worden gecontroleerd:

  • Is de pagina in de juiste map onder Pages geplaatst?
  • Zijn herbruikbare onderdelen als component geïsoleerd?
  • Zijn bestaande OefenHub-componenten hergebruikt voordat nieuwe pagina-eigen opbouw is toegevoegd?
  • Is voorkomen dat HTML-, CSS- of JavaScript uit mockups als productiecode is gekopieerd?
  • Gebruikt de pagina uitsluitend publieke modulecontracten?
  • Worden routeparameters server-side opnieuw gevalideerd?
  • Bevat de pagina geen directe DbContext- of entitytoegang?
  • Worden loading, empty, validation en error states expliciet afgehandeld?
  • Is client-side validatie alleen ondersteunend?
  • Wordt geen autorisatie-informatie in browserstorage opgeslagen?
  • Werkt de pagina met toetsenbord en screenreaderlabels waar relevant?
  • Zijn badges/notificaties correct onderdrukt tijdens actieve oefenruns?
  • Zijn foutmeldingen veilig en niet technisch lekgevoelig?
  • Blijft MudBlazor beperkt tot OefenHub.Web en webspecifieke tests?
  • Loopt styling via theme, tokens of gedeelde componentstijl in plaats van losse pagina-CSS?
  • Is de impact op Functioneel Ontwerp, Software Requirements Specification, schermdocumentatie, usecases, Technisch Ontwerp en database-informatie beoordeeld?

24.28 Implementatieverificaties

De volgende punten moeten tijdens implementatie of detailontwerp nog concreet worden gecontroleerd:

PuntControle
Blazor hostingdetailsDefinitieve keuze en configuratie van render-/interactiemodel per omgeving vastleggen.
Accessibility toolingBepalen welke automatische en handmatige toegankelijkheidschecks in CI of review worden opgenomen.
Browserstorage-wrapperBepalen of er één centrale wrapper komt voor veilige browserwaarde-toegang.
PopupintegratieVastleggen hoe popupregisterkeys technisch naar UI-componenten worden gemapt.
SignalR reconnect UIControleren dat 0/2/10/30-reconnectstatus, definitieve verbroken status en herlaadactie in componenttests zijn afgedekt.
CSP-impactControleren welke inline scripts/styles of third-party assets door de CSP worden geraakt.
FormuliervalidatieStandaardiseren hoe servervalidatiefouten naar velden en algemene meldingen worden vertaald.