Oefencatalogus, niveaus, categorieën, oefeningen en modules
8.1 Intentie
Dit hoofdstuk beschrijft de technische realisatie van de oefencatalogus binnen OefenHub. De oefencatalogus omvat niveaus, centrale categorieën, concrete oefeningen, catalogusmetadata van technische oefenmodules, moduleconfiguratie, statusbeheer, kopiëren, migraties en catalogushistorie.
De oefencatalogus is het domein waarin wordt bepaald welke oefeninhoud bestaat, binnen welke niveau- en categoriecontext deze beschikbaar kan zijn en welke technische module voor een oefening gebruikt wordt. De daadwerkelijke uitvoering van een oefening, voortgang, antwoorden, resultaten en geschiedenis horen niet bij de catalogus, maar bij het practice-domein.
8.2 Afbakening
De oefencatalogus beschrijft technische implementatiekeuzes voor bestaande functionele en datamodelafspraken. Nieuwe functionele eisen ontstaan niet in dit hoofdstuk. Wanneer implementatie aantoont dat een functionele regel ontbreekt of wijzigt, moet de wijziging worden teruggelegd naar Functioneel Ontwerp, Software Requirements Specification, schermdocumentatie, usecases en database-informatie voordat het Technisch Ontwerp daarop wordt aangepast.
| Onderwerp | Hoort in dit hoofdstuk | Hoort niet in dit hoofdstuk |
|---|---|---|
| Niveaus | Technische opslag, eigenaarschap, zichtbaarheid en collaborator-koppeling vanuit catalogusperspectief | Volledige docent-leerlingautorisatieflow |
| Categorieën | Centrale categorie-identiteit, koppeling aan niveaus, status, historie en migratie | Leerlingresultaten per categorie |
| Oefeningen | Concrete oefeningmetadata, status, modulekeuze, configuratiepayload en historie | Runvoortgang, antwoorden en scoreberekening |
| ExerciseModules | Catalogusregistratie van beschikbare technische modules | Implementatie van modulelogica zelf |
| Moduleconfiguratie | Opslag, validatie-aanroep en versiecontext | De interne module-DTechnisch Ontwerp als catalogusdatabaseontwerp |
| Migraties | Categorie- en modulemigratie als beheeractie met audit | Herschrijven van historische exercise runs |
| Historie | Catalogusgerichte historyrecords | Centrale generieke auditmodule |
8.3 Belangrijke inputbronnen
De detaildefinities van tabellen, kolommen, relaties en enumwaarden blijven primair onderdeel van de database-informatie. Dit hoofdstuk beschrijft hoe die informatie technisch wordt toegepast in de modulaire monoliet.
- Functioneel Ontwerp: oefencatalogus
- Database-informatie: docentstructuur en leerlingtoegang
- Software Requirements Specification: oefenmodule-requirements
- Technisch Ontwerp: architectuuroverzicht en solution-opbouw
- Technisch Ontwerp: applicatielagen, projectstructuur en dependency-richting
- Technisch Ontwerp: databaseontwerp, migraties, seeddata en constraints
- Technisch Ontwerp: oefenmodulecontract en dynamische module-integratie
8.4 Project, DbContext en schema
De oefencatalogus wordt technisch ondergebracht in het moduleproject OefenHub.Catalog.
| Technisch onderdeel | Keuze |
|---|---|
| Project | OefenHub.Catalog |
| DbContext | CatalogDbContext |
| Databaseschema | catalog |
| Schema-naamgeving | kleine letters |
| Tabellen en kolommen | PascalCase |
| Migrations | OefenHub.Catalog/Data/Migrations |
| EF-configuraties | OefenHub.Catalog/Data/Configurations |
| Entities | standaard internal |
| Publieke ingang | Contracts |
De regel blijft: één moduleproject heeft maximaal één eigen DbContext en daarmee één primair databaseschema. Voor de catalogus betekent dit dat catalogustabellen in het schema catalog staan. Verwijzingen naar andere modules worden standaard niet als harde cross-module foreign key gemodelleerd, tenzij daarvoor via een expliciet gemotiveerde uitzondering wordt vastgelegd.
8.5 Verantwoordelijkheid van OefenHub.Catalog
OefenHub.Catalog is eigenaar van de technische catalogusbrondata. De module publiceert alleen de contracten die andere modules nodig hebben om cataloguscontext te lezen, catalogusacties uit te voeren of modulemetadata op te vragen.
| Verantwoordelijkheid | Uitwerking |
|---|---|
| Niveaus beheren | Aanmaken, wijzigen, zichtbaarheid, eigenaarcontext en collaboratorcontext vanuit catalogusinhoud |
| Categorieën beheren | Centrale categorie-identiteit, koppeling aan niveaus, gebruiksimpact, status en historie |
| Oefeningen beheren | Metadata, status, modulekeuze, configuratiepayload, parentrelatie en history |
| Modulecatalogus beheren | Beschikbare technische modulemetadata en beschikbaarheidsstatus |
| Configuratie valideren | Via OefenHub.ExerciseModuleHost en OefenHub.Modules.Abstractions |
| Migraties ondersteunen | Categorie- en modulemigraties als expliciete beheeracties |
| Catalogusreaders aanbieden | Publieke query-services voor Web, Practice, Reporting en beheerflows |
OefenHub.Catalog is niet eigenaar van leerlingvoortgang, resultaten, live-meekijken, PDF-generatie, relatiebeheer of autorisatiebesluiten. Die domeinen gebruiken catalogusinformatie via publieke contracts en snapshots.
8.6 Relatie met andere modules
| Andere module | Relatie met catalogus | Technische grens |
|---|---|---|
OefenHub.Web | Toont catalogusoverzichten, configuratieschermen en beheeracties | Web gebruikt publieke cataloguscontracts, geen CatalogDbContext |
OefenHub.Authorization | Bepaalt of gebruiker catalogusobjecten mag zien of muteren | Autorisatiebesluit buiten catalogus, objectcontext via contract |
OefenHub.Relationships | Levert relatiecontext voor docent/docent en docent/leerling afhankelijkheden | Geen directe entity access vanuit catalogus |
OefenHub.Practice | Gebruikt cataloguscontext bij starten van runs en snapshots | Soft links en snapshots, geen directe catalogusentity in runlogica |
OefenHub.ExerciseModuleHost | Valideert en resolved technische modulecontracten | Catalogus bewaart modulekeuze en config; host voert modulelogica uit |
OefenHub.Reporting | Leest historische of actuele cataloguscontext voor exports | Via query-service of snapshotdata, geen directe DbContext-toegang |
OefenHub.Admin | Start zware beheeracties zoals migraties of centrale correcties | Via cataloguscommandservices |
OefenHub.Scheduling | Kan catalogusgerelateerde jobs triggeren | Alleen via publieke catalogusjobcontracts |
8.7 Catalogusobjecten op technisch niveau
Dit hoofdstuk dupliceert niet de volledige tabeldefinities uit de database-informatie. Wel wordt vastgelegd welke technische rol de belangrijkste catalogusobjecten hebben.
| Object | Technische rol | Eigenaarschap |
|---|---|---|
| Niveau | Onderwijscontext waar categorieën en oefeningen onder vallen | OefenHub.Catalog |
| Categorie | Centrale gedeelde categorie-identiteit met naam, kleur, icoon en status | OefenHub.Catalog |
| Niveau-categorie-koppeling | Maakt categorie beschikbaar binnen een niveaucontext | OefenHub.Catalog |
| Oefening | Concrete configureerbare oefening binnen niveau/categoriecontext | OefenHub.Catalog |
| ExerciseModule | Catalogusrecord voor een technische module | OefenHub.Catalog voor metadata, moduleproject voor uitvoering |
| ExerciseHistory | History voor oefeningmutaties | OefenHub.Catalog |
| CategoryHistory | History voor centrale categorie-identiteit | OefenHub.Catalog |
| CategoryMigration | Registratie van categorieomzetting | OefenHub.Catalog |
| ExerciseModuleMigration | Registratie van modulemigratie | OefenHub.Catalog, uitvoering via modulehost waar nodig |
8.8 Niveaus
Een niveau is een cataloguscontext waarbinnen categorieën en oefeningen beheerd en aangeboden kunnen worden. Technisch hoort het niveau bij het catalog schema. De eigenaar van een niveau wordt vastgelegd als verwijzing naar een gebruiker, maar de gebruikersentiteit zelf blijft eigendom van OefenHub.Identity.
8.8.1 Technische regels voor niveaus
| Regel | Technische uitwerking |
|---|---|
| Niveau heeft een eigenaar | Opslag als gebruikers-id soft link naar identitycontext, aangevuld met relevante snapshotvelden waar nodig |
| Niveau heeft zichtbaarheid | Waarde zoals open of privé volgens database-informatie en Functioneel Ontwerp en Software Requirements Specification |
| Niveau kan collaborators hebben | Collaboratorrecords horen bij catalogus als zij inhoudsbewerking op niveau mogelijk maken |
| Niveau-autorisatie voor leerlingen | Niet bronhoudend in catalogus wanneer het om leerlingtoegang gaat; autorisatie-/relatiecontext bepaalt toegang |
| Historische runs blijven leesbaar | Practice legt runcontext en snapshots vast; cataloguswijzigingen herschrijven runs niet |
8.8.2 Eigenaarschap en collaborators
Eigenaarschap en collaboratorrechten worden technisch behandeld als catalogusrechten op onderwijsinhoud. Zij geven geen automatische toegang tot leerlingresultaten, geschiedenis of live-meekijken. Die grenzen worden in autorisatie-, relatie- en practiceflows opnieuw server-side gecontroleerd.
Voorbeeld:
Docent A is eigenaar van niveau Groep 6 Rekenen.
Docent B is actieve collaborator op dat niveau.
Docent B mag oefeningen binnen dat niveau aanpassen.
Docent B ziet daardoor niet automatisch resultaten van leerlingen van docent A.
De catalogusmodule mag daarom wel vastleggen wie eigenaar/collaborator is voor inhoudsbeheer, maar mag deze relatie niet hergebruiken als resultaat- of live-meekijkautorisatie.
8.9 Centrale categorieën
Categorieën zijn centrale, gedeelde catalogusobjecten. Een categorie zoals Rekenen of Taal heeft één centrale identiteit. Docenten maken geen privé-categorievariant met eigen verborgen betekenis.
8.9.1 Technische regels voor categorieën
| Regel | Technische uitwerking |
|---|---|
| Categorie-identiteit is centraal | Naam, kleur en icoon staan op de centrale categorie |
| Categorieën zijn herbruikbaar | Koppeling aan meerdere niveaus/docentcontexten via cataloguskoppeltabellen |
| Docenten kunnen bestaande categorie koppelen | Via commandservice met autorisatiecontrole |
| Docenten kunnen nieuwe centrale categorie voorstellen/aanmaken | Binnen toegestane flow, met history en waarschuwing over gedeelde identiteit |
| Naam/kleur/icoon wijzigen is beheeractie wanneer categorie in gebruik is | Via cataloguscommand met reden en history |
| Deactiveren/uitfaseren vereist impactcontrole | Actieve koppelingen en oefeningen bepalen of dit mag |
8.9.2 Categoriezichtbaarheid voor leerlingen
De zichtbaarheid van een categorie in leerlingnavigatie wordt niet handmatig als losse leerlingvlag beheerd. Zij wordt afgeleid uit:
- actuele niveaucontext;
- actieve categorie-niveaukoppeling;
- minimaal één actieve, toegankelijke oefening binnen die categorie;
- server-side autorisatiecontext van de leerling.
Voor performance mag een afgeleide status of readmodel worden gebruikt, maar de bron blijft de catalogus- en autorisatiecontext. Een cache of readmodel mag autorisatie nooit verruimen.
8.10 Niveau-categorie-koppelingen
Een centrale categorie wordt pas bruikbaar in een niveau wanneer zij aan dat niveau is gekoppeld. Deze koppeling hoort technisch bij OefenHub.Catalog.
| Aspect | Technische keuze |
|---|---|
| Broncategorie | Hard FK binnen catalog, omdat categorie en koppeling dezelfde module delen |
| Bronniveau | Hard FK binnen catalog, omdat niveau en koppeling dezelfde module delen |
| Status | Actieve/inactieve koppeling volgens datamodelregels |
| Duplicaten | Unieke constraint op relevante combinatie van niveau en categorie |
| Historie | Via categorie-/catalogushistory waar functioneel relevant |
Binnen het catalogusdomein zijn harde foreign keys dus juist gewenst. De soft-link-regel geldt vooral over modulegrenzen heen.
8.11 Concrete oefeningen
Een oefening is een concrete configureerbare catalogusdefinitie binnen een niveau- en categoriecontext. De oefening bevat generieke metadata en een verwijzing naar precies één technisch modulecatalogusrecord.
8.11.1 Technische regels voor oefeningen
| Regel | Technische uitwerking |
|---|---|
| Oefening hoort bij niveau/categoriecontext | Hard FK binnen catalog naar de relevante catalogusobjecten |
| Oefening verwijst naar één ExerciseModule-record | Hard FK binnen catalog, omdat ExerciseModule als catalogusmetadata in hetzelfde schema staat |
| Oefening heeft generieke metadata | Naam, icoon, status en overige generieke velden volgens database-informatie |
| Oefening heeft moduleconfiguratie | Module-specifieke configuratiepayload generiek opgeslagen |
| Oefeningstatus bepaalt startbaarheid | IsActive of equivalente status volgens datamodel |
| Nieuwe oefening start in onderhoud | Niet zichtbaar/startbaar voor leerlingen totdat actief gemaakt |
| Oefening kan afgeleid zijn van andere oefening | Parentrelatie binnen catalogus wanneer bron nog catalogusobject is |
| Wijzigingen zijn historyplichtig | ExerciseHistory legt mutaties vast |
8.11.2 Oefeningstatus
De technische status van een oefening bepaalt of zij zichtbaar, startbaar of alleen beheerbaar/testbaar is. De exacte enum- of booleanvorm volgt uit de database-informatie. In de applicatielaag wordt status altijd via betekenisvolle domeinlogica gebruikt en niet verspreid als losse booleancontrole in Web-components.
| Statusbetekenis | Gedrag |
|---|---|
| In onderhoud | Beheerbaar door bevoegde docent/collaborator/beheerder, niet startbaar voor leerlingen |
| Actief | Beschikbaar binnen autorisatie- en niveaucontext |
| Inactief/verwijderd indien ondersteund | Niet beschikbaar voor nieuwe starts, historisch herleidbaar |
Wanneer een oefening niet actief is, mag een bestaande historische run leesbaar blijven via practice/resultaten, maar de oefening mag niet als nieuwe leerlingstart worden aangeboden.
8.12 ExerciseModules als catalogusmetadata
ExerciseModules is de catalogusregistratie van technische oefenmodules. Dit is niet hetzelfde als de concrete module-implementatie in een project zoals OefenHub.Modules.Arithmetic.Addition.
| Onderdeel | Betekenis |
|---|---|
| Catalogusrecord | Metadata over beschikbare technische module |
| Module key | Stabiele sleutel waarmee modulehost de implementatie kan resolven |
| Moduleversie | Versiecontext voor configuratie en backwards compatibility |
| Beschikbaarheid | Of module selecteerbaar, testbaar of uitgefaseerd is |
| Implementatieproject | Concrete code die het contract uit Modules.Abstractions implementeert |
Voorbeeld:
catalog.ExerciseModules
ModuleKey = Arithmetic.Addition
DisplayName = Optellen
Version = 1
src/OefenHub.Modules.Arithmetic.Addition
Implementeert het technische modulecontract.
De catalogus bewaart dus welke module gekozen is en met welke configuratie, maar voert de oefenlogica niet zelf uit.
8.13 Moduleconfiguratiepayload
De configuratie van een oefening is module-specifiek, maar de opslag ervan blijft generiek. De catalogusmodule bewaart de payload, terwijl de technische module verantwoordelijk is voor interpretatie, validatie en eventuele migratie.
| Aspect | Technische regel |
|---|---|
| Opslag | Generieke configuratiepayload op oefeningrecord of gekoppeld configuratierecord |
| Formaat | JSON/base64 of equivalent volgens database-informatie en modulecontract |
| Validatie | Via ExerciseModuleHost naar concrete modulevalidator |
| Versie | Payload bevat of is gekoppeld aan module-/configuratieversie |
| Mutatie | Alleen via cataloguscommandservice, niet rechtstreeks vanuit Web |
| History | Oude en nieuwe configuratie of veilige samenvatting in ExerciseHistory |
8.13.1 Validatieflow
Web configuratiescherm
→ Catalog commandservice
→ autorisatiecontrole
→ ExerciseModuleHost.Resolve(ModuleKey)
→ concrete module valideert configuratie
→ Catalog slaat geldige payload op
→ Catalog schrijft ExerciseHistory
De modulevalidator mag inhoudelijke moduleconfiguratieregels afdwingen. De catalogus blijft verantwoordelijk voor generieke regels zoals eigenaarschap, status, history, concurrency en opslag.
8.14 Configuratie-UI
De catalogusmodule levert niet zelf alle module-specifieke formulierlogica. De technische module levert via het modulecontract de informatie waarmee Web een configuratie-interface kan tonen.
| Onderdeel | Eigenaar |
|---|---|
| Generiek configuratiescherm | OefenHub.Web |
| Beschikbare modulemetadata | OefenHub.Catalog en ExerciseModuleHost |
| Configuratieschema/velden | Concrete technische module via abstractions |
| Validatie van modulevelden | Concrete technische module |
| Opslag van payload | OefenHub.Catalog |
Hiermee blijft Web generiek genoeg om meerdere modules te ondersteunen, zonder dat catalogus of Web de inhoudelijke regels van elke oefenmodule hoeft te kennen.
8.15 Kopiëren en afgeleide oefeningen
Wanneer een docent een bestaande open oefening of configuratie als uitgangspunt gebruikt, ontstaat een nieuwe zelfstandige catalogusoefening. De nieuwe oefening mag een parentverwijzing bevatten naar de bron, zodat afgeleide configuraties zichtbaar en analyseerbaar blijven.
| Regel | Technische uitwerking |
|---|---|
| Kopie is zelfstandig | Nieuwe oefening krijgt eigen record en eigen configuratiepayload |
| Bron blijft ongewijzigd | Wijzigingen in kopie beïnvloeden bron niet |
| Parentrelatie | Binnen catalogus hard te modelleren wanneer bron en kopie catalogusoefeningen zijn |
| History | Aanmaak via kopie krijgt expliciete COPY_FROM_PARENT-achtige historyactie |
| Leerlingruns | Bestaande runs blijven in practice en worden niet aangepast |
Dit verschilt van Maak deze oefening opnieuw na een afgeronde run. Dat is een practice-flow die een nieuwe run maakt op basis van historische runinhoud en valt niet onder cataloguskopiëren.
8.16 Catalogushistorie
Er komt geen centrale generieke auditmodule voor cataloguswijzigingen. Historie blijft domeinspecifiek binnen OefenHub.Catalog.
| Historytype | Doel |
|---|---|
| CategoryHistory | Wijzigingen in centrale categorie-identiteit, status en migratiecontext |
| ExerciseHistory | Wijzigingen aan oefeningmetadata, modulekeuze, configuratie en status |
| CategoryMigration | Registratie van broncategorie naar doelcategorie |
| ExerciseModuleMigration | Registratie van migratie van concrete oefeningen naar andere modulecontext |
Historyrecords moeten voldoende informatie bevatten voor beheeranalyse en reconstructie. Vrije technische payloads of persoonsgegevens worden daarbij vermeden wanneer een compacte, veilige samenvatting voldoende is.
8.17 Categoriemigratie
Een categoriemigratie is een expliciete beheeractie waarbij een broncategorie administratief wordt omgezet naar een bestaande doelcategorie. Deze actie kan meerdere niveaukoppelingen en oefeningen raken en moet daarom gecontroleerd, transactioneel en auditbaar worden uitgevoerd.
8.17.1 Technische flow
1. Beheerder opent broncategorie.
2. Catalog bepaalt gebruiksimpact.
3. Beheerder kiest bestaande actieve doelcategorie.
4. Catalog valideert dat bron en doel verschillend zijn.
5. Catalog detecteert conflictsituaties.
6. Beheerder geeft verplichte reden op.
7. Catalog voert kritieke catalogusmutaties atomair uit.
8. Catalog registreert CategoryMigration en history.
9. Eventuele communicatie/readmodels worden volgens workflowbesluit verwerkt.
8.17.2 Migratieregels
| Regel | Technische uitwerking |
|---|---|
| Doelcategorie bestaat al | Geen nieuwe categorie aanmaken tijdens migratie |
| Bron en doel verschillen | Validatie in commandservice en databasebescherming waar mogelijk |
| Doelcategorie is actief | Alleen geldige doelstatus toegestaan |
| Conflicten worden expliciet afgehandeld | Geen blinde dubbele records |
| Historische runs worden niet herschreven | Practice blijft bron voor historische uitvoering |
| Bron blijft herleidbaar | Bronrecord wordt niet hard verwijderd |
| Actie is auditbaar | Migrationrecord + CategoryHistory |
De kritieke catalogusmutaties horen in één functionele transactie. Als een conflict niet oplosbaar is of een kritieke stap faalt, wordt de migratie niet gedeeltelijk uitgevoerd.
8.18 Modulemigratie
Een modulemigratie wijzigt de technische modulecontext van bestaande concrete oefeningen. Dit is risicovoller dan een gewone configuratiewijziging, omdat modulepayloads, validatie, vraaggeneratie, rendering en PDF-output kunnen verschillen.
8.18.1 Scope van modulemigratie
| Scope | Betekenis |
|---|---|
| Proefmigratie | Analyseert impact zonder definitieve wijziging |
| Oefeningmigratie | Migreert één concrete oefening |
| Docent-/niveaugerichte migratie | Migreert geselecteerde oefeningen binnen beheercontext |
| Globale migratie | Alleen als expliciete beheeractie met zware validatie |
8.18.2 Technische regels
| Regel | Technische uitwerking |
|---|---|
| Historische runs blijven ongewijzigd | Practice-runpayloads en snapshots worden niet herschreven |
| Configuratie wordt gevalideerd | Doelmodule valideert of geconverteerde payload geldig is |
| Migratie is auditbaar | ExerciseModuleMigration + ExerciseHistory |
| Falen stopt kritieke mutaties | Geen halve modulewissel op catalogusoefening |
| Backwards compatibility blijft moduleverantwoordelijkheid | Zie modulecontract in hoofdstuk 09 |
Wanneer een modulemigratie alleen catalogusmetadata wijzigt maar bestaande payload niet kan worden geconverteerd, mag de migratie niet als geslaagd worden opgeslagen. Een beheerder moet dan een foutstatus of proefrapport krijgen, zonder dat de oefening half is omgezet.
8.19 Publieke cataloguscontracts
Andere modules mogen catalogusdata niet rechtstreeks via CatalogDbContext lezen of wijzigen. OefenHub.Catalog publiceert contracts voor de toegestane interacties.
| Contracttype | Voorbeeld | Doel |
|---|---|---|
| Commandservice | ICatalogExerciseCommandService | Oefeningen aanmaken, wijzigen, status wijzigen |
| Query-service | ICatalogExerciseReader | Oefeningcontext uitlezen voor Web of Practice |
| Modulemetadata-reader | IExerciseModuleCatalogReader | Beschikbare modules en modulekeys raadplegen |
| Migrationservice | ICatalogMigrationService | Categorie- en modulemigraties uitvoeren |
| Authorization context reader | ICatalogObjectContextReader | Objectcontext leveren aan autorisatiemodule |
Concrete namen zijn implementatiedetails, maar het principe is verplicht: cross-module toegang loopt via publieke cataloguscontracts.
8.20 Voorbeeld: oefening aanmaken
Web
→ ICatalogExerciseCommandService.CreateExerciseAsync
→ Authorization controleert docent/collaboratorcontext
→ Catalog controleert niveau en categorie
→ Catalog vraagt ExerciseModuleHost om modulemetadata/configuratieschema
→ Concrete module valideert configuratiepayload
→ Catalog slaat Exercise op in catalog schema
→ Catalog schrijft ExerciseHistory
De Web-laag krijgt geen Exercise entity terug, maar een contractmodel of resultaatmodel dat geschikt is voor UI-terugkoppeling.
8.21 Voorbeeld: leerling start een oefening
Web
→ Practice start-flow
→ Authorization controleert leerlingcontext
→ Practice vraagt Catalog om startbare oefeningcontext
→ Catalog retourneert contractmodel met ExerciseId, ModuleKey, configversie en snapshotwaarden
→ Practice vraagt ExerciseModuleHost om vraaggeneratie
→ Practice maakt ExerciseRun met soft links en snapshots
Catalogus blijft eigenaar van de oefendefinitie. Practice wordt eigenaar van de run, voortgang en historische snapshotcontext.
8.22 Soft links en snapshots vanuit practice
Wanneer OefenHub.Practice een run start, gebruikt zij catalogusinformatie om runcontext en snapshots vast te leggen. De run mag verwijzen naar catalogusobjecten, maar historische leesbaarheid mag niet afhankelijk zijn van actuele catalogusnamen of toekomstige migraties.
| Practice-veld | Technische bedoeling |
|---|---|
| LevelId | Soft link naar catalogusniveau |
| CategoryId | Soft link naar cataloguscategorie of historische categoriecontext |
| ExerciseId | Soft link naar catalogusoefening |
| ExerciseModuleId/ModuleKey | Soft link of snapshot naar modulecataloguscontext |
| LevelNameSnapshot | Historische weergavenaam |
| CategoryNameSnapshot | Historische weergavenaam |
| ExerciseNameSnapshot | Historische weergavenaam |
| ModuleKeySnapshot | Herleidbaarheid van technische module |
Exacte velden blijven onderdeel van database-informatie. Het Technisch Ontwerp-principe is dat practice niet afhankelijk mag worden van actuele catalogusrecords om historische resultaten te tonen.
8.23 Concurrency en wijzigingsconflicten
Catalogusobjecten worden door docenten, collaborators en beheerders gewijzigd. Daarom moet de catalogusmodule rekening houden met gelijktijdige wijzigingen.
| Situatie | Technische aanpak |
|---|---|
| Twee gebruikers wijzigen dezelfde oefening | Optimistic concurrency of expliciete versiecontrole |
| Status wijzigt tijdens configuratie | Opslaan faalt veilig of vereist herladen |
| Module wordt uitgefaseerd tijdens oefeningaanmaak | Commandservice valideert modulebeschikbaarheid opnieuw |
| Categorie wordt gemigreerd tijdens bewerking | Commandservice valideert actuele categoriecontext opnieuw |
| Collaboratorrechten wijzigen tijdens bewerking | Autorisatiecontrole wordt bij opslaan opnieuw uitgevoerd |
Web mag zichtbare knoppen tonen op basis van eerder opgehaalde context, maar iedere mutatie herhaalt server-side de actuele autorisatie- en objectstatuscontrole.
8.24 Delete behavior en historische leesbaarheid
Catalogusobjecten die historisch relevant zijn, worden niet hard verwijderd zolang zij door practice, reporting, history of migratieanalyse nodig kunnen zijn.
| Object | Voorkeursgedrag |
|---|---|
| Centrale categorie | Soft delete/deactiveren/uitfaseren volgens domeinstatus |
| Niveau | Inactief/historisch maken wanneer nodig |
| Oefening | Inactief of verwijderd markeren, niet hard verwijderen bij historische runs |
| ExerciseModule-record | Uitfaseren of niet-selecteerbaar maken, niet blind verwijderen |
| History/migrationrecords | Bewaren volgens retentie- en auditbeleid |
Hard delete is alleen toegestaan voor records zonder historische, juridische, audit- of functionele afhankelijkheid en moet in database-informatie en Technisch Ontwerp expliciet zijn toegestaan.
8.25 Seeddata en modulemetadata
Catalogusseeddata moet module-eigen, idempotent en niet blind overschrijvend zijn.
| Seedtype | Voorbeeld | Gedrag |
|---|---|---|
| Modulemetadata | Beschikbare technische modules | Idempotent op ModuleKey en versiecontext |
| Basiswaardelijsten | Statussen of vaste codes indien in catalogus nodig | Idempotent, niet afhankelijk van weergavetaal waar mogelijk |
| Ontwikkeldata | Voorbeeldniveaus/oefeningen | Alleen development/testomgeving |
| Beheerbare content | Niet automatisch overschrijven na beheerwijziging | Alleen initieel of via expliciete migratieactie |
Concrete technische moduleprojecten registreren hun descriptor via ExerciseModuleHost. De catalogus mag modulemetadata synchroniseren of valideren, maar mag module-implementaties niet hard coderen.
8.26 Beveiliging en autorisatie
Catalogusmutaties zijn autorisatiegevoelig. De catalogusmodule voert geen autorisatie uit op basis van clientstate, zichtbare knoppen of routeparameters. Iedere commandservice ontvangt een server-side bepaalde actor- en rolcontext.
| Actie | Minimale controle |
|---|---|
| Niveau aanmaken | Actieve docent- of beheercontext |
| Categorie koppelen | Rechten op het gekozen niveau |
| Nieuwe categorie aanmaken | Toegestane rolcontext en validatie op duplicaat/semantische overlap waar ondersteund |
| Oefening aanmaken/wijzigen | Eigenaar, actieve collaborator of beheercontext |
| Oefening activeren | Geldige configuratie en bevoegde rolcontext |
| Categoriemigratie | Beheercontext en verplichte reden |
| Modulemigratie | Beheercontext of expliciet ondersteunde supportflow |
Bij geweigerde toegang wordt geen gedeeltelijke catalogusdata teruggegeven die buiten de actuele rolcontext valt. Technische logs gebruiken correlation-id en lekken geen gevoelige payloadinhoud.
8.27 Logging en correlation
Catalogusacties moeten herleidbaar zijn, vooral bij migraties, statuswijzigingen en configuratiewijzigingen.
| Gebeurtenis | Logging/historie |
|---|---|
| Oefening aangemaakt | ExerciseHistory + technische log op informatieniveau |
| Configuratie gewijzigd | ExerciseHistory met veilige oude/nieuwe waarde of samenvatting |
| Oefening geactiveerd/gedeactiveerd | ExerciseHistory |
| Categorie-identiteit gewijzigd | CategoryHistory met reden |
| Categoriemigratie uitgevoerd | CategoryMigration + CategoryHistory + correlation-id |
| Modulemigratie uitgevoerd | ExerciseModuleMigration + ExerciseHistory + correlation-id |
| Validatiefout moduleconfiguratie | Functionele foutmelding, technische log zonder volledige gevoelige payload |
Cross-module acties moeten dezelfde correlation-id doorgeven aan betrokken modules en eventuele schedulingjobs.
8.28 Performance en caching
Catalogusdata wordt veel gelezen door navigatie, frontpages, oefeningstartpagina’s en beheerinterfaces. Toch mag caching autorisatie nooit vervangen.
| Data | Caching/readmodelbeleid |
|---|---|
| Beschikbare categorieën voor leerling | Cache/readmodel toegestaan, server-side context blijft leidend |
| Modulemetadata | Cachebaar zolang invalidatie bij modulebeschikbaarheid is geregeld |
| Beheerimpacttellingen | Readmodel of queryoptimalisatie toegestaan |
| Oefeningconfiguratie | Alleen cachebaar met versiecontrole en invalidatie |
| Migratie-impact | Bij voorkeur live of expliciet berekend vlak vóór uitvoeren |
Wanneer performance-optimalisatie nodig is, wordt eerst bepaald of het gaat om brondata, readmodeldata of UI-compositie. Materialisatie hoort bij de eigenaar van de data of bij een expliciet vastgelegd readmodel.
8.29 Teststrategie voor catalogus
OefenHub.Catalog krijgt een eigen testproject onder tests.
tests/OefenHub.Catalog.Tests
| Testtype | Dekking |
|---|---|
| Unit tests | Statusovergangen, validatie, historyregels, conflictregels |
| Module integration tests | CatalogDbContext, migrations, constraints, seeddata |
| Contract tests | Publieke cataloguscontracts voor Practice, Web en Admin |
| Migration tests | Categorie- en modulemigraties inclusief rollback bij fouten |
| Authorization tests | Mutaties met eigenaar, collaborator, beheerder en geweigerde context |
| Concurrency tests | Gelijktijdige configuratie- en statuswijzigingen |
| Snapshot integration tests | Startflow met Practice legt juiste cataloguscontext vast |
Architecture tests moeten bewaken dat andere modules geen directe toegang nemen tot OefenHub.Catalog/Data/Entities of CatalogDbContext.
8.30 Implementatiechecklist
Bij implementatie of wijziging van catalogusfunctionaliteit moet minimaal gecontroleerd worden:
- Is de wijziging bronhoudend voor catalogusdata of hoort zij bij Practice, Reporting, Web of Admin?
- Staat de tabel in het schema
catalogwanneerOefenHub.Catalogeigenaar is? - Is de DbContext-wijziging opgenomen in
CatalogDbContexten bijbehorende migration? - Zijn cross-module verwijzingen soft links of gemotiveerde uitzonderingen?
- Zijn historyrecords toegevoegd voor beheer- en configuratiewijzigingen?
- Wordt moduleconfiguratie gevalideerd via
ExerciseModuleHost? - Worden Web en andere modules alleen via publieke cataloguscontracts bediend?
- Wordt bij iedere mutatie server-side autorisatie opnieuw gecontroleerd?
- Zijn snapshotconsequenties voor Practice en Reporting beoordeeld?
- Zijn seeddata en modulemetadata idempotent?
- Zijn migration- en concurrencytests toegevoegd waar nodig?
- Zijn open punten vastgelegd in het besluitenregister van het Technisch Ontwerp wanneer een keuze nog niet definitief is?
8.31 Implementatieverificaties
| Punt | Toelichting | Plaats voor besluit |
|---|---|---|
| Exacte catalogusstatuswaarden | Boolean IsActive of expliciete statusenum per object moet consistent blijven met database-informatie | database-informatie en besluitenregister van het Technisch Ontwerp |
| Modulemetadata-synchronisatie | Bepalen of modulemetadata bij startup, deployment of expliciete beheeractie wordt gesynchroniseerd | Hoofdstuk 09 en 21 |
| Configuratiepayloadformaat | Definitief vastleggen hoe JSON/base64, versie en validatiesamenvatting worden opgeslagen | Hoofdstuk 07 en 09 |
| Migratieconflictstrategie | Exacte conflictregels bij categoriemigratie en modulemigratie uitwerken per datamodel | Database-informatie en schermdocumentatie |
| Cache/readmodelstrategie | Bepalen welke catalogusoverzichten materialisatie nodig hebben | Hoofdstuk 17 |
| Concurrencymechanisme | Keuze voor rowversion, concurrency token of andere EF Core-strategie | Hoofdstuk 07 |