Skip to main content

Oefenruns, voortgang, resultaten, statistieken en PDF-brondata

10.1 Doel en positie binnen het Technisch Ontwerp

Dit hoofdstuk beschrijft de technische verwerking van oefenen binnen OefenHub: het starten van een oefening, het opslaan van voortgang, het verwerken van antwoorden, het afronden van een run, het vastleggen van uniforme resultaten en statistieken en het beschikbaar maken van historische brondata voor resultaatweergave, geschiedenis, live meekijken en PDF-export.

De technische eigenaar van dit domein is OefenHub.Practice. Deze module beheert de brondata voor daadwerkelijke oefenuitvoeringen. De catalogus bepaalt welke oefening, categorie, niveau en technische module gekozen worden; Practice legt vast wat er op het moment van starten daadwerkelijk is uitgevoerd en welke voortgang en resultaten daaruit zijn ontstaan.

Het hoofdstuk dupliceert geen volledige tabeldefinities uit de database-informatie. De de de database-informatie blijft de bron voor tabellen, kolommen, relaties en constraints. Dit hoofdstuk legt vast hoe die data technisch wordt gebruikt binnen EF Core, services, modulegrenzen, snapshots, transaction boundaries en exportflows.

10.2 Ontwerpuitgangspunten

OnderwerpTechnisch uitgangspunt
Module-eigenaarOefenHub.Practice is eigenaar van exercise runs, voortgang, gedeelde oefenrecords en resultaatbrondata.
DatabasecontextPracticeDbContext beheert de practice-tabellen.
Schemapractice, volledig in kleine letters.
BrongegevensAfgeronde en lopende oefenruns zijn de bron voor voortgang, resultaten, geschiedenis, live meekijken en PDF-export.
ModulegrenzenAndere modules gebruiken publieke Practice-contracts en krijgen geen directe toegang tot PracticeDbContext of entities.
CatalogusverwijzingenVerwijzingen naar catalogusobjecten zijn cross-module soft links en waar nodig aangevuld met snapshots.
GebruikersverwijzingenVerwijzingen naar gebruikers zijn cross-module soft links en waar nodig aangevuld met snapshots of geanonimiseerde weergavewaarden.
VraaginhoudModule-specifieke vraag- en antwoorddata wordt generiek opgeslagen als versieerbare payload.
Uniforme veldenUniforme totalen en statistieken staan als expliciete runvelden beschikbaar voor snelle en consistente uitlezing.
PDF-brondataPDF-export wordt opnieuw opgebouwd uit historische runbrondata en snapshots; PDF-bestanden zijn tijdelijke output.
Live meekijkenLive meekijken leest server-side opgeslagen voortgang; SignalR is transport en geen bron van waarheid.

10.3 Project- en mapstructuur

OefenHub.Practice volgt de standaard moduleprojectstructuur uit het Technisch Ontwerp. Binnen deze module worden runlogica, opslag, contracts, readmodels en domeinservices bij elkaar gehouden.

OefenHub.Practice/
Contracts/
Models/
Enums/
Data/
PracticeDbContext.cs
Entities/
Configurations/
Migrations/
Models/
Commands/
Queries/
ReadModels/
Enums/
Services/
Interfaces/
Events/
Helpers/
Extensions/

De implementatieclasses, EF Core entities en interne services zijn standaard internal. Alleen publieke module-ingangen onder Contracts zijn bedoeld voor gebruik door andere modules.

10.4 Verantwoordelijkheden van OefenHub.Practice

VerantwoordelijkheidBeschrijving
Run startenAanmaken van een nieuwe exercise run op basis van een gevalideerde catalogus- en modulecontext.
Run hervattenBepalen en openen van de laatst gestarte niet-afgeronde run binnen dezelfde oefening en niveaucontext.
Voortgang opslaanServer-side verwerken en opslaan van iedere bevestigde vraagstap.
Antwoordresultaat vastleggenOpslaan van gegeven antwoord, resultaatstatus, Geen idee-status en timing.
Uniforme totalen bijwerkenBijwerken van aantallen goed, fout, Geen idee, afgerond en resterend waar nodig.
Run afrondenVastleggen van afrondmoment, eindstatus, statistieken en historische brondata.
Geschiedenis leverenReadmodels leveren voor afgeronde niet-test runs binnen geautoriseerde context.
Resultaatdetail leverenHistorische vraag- en antwoorddetails leveren voor resultaatweergave en export.
Gedeelde oefeningen beherenAdministratieve shared records beheren tot de ontvanger daadwerkelijk een eigen run start.
Testoefeningen ondersteunenDocenttestruns isoleren van reguliere geschiedenis en opruimbaar houden.
PDF-bronmodel leverenHistorisch exportmodel leveren aan Reporting of de PDF-exportservice.
Live brondata leverenActuele voortgang beschikbaar maken voor LiveMonitoring.

10.5 Relatie met andere modules

ModuleRelatie met Practice
OefenHub.WebRoept publieke command- en querycontracts aan voor starten, beantwoorden, hervatten, resultaten en geschiedenis.
OefenHub.AuthorizationLevert server-side autorisatiecontrole en contextcontrole voor leerling, docent, ouder/voogd en beheerder.
OefenHub.CatalogLevert gevalideerde oefening-, niveau-, categorie- en modulecontext bij start/configuratie.
OefenHub.ExerciseModuleHostResolveert de technische module voor vraaggeneratie, antwoordcontrole, rendering en exportrepresentatie.
OefenHub.RelationshipsLevert relatiecontrole voor delen met vrienden en ouder-/voogd- of docentcontexten waar relevant.
OefenHub.CommunicationKan systeemcommunicatie ontvangen na delen of relevante oefengebeurtenissen, uitsluitend via publieke contracts.
OefenHub.LiveMonitoringLeest actuele voortgang via publieke readcontracts; voert geen mutaties op runs uit.
OefenHub.ReportingVraagt exportmodellen op voor PDF-generatie; PDF-output is geen brondata.
OefenHub.SchedulingTriggert opruiming van testruns, tijdelijke exports of retrybare naverwerking via publieke contracts.

Practice mag geen interne entities, DbContexts of services van andere modules gebruiken. Cross-module communicatie verloopt via contracts, query-services, command-services of expliciet vastgelegde workflowstappen.

10.6 Kernobjecten op logisch niveau

Onderstaande objecten zijn logisch beschreven. De exacte tabel- en kolomdefinities blijven in de database-informatie.

ObjectDoelEigenaarschap
ExerciseRunHoofdrecord van één unieke oefenuitvoering.Practice
ExerciseRunProgressVraaggebonden voortgang en status binnen een run.Practice
SharedExerciseAdministratief record voor een ontvangen gedeelde oefening voordat de ontvanger start.Practice
PracticeHistoryReadModelAfgeleid leesmodel voor geschiedenis.Practice, onder Models/ReadModels
PracticeResultDetailReadModelAfgeleid leesmodel voor resultaatdetail.Practice, onder Models/ReadModels
PracticePdfExportModelTijdelijk exportmodel voor PDF-generatie.Practice levert model, Reporting rendert output.
PracticeLiveProgressReadModelAfgeleide actuele voortgang voor live meekijken.Practice levert bron, LiveMonitoring toont.

10.7 Run lifecycle

Een exercise run doorloopt een beperkte lifecycle. Er wordt bewust geen zware workflow-state-machine geïntroduceerd voor normale leerlingruns. De status wordt afgeleid uit start-, voortgangs- en afrondgegevens.

FaseTechnische betekenisPersistente bron
AangemaaktRunrecord bestaat met gekozen context, moduleversie en gegenereerde vraagset.ExerciseRun + payload/progress.
GestartDe eerste vraag kan of is getoond; starttijd is bekend.ExerciseRun.StartedAtUtc en vraagprogressie.
LopendEén of meer vragen zijn verwerkt, maar run is niet afgerond.ExerciseRunProgress + uniforme voortgangsvelden.
OnderbrokenGebruiker verlaat oefencontext, verbinding valt weg of logout vindt plaats zonder afronding.Run blijft niet-afgerond.
AfgerondLaatste vraag is definitief verwerkt en resultaatstap is bereikt.ExerciseRun.CompletedAtUtc en statistiekvelden.
HistorischAfgeronde run wordt gebruikt voor geschiedenis, resultaatdetail en export.Opgeslagen runvelden + historische payload/snapshots.
Test-opruimbaarDocenttestrun is afgerond of verlaten en mag volgens beleid worden verwijderd.IsTestRun en schedulercriteria.

Niet-afgeronde runs verschijnen niet als afgeronde geschiedenisregel. Zij kunnen wel worden gebruikt voor hervatten en live meekijken zolang de run toegankelijk en functioneel geldig is.

10.8 Starten van een nieuwe run

Een nieuwe run ontstaat alleen na een expliciete startactie vanuit een toegankelijke oefeningcontext. De UI mag deze startactie tonen, maar de server bepaalt opnieuw of starten is toegestaan.

10.8.1 Startflow

1. Web ontvangt startactie.
2. Web roept Practice-startcontract aan.
3. Practice vraagt server-side context/autorisatiecontrole aan.
4. Practice vraagt geldige catalogus- en modulecontext op.
5. Practice resolveert technische module via ExerciseModuleHost.
6. Module genereert vragen op basis van configuratie en gevraagde aantallen.
7. Practice maakt ExerciseRun en voortgangsbrondata aan.
8. Practice commit de run binnen PracticeDbContext.
9. Web navigeert naar het oefenscherm met de eerste vraag.

10.8.2 Vast te leggen startcontext

Bij start worden minimaal de volgende typen informatie vastgelegd of afgeleid:

GegevenTechnische behandeling
StudentUserIdSoft link naar identity/account.
LevelIdSoft link naar catalogusniveau.
CategoryIdSoft link naar cataloguscategorie of niveau-categoriekoppeling, afhankelijk van DB-model.
ExerciseIdSoft link naar catalogusoefening.
ExerciseModuleIdSoft link naar catalogusmodulemetadata.
ModuleKeySnapshot of redundante technische sleutel voor module-resolving.
ModuleVersionSnapshot van moduleversie/contractversie bij starten.
ConfigurationVersionSnapshot of hash van gebruikte configuratiepayload.
LevelNameSnapshotHistorische weergave/fallback.
CategoryNameSnapshotHistorische weergave/fallback.
ExerciseNameSnapshotHistorische weergave/fallback.
CreatedAtUtcAanmaakmoment.
StartedAtUtcStartmoment of eerste toonmoment, afhankelijk van implementatiekeuze.
TotalQuestionCountGevraagde/gegeneerde vraagsetgrootte.
IsTestRunOnderscheid tussen reguliere run en docenttest.
SourceSharedExerciseIdSoft link naar gedeeld record indien gestart vanuit gedeelde oefening.
DuplicateOfRunIdBinnen practice te behandelen runrelatie voor opnieuw maken.

Snapshots worden niet stilzwijgend aangepast bij toekomstige wijzigingen in catalogus, identiteit of modulemetadata. Zij waarborgen historische leesbaarheid.

10.9 Hervatten van runs

Hervatten gebruikt de laatst gestarte niet-afgeronde run binnen dezelfde oefening en actieve niveaucontext. De bepaling gebeurt server-side en gebruikt geen browserstate als autorisatiebron.

ControleRegel
GebruikerDe run hoort bij de ingelogde leerling of geautoriseerde context.
OefeningDe run hoort bij dezelfde oefening als de geopende startpagina.
NiveauDe run hoort bij dezelfde actieve niveaucontext.
StatusDe run is niet afgerond.
ToegangDe actuele context mag de run nog openen of hervatten.
TeststatusTestoefeningen worden niet als reguliere hervatbare leerlingruns behandeld.

Niet-afgeronde runs uit andere niveaus, niet langer toegankelijke contexten of testcontexten worden niet als normale hervatoptie aangeboden.

10.10 Vraagvoortgang

Na iedere bevestigde stap wordt voortgang server-side verwerkt. Dat geldt voor gewone antwoorden, Geen idee, antwoord tonen en de laatste stap richting resultaat.

VeldtypeDoel
QuestionNumberStabiele volgordepositie binnen de run.
QuestionPayloadModule-specifieke vraagrepresentatie.
AnswerPayloadModule-specifieke antwoordrepresentatie.
UserAnswerPayloadDoor gebruiker gegeven antwoordrepresentatie.
FirstShownAtUtcEerste toonmoment van de vraag.
AnsweredAtUtcDefinitieve antwoordverwerking.
IsCompletedAdministratieve afronding van deze vraag, niet van de hele run.
IsCorrectUniforme goed/fout-status.
IsDunnoUniforme Geen idee-markering.
DurationMsAfgeleide of opgeslagen doorlooptijd.
ResultStateUniforme status voor UI/readmodels.

IsCompleted op vraagniveau betekent uitsluitend dat die specifieke vraag administratief is afgerond. Dit is niet hetzelfde als runniveau-afronding.

10.11 Payloadstrategie

Technische modules kunnen sterk verschillende vraag-, antwoord- en configuratiestructuren hebben. Daarom worden module-specifieke gegevens niet volledig relationeel uitgesplitst.

PayloadEigenschap
ConfiguratiepayloadCatalogusbron voor oefeningconfiguratie; gebruikt bij start.
VraagpayloadModule-specifieke vraagdata per gegenereerde vraag.
AntwoordpayloadCorrect antwoord of evaluatiegegevens, module-specifiek.
GebruikersantwoordpayloadInvoer van de leerling, module-specifiek.
ResultaatpayloadEventuele module-specifieke toelichting of renderdata.

Payloads zijn versieerbaar en moeten door de technische module geïnterpreteerd kunnen worden. De modulehost en concrete module zijn verantwoordelijk voor compatibele interpretatie van payloads binnen de ondersteunde versiegrenzen.

10.11.1 JSON/base64

De functionele basis gaat uit van JSON/base64-opslag voor module-specifieke payloads. Technisch betekent dit:

RegelToelichting
JSON is de inhoudelijke structuurDe module serialiseert naar een afgesproken DTO-versie.
Base64 is opslag-/transportomhullingBase64 is geen encryptie en mag niet als securitymaatregel worden gezien.
Payloadversie is verplichtIedere payload moet naar module- en schema-/contractversie herleidbaar zijn.
Uniforme velden blijven relationeelTotalen, statussen, timing en filters staan niet uitsluitend in payloads.
Payloadherberekening is herstelpadNormale UI, geschiedenis en PDF lezen uniforme velden en snapshots.

Payloads mogen geen credentials, tokens of onnodige persoonsgegevens bevatten.

10.12 Antwoordverwerking

De generieke applicatie verwerkt de runstatus, voortgang en uniforme velden. De technische module voert de inhoudelijke antwoordcontrole uit.

1. Web stuurt antwoordactie naar Practice.
2. Practice valideert run, vraagpositie en autorisatie.
3. Practice resolveert de technische module en payloadversie.
4. Module evalueert het antwoord.
5. Practice slaat uniforme resultaatstatus en payloadupdates op.
6. Practice werkt runniveau-totalen bij.
7. Practice commit de vraagmutatie.
8. Practice maakt actuele voortgang beschikbaar voor live meekijken.

De evaluatie door de technische module mag geen directe databasewrites doen. Alle persistente runmutaties lopen via Practice.

10.13 Geen idee-actie

De Geen idee-actie is een aparte vraagroute. De waarschuwing en de verborgen voorkeur horen bij gebruikersinstellingen; de definitieve vraagmutatie hoort bij Practice.

StapTechnische regel
Eerste klikWeb vraagt, indien nodig, bevestiging via popup/voorkeur.
AnnulerenEr blijft geen definitieve fout- of afrondmutatie achter.
BevestigenVraag wordt als IsDunno = true vastgelegd.
ScoreVraag telt uniform als fout.
Antwoord tonenJuiste antwoord/renderdata wordt getoond via module-output.
VoorkeurWaarschuw me niet weer wordt alleen opgeslagen bij bevestiging.

10.14 Uniforme totalen

Uniforme totalen worden relationeel op runniveau bijgehouden zodat geschiedenis, live meekijken, resultaatweergave en PDF-export niet telkens volledige payloads hoeven te reconstrueren.

VeldtypeMoment van bijwerken
TotalQuestionCountBij start/generatie.
CompletedQuestionCountNa iedere bevestigde vraagafronding.
CorrectCountNa iedere bevestigde antwoordverwerking.
IncorrectCountNa iedere fout of Geen idee.
DunnoCountNa bevestigde Geen idee.
CurrentQuestionNumberNa navigatie/antwoordverwerking waar relevant.
StartedAtUtcBij start of eerste vraagtoonmoment.
CompletedAtUtcPas na definitieve runafronding.
IsCompletedPas na definitieve runafronding.

De uniforme totalen zijn leidend voor reguliere schermweergave. Herberekening uit payloads is uitsluitend bedoeld als technische herstel- of controlemogelijkheid.

10.15 Statistiekberekening

Complexere statistiekvelden worden na afronding van een run berekend. De berekening gebruikt opgeslagen vraagtiming en uniforme vraagstatussen.

StatistiekTechnische bron
Gemiddelde tijd per vraagVraagdoorlooptijden binnen afgeronde run.
MediaanGesorteerde vraagdoorlooptijden.
Q1/Q3Onderste en bovenste helft van gesorteerde vraagdoorlooptijden.
IQRQ3 minus Q1.
OndergrensQ1 - 1,5 × IQR.
BovengrensQ3 + 1,5 × IQR.
Uitschieters ondergrensVraagdoorlooptijden kleiner dan ondergrens.
Uitschieters bovengrensVraagdoorlooptijden groter dan bovengrens.
Totale doorlooptijdVerschil tussen start- en afrondmoment of som van relevante vraagdoorlooptijden volgens vastgelegde definitie.
Snelste/langzaamste vraagMin/max van vraagdoorlooptijden.

De exacte berekeningsmethode moet deterministisch zijn en in tests worden vastgelegd. Afronding, eenheden en formatting worden niet door de database bepaald maar door services/readmodels/rendering.

10.15.1 Statistiekservice

De statistiekberekening hoort binnen Practice thuis, bijvoorbeeld als interne service:

Services/
Interfaces/
IPracticeStatisticsCalculator.cs
PracticeStatisticsCalculator.cs

Deze service is module-intern, tenzij andere modules expliciet dezelfde berekening nodig hebben. Andere modules lezen opgeslagen statistiekvelden via publieke readcontracts.

10.16 Afronden van een run

Afronden gebeurt pas nadat de laatste vraag definitief is verwerkt en de gebruiker de resultaatstap bereikt. De afronding is een kritieke mutatie binnen Practice.

1. Controleer run- en vraagstatus.
2. Controleer dat alle vereiste vragen administratief afgerond zijn.
3. Bereken uniforme eindtotalen en statistieken.
4. Vul CompletedAtUtc en IsCompleted.
5. Leg eventuele duplicate/shared-context vast.
6. Commit binnen PracticeDbContext.
7. Maak resultaatreadmodel beschikbaar.
8. Start alleen niet-kritieke vervolgacties na commit.

Wanneer afronding faalt, blijft de run niet-afgerond. De gebruiker krijgt een veilige foutmelding en er wordt correlation-logging vastgelegd.

10.17 Resultaatweergave

Resultaatweergave leest opgeslagen runcontext, vraagvoortgang, uniforme totalen en statistiekvelden. Het scherm rekent de functionele brondata niet opnieuw uit clientstate.

ResultaatonderdeelBron
Naam gebruikerIdentity/accountcontext of snapshot/geanonimiseerde weergave.
Niveau/categorie/oefeningSnapshots op runniveau, met live fallback alleen als passend.
VraagdetailsExerciseRunProgress en module-renderrepresentatie.
Gegeven antwoordUserAnswerPayload via module-rendering.
Juiste antwoordAnswerPayload of module-evaluatie-output.
ResultaatstatusUniforme vraagstatus.
StatistiekenOpgeslagen runstatistieken.
DuplicaatmeldingRunrelatie binnen practice.

10.18 Geschiedenis

Geschiedenisreadmodels tonen alleen afgeronde, niet-test runs binnen de geautoriseerde context.

ContextBegrenzing
LeerlingEigen afgeronde runs binnen gekozen oefening/niveau of alles-context.
DocentAfgeronde leerlingruns binnen eigen docentcontext en niveauautorisaties.
Ouder/voogdAfgeronde runs van actief gekoppelde kinderen over alle historische niveaus.
BeheerderAlleen via expliciete beheer-/supportcontext en volgens autorisatieregels.

Filters en paginering zijn readmodelgedrag en wijzigen geen run, voortgang, resultaat of statistiek.

10.19 Gedeelde oefeningen

Een gedeelde oefening is eerst een administratief record. Pas wanneer de ontvanger start, ontstaat een eigen zelfstandige exercise run.

StapTechnische verwerking
Delen vanuit afgeronde runPractice maakt shared record aan na relatiecontrole.
Informeren ontvangerVia Communication, kritiek of niet-kritiek afhankelijk van functionele ingang.
Overzicht ontvangenLeest shared records die voor ontvanger zichtbaar zijn.
Eerste start door ontvangerMaakt zelfstandige run met broninhoud en oorspronkelijke volgorde.
Opnieuw maken door ontvangerMaakt nieuwe zelfstandige run, eventueel met andere volgorde.
Verwijderen uit ontvangen overzichtSoft-deactivatie/verbergmarkering van shared record voor ontvanger.
GeschiedenisReeds afgeronde runs blijven zichtbaar via geschiedenis.

10.19.1 Snapshotgedrag bij delen

Shared records bewaren tekstuele momentopnamen van niveau, categorie en oefening zoals die waren op het moment van delen. Toekomstige categorie- of modulemigraties herschrijven deze snapshots niet.

10.20 Opnieuw maken en duplicaten

Maak deze oefening opnieuw maakt altijd een nieuwe zelfstandige run. De bronrun blijft ongewijzigd.

OnderdeelRegel
BronrunBlijft historisch ongewijzigd.
Nieuwe runKrijgt eigen voortgang, antwoorden, totalen en statistieken.
VraaginhoudWordt gebaseerd op de bronrun en modulepayloads.
VolgordeMag worden gewijzigd volgens functionele regel.
DuplicateOfRunIdBinnen practice vastgelegd als runrelatie.
ModulebeschikbaarheidAls module niet meer bruikbaar is, is opnieuw maken niet beschikbaar.

10.21 Docenttestruns

Docenttestruns mogen technisch dezelfde runstructuren gebruiken, maar worden geïsoleerd via IsTestRun en verschijnen niet in reguliere resultaten of geschiedenis.

AspectRegel
OpslagMag tijdelijk in practice worden opgeslagen.
GeschiedenisNiet zichtbaar in reguliere leerling-, docent- of oudergeschiedenis.
StatistiekenNiet meetellen in normale readmodels/tellers.
OpruimingVia OefenHub.Scheduling en Practice-contract.
Achtergebleven dataPeriodiek opruimbaar op basis van status, leeftijd en testflag.

10.22 PDF-brondata

PDF-export gebruikt dezelfde historische bron als resultaatweergave. Practice levert een exportmodel; Reporting of de PDF-exportservice rendert met QuestPDF.

Web
→ Reporting/PDF-exportcontract
→ PracticePdfExportReader
→ PracticePdfExportModel
→ QuestPDF-rendering
→ tijdelijke downloadresponse
PDF-onderdeelBron
BestandsnaamcontextRun-snapshots en veilige formattering.
SamenvattingOpgeslagen runvelden.
Vraag-/antwoordtabelExerciseRunProgress + module-exportrepresentatie.
StatistiekenOpgeslagen runstatistieken.
DuplicaatmeldingRunrelatie in practice.
Footer/exportdatumExportmoment, niet runbron.

PDF-bestanden worden niet als permanente waarheid opgeslagen. Bij opnieuw downloaden wordt de PDF opnieuw opgebouwd uit databasegegevens, snapshots en module-exportrepresentaties voor zover beschikbaar.

10.23 Live meekijken

Live meekijken leest server-side opgeslagen voortgang uit Practice. LiveMonitoring is eigenaar van live sessie-audit en SignalR-weergave, maar niet van oefenvoortgang.

OnderdeelEigenaar
VraagvoortgangPractice
Actuele livevraagPractice, afgeleid uit runprogressie.
SignalR-transportLiveMonitoring / Web.
LiveViewAuditLiveMonitoring of volgens database-informatie vastgelegde eigenaar.
MeekijkautorisatieAuthorization + contextmodule.

Na ieder bevestigd antwoord maakt Practice de actuele voortgang beschikbaar. SignalR-updates mogen falen zonder de oefenvoortgang terug te draaien; de volgende read of reconnect gebruikt de database als bron.

Omdat Practice verwijst naar gebruikers, catalogusobjecten en modules buiten de eigen module, zijn cross-module verwijzingen standaard soft links met snapshots.

VerwijzingTechnische behandeling
StudentUserIdSoft link naar identity.
LevelIdSoft link naar catalog.
CategoryIdSoft link naar catalog.
ExerciseIdSoft link naar catalog.
ExerciseModuleIdSoft link naar catalog/modules metadata.
SourceSharedExerciseIdBinnen practice, harde FK mogelijk.
DuplicateOfRunIdBinnen practice, harde FK mogelijk met zorgvuldig delete behavior.
CreatedBy/ActorId bij test of deelactieSoft link + eventueel snapshot.

Snapshots zijn fallback en historische representatie. Zij worden niet gebruikt om autorisatie te verruimen.

10.25 Transaction boundaries

De transaction boundary wordt per workflow bepaald. Practice is verantwoordelijk voor atomaire bronmutaties binnen de eigen module. Cross-module stappen worden alleen in dezelfde functionele transactie geplaatst wanneer de gebruikersactie anders onvolledig of onbereikbaar wordt.

WorkflowKritieke mutatiesNaverwerking
Start nieuwe runCataloguscontext valideren, vragen genereren, run en progressie opslaan.Eventuele readmodel/cache-update.
Vraag beantwoordenVraagstatus, payload, totalen en voortgang opslaan.SignalR-update/live notificatie.
Run afrondenLaatste voortgang, totalen, statistieken en afrondstatus opslaan.Badge/readmodel, eventuele communicatie.
Delen van oefeningShared record en eventueel kritieke ingang voor ontvanger.Niet-kritieke badges of notificaties.
Docenttestrun opruimenTestdata verwijderen/markeren volgens beleid.Technische log/update.
PDF-exportGeen runmutatie; exportmodel lezen.Tijdelijk bestand opschonen via scheduler.

Bij twijfel wordt een stap als kritiek behandeld totdat het tegendeel per workflow is onderbouwd.

10.26 Concurrency en idempotentie

Oefenruns zijn gevoelig voor dubbele submits, browserrefreshes en reconnects. De backend moet herhaald aangeboden acties veilig verwerken.

SituatieTechnische maatregel
Dubbel antwoordsubmitControleer vraagstatus en accepteer geen tweede definitieve antwoordmutatie zonder expliciete regel.
Refresh na antwoordLees opgeslagen voortgang en toon passende volgende staat.
Afronden dubbel gestartAfronding idempotent maken of tweede poging veilig afwijzen.
Gelijktijdig live lezenAlleen read-only toegang; geen lock op oefenflow.
Reconnect tijdens oefeningDatabaseprogressie opnieuw uitlezen.
Scheduler cleanupAlleen testdata of tijdelijke data selecteren met veilige criteria.

Optimistic concurrency via rowversion/concurrency token is gewenst op run- en/of progressierecords waar dubbele mutaties risico geven.

10.27 Autorisatie en contextcontrole

Iedere command- en queryingang van Practice herhaalt server-side autorisatie- en contextcontrole.

ActieControle
Start runLeerlingcontext, niveau, categorie, oefening, modulebeschikbaarheid.
Hervat runEigen run, niet-afgerond, juiste actieve context, toegankelijke oefening.
Beantwoord vraagEigen actieve run, juiste vraagstatus, geen afgeronde run.
Bekijk resultaatGeautoriseerde afgeronde run.
Download PDFZelfde autorisatie als resultaatdetail/exportcontext.
Deel oefeningEigen afgeronde run, delen toegestaan, actieve vriendrelatie.
DocentgeschiedenisDocentcontext en eigen niveauautorisaties.
Ouder-/voogdgeschiedenisActieve GuardianStudent-relatie.
Live brondataGeautoriseerde live-meekijkcontext.

Routeparameters, browserstate, zichtbare knoppen en clientfilters zijn nooit autorisatiebewijs.

10.28 Logging en correlation

Practice moet alle kritieke runmutaties herleidbaar loggen zonder gevoelige payloads in technische logs op te nemen.

LogcontextVerplicht waar relevant
CorrelationIdKoppelt HTTP-request, workflow, job en domeinactie.
RunIdAlleen in technische logs; niet per se gebruikersgericht tonen.
ActorUserIdSoft link of geanonimiseerde context volgens privacyregels.
RoleContextLeerling, docent, ouder/voogd, beheerder of systeemjob.
ActionStart, submit answer, complete, share, export, cleanup.
ResultSuccess, validation failed, authorization denied, technical failure.
ErrorCodeGestandaardiseerde foutcode, geen ruwe exceptiontekst naar gebruiker.

Payloadinhoud, volledige antwoorden, tokens, cookies en credentials worden niet in technische logs opgenomen.

10.29 Security- en privacygrenzen

OnderwerpRegel
AntwoorddataAlleen tonen aan geautoriseerde contexten.
PayloadsGeen credentials/tokens; minimale noodzakelijke data.
SnapshotsPersoonsgegevens alleen waar functioneel noodzakelijk en anonimiseerbaar.
PDF-exportTijdelijk bestand; veilige naamgeving en cleanup.
TestdataGeen productiepersoonsdata in testfixtures.
Live meekijkenRead-only; geen mutaties door meekijkers.
Soft linksMogen geen autorisatie vervangen.

Bij accountanonimisering moeten runbrondata, snapshots en historische weergavewaarden volgens het privacyhoofdstuk worden behandeld. Historische functionele reconstructie mag blijven bestaan zonder actuele persoonsgegevens te tonen.

10.30 Readmodels binnen Practice

Readmodels staan onder Models/ReadModels en zijn module-eigen. Zij zijn geen aparte bron van waarheid.

Voorbeelden:

Models/
ReadModels/
StudentExerciseHistoryReadModel.cs
TeacherPracticeHistoryReadModel.cs
GuardianResultSummaryReadModel.cs
PracticeResultDetailReadModel.cs
PracticePdfExportModel.cs
PracticeLiveProgressReadModel.cs

Readmodels mogen query-optimalisaties bevatten, maar mogen geen autorisatiecontrole vervangen. De query-service voert of vraagt de vereiste server-side contextcontrole uit.

10.31 Publieke contracts

Andere modules en Web gebruiken alleen publieke contracts. Voorbeelden:

Contracts/
IPracticeRunCommandService.cs
IPracticeAnswerCommandService.cs
IPracticeHistoryReader.cs
IPracticeResultReader.cs
IPracticePdfExportReader.cs
IPracticeLiveProgressReader.cs
ISharedExerciseService.cs
Models/
StartRunRequest.cs
StartRunResult.cs
SubmitAnswerRequest.cs
PracticeResultDetailDto.cs
PracticePdfExportDto.cs

Interne services zoals statistiekberekening, payloadserialisatie of progressiemutatie blijven buiten Contracts.

10.32 Teststrategie

TesttypeDekking
Unit testsStatistiekberekening, payloadmapping, runstatusregels, Geen idee-logica.
Module integration testsPracticeDbContext, migrations, seeddata indien aanwezig, progressiewrites.
Contract testsPublieke Practice-contracts en DTO-gedrag.
Cross-module integration testsStartflow met Catalog/Authorization/ModuleHost, delen met Relationships/Communication.
Concurrency testsDubbele submits, dubbel afronden, reconnect-situaties.
Security testsToegang tot resultaat/PDF/livebron vanuit verkeerde context.
PDF-bronmodeltestsExportmodel bevat historische snapshots en correcte statistiekdata.
Scheduling testsTestrun cleanup en tijdelijke exportcleanup via Scheduling-contracts.

10.33 Implementatiechecklist

  • OefenHub.Practice heeft één PracticeDbContext en schema practice.
  • Entities, DbContext en implementatieclasses zijn standaard internal.
  • Publieke module-ingangen staan onder Contracts.
  • Cross-module verwijzingen naar identity, catalogus en modulemetadata zijn soft links met snapshots waar nodig.
  • Binnen practice mogen harde FK’s worden gebruikt voor eigen aggregate- en historyrelaties.
  • Uniforme totalen en statistieken staan relationeel beschikbaar.
  • Payloads zijn versieerbaar en bevatten geen credentials of tokens.
  • Antwoordverwerking en runafronding zijn server-side en transactioneel veilig.
  • Resultaten, geschiedenis, live meekijken en PDF-export lezen dezelfde historische runbron.
  • Docenttestruns zijn herkenbaar, niet regulier zichtbaar en opruimbaar.
  • Schedulingacties gebruiken publieke contracts en correlation-id’s.
  • Autorisatie wordt per command/query server-side herhaald.
  • Logs bevatten correlation en technische context, maar geen gevoelige payloadinhoud.

10.34 Implementatieverificaties

OnderwerpTe controleren keuze
StartmomentdefinitieDefinitief bepalen of StartedAtUtc bij runcreatie of eerste vraagtoonmoment wordt gezet.
PayloadopslagExacte JSON/base64-serialisatiestrategie en compressiebeleid bepalen.
Concurrency tokenVastleggen op welke run/progress-tabellen rowversion nodig is.
StatistiekafrondingAfrondingsregels, tijdseenheden en null/edge cases testen en documenteren.
ModuleversiecompatibiliteitMinimumbeleid bepalen voor oude payloads na modulemigraties.
PDF-exportmodelExacte scheiding tussen PracticePdfExportModel en QuestPDF-rendering bepalen.
Live progress readerBepalen welke progressievelden minimaal voor live meekijken nodig zijn.
SharedExercise-transactiePer deelworkflow bepalen of systeembericht kritieke ingang of naverwerking is.
Testrun cleanupExacte leeftijdscriteria en batchgrootte bepalen.
AnonimiseringDefinitieve anonimiseringswaarden voor persoonsgebonden snapshots afstemmen met privacyhoofdstuk.