3. Docentstructuur en leerlingtoegang
Deze sectie beschrijft de door docenten ingerichte niveaustructuur, samenwerking tussen docenten op niveau-laag, eigendomsoverdracht van niveaus en de autorisatie van leerlingen tot die structuur.
3.1 TeacherLevels
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherLevels | Docentstructuur | Door een docent aangemaakte niveaus met vrije naamgeving en één actuele eigenaar. | Users, TeacherLevelCollaborators, TeacherLevelOwnershipTransfers, TeacherLevelCategories, TeacherStudentLevelAccess |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| OwnerTeacherUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Docent die op dit moment eigenaar is van het niveau. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| Name | nvarchar(150) | - | N | N | - | N | N | J | Vrije naamgeving, bijvoorbeeld Groep 7 of Klas 2026 - Groep 7. |
| Description | nvarchar(500) | null | N | N | - | N | J | N | Optionele beschrijving. |
| IsActive | bit | 1 | N | N | - | N | N | J | Maakt niveau actief of historisch. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
| UpdatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Laatste wijziging. |
Validaties / constraints
- Naam hoeft niet globaal uniek te zijn.
Business rules
- Een niveau behoort altijd aan precies één docent als actuele eigenaar.
- Een niveau kan daarnaast één of meerdere actieve collaborators hebben.
- Een leerling kan pas toegang krijgen tot een niveau via een actieve docent-leerlingrelatie.
- Wanneer de actuele eigenaar van een niveau het account verwijdert of geanonimiseerd wordt, mag geen verweesd actief niveau overblijven. Zonder actieve collaborators wordt het niveau gedeactiveerd; met actieve collaborators moet eigenaarschap eerst gecontroleerd worden overgedragen.
Lifecycle / gedrag
- Niveaus worden gedeactiveerd in plaats van verwijderd wanneer historie behouden moet blijven.
- Eigenaarschap kan worden overgedragen; de actuele eigenaar wordt dan in deze tabel aangepast.
- Bij accountverwijdering van de eigenaar geldt aanvullend: bij precies één actieve collaborator mag automatische overdracht plaatsvinden; bij meerdere actieve collaborators moet de opvolgende eigenaar expliciet door de eigenaar of een beheerder worden gekozen.
Designkeuzes
- Niveau is bewust docent-specifiek en niet leerling-specifiek.
- Samenwerking en eigendomsoverdracht worden in aparte tabellen vastgelegd zodat eigenaarschap, samenwerking en leerlingtoegang logisch gescheiden blijven.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: niet van toepassing binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
OwnerTeacherUserIdis een soft link naarUsers.Id. De eigenaar wordt applicatief gevalideerd via het identity-/autorisatiedomein; er wordt geen harde database-FK naarUsersgelegd.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.2 TeacherLevelCollaborators
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherLevelCollaborators | Docentstructuur | Actieve samenwerking van extra docenten op een bestaand niveau. | TeacherLevels, Users, UserRelationships |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| TeacherLevelId | uniqueidentifier | - | N | J | TeacherLevels.Id | N | N | J | Niveau waarop wordt samengewerkt. |
| CollaboratorTeacherUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Docent die als collaborator op dit niveau mag meewerken. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| GrantedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Moment waarop de samenwerking is toegekend. |
| GrantedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | N | Gebruiker die de samenwerking heeft toegekend. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| RevokedAtUtc | datetime2 | null | N | N | - | N | J | N | Moment waarop de samenwerking is ingetrokken. |
| RevokedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | N | Gebruiker die de samenwerking heeft ingetrokken. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| IsActive | bit | 1 | N | N | - | N | N | J | Actieve samenwerking op dit niveau. |
Validaties / constraints
- Actieve combinatie TeacherLevelId + CollaboratorTeacherUserId is uniek.
- De actuele eigenaar van het niveau mag niet als collaborator van hetzelfde niveau voorkomen.
Business rules
- Een collaborator mag alleen worden gekoppeld wanneer tussen eigenaar en collaborator een actieve TeacherTeacher-relatie bestaat.
- De actieve TeacherTeacher-relatie is een voorwaarde voor het aangaan van nieuwe collaboration, maar niet de technische drager van bestaand niveau-bewerkrecht. Een reeds bestaand collaboratorrecord blijft dus zelfstandig geldig totdat dit expliciet op niveau-laag wordt ingetrokken.
- Een record in deze tabel geeft bewerkrecht op het betreffende niveau.
- In beheercontext worden eerst de al actieve collaborators getoond, alfabetisch op naam, en daaronder de nog beschikbare docenten, eveneens alfabetisch op naam.
- Een beheerder mag in uitzonderingssituaties een nieuwe collaborator forceren; daarbij wordt zo nodig eerst een docent-docentrelatie vastgelegd en daarna direct een collaboratorrecord aangemaakt zonder acceptatiestap door de nieuwe docent.
Lifecycle / gedrag
- Samenwerking op niveau wordt ingetrokken via RevokedAtUtc/RevokedByUserId en niet hard verwijderd.
- Bij eigendomsoverdracht blijft de oude eigenaar standaard collaborator.
- Het beëindigen van een algemene TeacherTeacher-relatie trekt bestaande collaboratorrecords niet automatisch in. Intrekking van niveau-samenwerking vereist een expliciete actie op het collaboratorrecord of een afgeleide beheerflow zoals eigendomsoverdracht of accountverwijdering.
- Wanneer een beheerder een collaborator forceert, moet deze actie auditbaar zijn en ontvangt de oorspronkelijk geselecteerde docent hierover een systeembericht.
Designkeuzes
- Samenwerking op niveau is bewust los gemodelleerd van de algemene docent-docent relatie. Daardoor geeft de relatie alleen het algemene samenwerkingsverband aan en bepaalt deze tabel de concrete samenwerking op niveau-laag.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
TeacherLevelId -> TeacherLevels.Id.
Functionele / logische verwijzingen zonder harde FK
CollaboratorTeacherUserId,GrantedByUserIdenRevokedByUserIdzijn soft links naarUsers.Id. De gebruikerscontext wordt applicatief gevalideerd via identity-/autorisatiecontracten; er worden geen harde database-FK's naarUsersgelegd.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.3 TeacherLevelOwnershipTransfers
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherLevelOwnershipTransfers | Docentstructuur | Historie van overdracht van eigenaarschap van een niveau tussen docenten. | TeacherLevels, TeacherLevelCollaborators, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| TeacherLevelId | uniqueidentifier | - | N | J | TeacherLevels.Id | N | N | J | Niveau waarvan eigenaarschap is overgedragen. |
| PreviousOwnerTeacherUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Docent die vóór de overdracht eigenaar was. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| NewOwnerTeacherUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Docent die na de overdracht eigenaar is geworden. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| TransferredAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Moment van overdracht. |
| TransferredByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | N | Gebruiker die de overdracht heeft uitgevoerd; dit is de huidige eigenaar of een beheerder. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| Reason | nvarchar(1000) | - | N | N | - | N | N | N | Verplichte toelichting op de overdracht. |
Validaties / constraints
- NewOwnerTeacherUserId moet op het moment van overdracht een actieve collaborator van het niveau zijn.
- PreviousOwnerTeacherUserId en NewOwnerTeacherUserId mogen niet gelijk zijn.
Business rules
- Eigenaarschap kan alleen worden overgedragen door de huidige eigenaar of een beheerder. Bij iedere overdracht is een reden verplicht.
- Na overdracht wordt de nieuwe eigenaar actuele eigenaar van het niveau.
- De oude eigenaar blijft standaard collaborator.
- In de beheerinterface mag eigenaarschap uitsluitend worden overgezet naar een reeds actieve collaborator; als er nog geen collaborator beschikbaar is, moet eerst collaboration op dat niveau worden toegevoegd.
- Bij accountverwijdering van de actuele eigenaar blijft dezelfde regel gelden: automatische of geforceerde overdracht mag alleen naar een docent die op dat moment al actieve collaborator van het niveau is.
Lifecycle / gedrag
- Iedere overdracht levert één historisch record op en overschrijft niet eerdere overdrachten.
- De nieuwe eigenaar hoort daarna niet meer als actieve collaborator op hetzelfde niveau voor te komen.
Designkeuzes
- Eigendomsoverdracht is apart gemodelleerd om volledige audittrail, onderbouwing en latere reconstructie van eigenaarschap te behouden.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
TeacherLevelId -> TeacherLevels.Id.
Functionele / logische verwijzingen zonder harde FK
PreviousOwnerTeacherUserId,NewOwnerTeacherUserIdenTransferredByUserIdzijn soft links naarUsers.Id. De overdrachtshistorie blijft daardoor domein-eigen vastgelegd zonder harde database-afhankelijkheid op het identity-domein.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.4 Categories
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| Categories | Centrale onderwijsinhoud | Centrale categorie-identiteit die door meerdere docentniveaus kan worden hergebruikt. Bevat naam, visuele presentatie en auditinformatie. | TeacherLevelCategories, CategoryHistory |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| Name | nvarchar(150) | - | N | N | - | J | N | J | Centrale categorienaam. Wordt gedeeld gebruikt over docentniveaus heen. |
| IconKey | nvarchar(100) | - | N | N | - | N | N | N | Vaste sleutel naar de gekozen categorie-icoonset. |
| ColorHex | nvarchar(7) | - | N | N | - | N | N | N | Centrale kleurwaarde in hex-formaat, bijvoorbeeld #4A90E2. |
| CreatedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Gebruiker die de categorie oorspronkelijk heeft aangemaakt. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
| UpdatedByUserId | uniqueidentifier | - | N | N | Users.Id | N | J | N | Gebruiker die de laatste inhoudelijke wijziging heeft uitgevoerd. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| UpdatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Laatste wijzigmoment. |
| IsDeleted | bit | 0 | N | N | - | N | N | J | Soft delete voor beheerbare opschoning zonder historieverlies. |
Validaties / constraints
- Name is uniek binnen actieve records.
- IconKey en ColorHex zijn verplicht.
- ColorHex moet een geldig hex-kleurformaat hebben.
Business rules
- Categories bevat de centrale identiteit van een categorie: naam, kleur en icoon.
- Deze identiteit kan door meerdere docenten en docentniveaus worden hergebruikt en mag daarom niet vrij per docentcontext afwijken.
- Een beheerder kan een categorie hernoemen, deactiveren, soft deleten of migreren naar een andere bestaande centrale categorie wanneer dubbele of semantisch overlappende categorieën zijn ontstaan.
Lifecycle / gedrag
- Beheer kan naam, kleur en icoon wijzigen wanneer daar een geldige reden voor is.
- Dergelijke wijzigingen moeten herleidbaar zijn omdat ze meerdere docentcontexten en leerlingweergaven kunnen beïnvloeden.
- Na migratie blijft een broncategorie historisch bestaan, maar mag deze niet langer nieuw gekozen of gekoppeld worden door docenten.
Designkeuzes
- Kleur en icoon worden bewust centraal op Categories vastgelegd. Hiermee blijft voor alle gebruikers die dezelfde categorie gebruiken één consistente semantische en visuele identiteit behouden.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: niet van toepassing binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
CreatedByUserIdenUpdatedByUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.IconKeyfunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.ColorHexbevat een inhoudelijke of technische contextwaarde en is daarom logische samenhang zonder harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.5 CategoryHistory
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| CategoryHistory | Audit en historie | Vastleggen van beheerwijzigingen op centrale categorieën zodat altijd herleidbaar is wie, wanneer, wat en waarom heeft aangepast. | Categories, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| CategoryId | uniqueidentifier | - | N | J | Categories.Id | N | N | J | Categorie waarop de wijziging betrekking heeft. |
| ActionType | nvarchar(50) | - | N | N | - | N | N | J | Gesloten domein, bijvoorbeeld CREATE, UPDATE_NAME, UPDATE_COLOR, UPDATE_ICON, SOFT_DELETE, RESTORE. |
| OldValue | nvarchar(max) | - | N | N | - | N | J | N | Vorige waarde voor de betreffende wijziging, indien van toepassing. |
| NewValue | nvarchar(max) | - | N | N | - | N | J | N | Nieuwe waarde voor de betreffende wijziging, indien van toepassing. |
| Reason | nvarchar(500) | - | N | N | - | N | N | N | Verplichte reden van de wijziging. |
| ChangedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Beheerder die de wijziging heeft uitgevoerd. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| ChangedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Moment waarop de wijziging is vastgelegd. |
Validaties / constraints
- ActionType gebruikt een gesloten vaste waardeset in code en database.
- ChangedByUserId en ChangedAtUtc zijn verplicht.
- Per inhoudelijke wijziging wordt een afzonderlijk historyrecord vastgelegd.
Business rules
- Alleen beheerwijzigingen op centrale categorie-identiteit worden hier vastgelegd.
- Door specifieke ActionType-waarden te gebruiken is FieldName niet nodig; OldValue en NewValue zijn voldoende voor reconstructie.
- Bij een migratie moet CategoryHistory zowel op de broncategorie als op de doelcategorie een entry ontvangen, zodat later in een eventuele UI zichtbaar blijft dat een beheerder een migratie heeft uitgevoerd.
Lifecycle / gedrag
- CategoryHistory is append-only en wordt niet aangepast of verwijderd door reguliere functionele processen.
- De tabel ondersteunt reconstructie, impactanalyse en navraag wanneer een categorie later wordt aangepast, gedeactiveerd of gemigreerd.
Designkeuzes
- Er is bewust geen aparte CategoryActionTypes-tabel opgenomen.
- De set actiecodes is klein, stabiel en alleen wijzigbaar bij code- of datamodelaanpassingen.
- Voor migraties worden expliciete bron- en doelactiecodes gebruikt, zodat zonder extra JSON-detailveld duidelijk blijft welke zijde van de migratie in een historyregel wordt beschreven.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
CategoryId -> Categories.Id.
Functionele / logische verwijzingen zonder harde FK
ChangedByUserIdis een soft link naarUsers.Id; er wordt geen harde database-FK naar het identity-domein gelegd.ActionTypefunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.5.1 CategoryMigrations
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| CategoryMigrations | Audit en historie | Registreert migraties van een broncategorie naar een bestaande doelcategorie, inclusief actor, moment en reden. | Categories, Users, CategoryHistory, TeacherLevelCategories, TeacherLevelCategoryExercises |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| SourceCategoryId | uniqueidentifier | - | N | J | Categories.Id | N | N | J | Broncategorie die gemigreerd wordt. |
| TargetCategoryId | uniqueidentifier | - | N | J | Categories.Id | N | N | J | Bestaande doelcategorie waarnaar gemigreerd wordt. |
| MigratedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Beheerder die de migratie uitvoerde. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| MigratedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Moment waarop de migratie administratief is vastgelegd. |
| Reason | nvarchar(1000) | - | N | N | - | N | N | N | Verplichte reden van de migratie. |
Validaties / constraints
- SourceCategoryId en TargetCategoryId moeten verschillend zijn en beide naar actieve of historisch bekende categorieën verwijzen.
- Een broncategorie mag niet meerdere keren naar verschillende doelcategorieën tegelijk gemigreerd worden.
Business rules
- Een categoriemigratie zet alle relevante koppelingen naar docentniveaus en de onderliggende oefenkoppelingen van de broncategorie over naar de doelcategorie. Daarbij moet expliciet gecontroleerd worden op reeds bestaande doelkoppelingen en reeds bestaande oefenkoppelingen onder de doelcategorie; dubbele records mogen niet blind worden aangemaakt.
- Bestaande historische ExerciseRuns worden door een categoriemigratie niet aangepast en behouden hun oorspronkelijke CategoryId-context.
Lifecycle / gedrag
- Na een succesvolle migratie blijft de broncategorie historisch bestaan, maar nieuwe koppelingen naar die broncategorie zijn niet meer toegestaan.
- De migratie zelf wordt append-only vastgelegd en niet aangepast of verwijderd door reguliere processen.
- Dit geldt ook voor runs die later vanuit geschiedenis gedeeld worden.
Designkeuzes
- Een aparte migratietabel houdt bron, doel, actor en reden compact en eenduidig vast, terwijl CategoryHistory leesbare historyregels per bron- en doelcategorie blijft bieden.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
SourceCategoryId -> Categories.Id;TargetCategoryId -> Categories.Id.
Functionele / logische verwijzingen zonder harde FK
MigratedByUserIdis een soft link naarUsers.Id; er wordt geen harde database-FK naar het identity-domein gelegd.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.6 TeacherLevelCategories
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherLevelCategories | Docentstructuur | Koppeling van centrale categorieën aan een docentniveau. | TeacherLevels, Categories |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| TeacherLevelId | uniqueidentifier | - | N | J | TeacherLevels.Id | N | N | J | Bepaalt bij welk docentniveau de categorie hoort. |
| CategoryId | uniqueidentifier | - | N | J | Categories.Id | N | N | J | Geselecteerde categorie uit het centrale model. |
| SortOrder | int | 0 | N | N | - | N | N | N | Volgorde binnen het niveau. |
| IsActive | bit | 1 | N | N | - | N | N | J | Performancevlag voor leerlingquery's. Wordt automatisch bijgewerkt op basis van actieve onderliggende items binnen dit docentniveau; docenten wijzigen deze waarde niet handmatig. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
Validaties / constraints
- Actieve combinatie TeacherLevelId + CategoryId is uniek.
- IsActive wordt niet handmatig door docenten aangepast maar automatisch afgeleid uit de actuele onderliggende niveau-items.
Business rules
- Een docent kan per niveau één of meerdere categorieën opnemen.
- SortOrder is puur presentatielogica.
- IsActive fungeert als afgeleide operationele vlag en staat op true zolang binnen deze niveau-categorie minimaal één relevante onderliggende oefenkoppeling of oefening actief is.
Lifecycle / gedrag
- Delen van de structuur worden gedeactiveerd in plaats van verwijderd wanneer resultatenhistorie behouden moet blijven.
- Als de laatste relevante actieve onderliggende inhoud binnen een docentniveau vervalt, kan de koppeling automatisch op inactief worden gezet.
Designkeuzes
- TeacherLevelCategories bevat alleen de koppeling tussen docentniveau en centrale categorie-identiteit plus contextspecifieke ordening en actieve status.
- Naam, kleur en icoon horen bewust bij Categories en niet bij deze koppeltabel.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: TeacherLevelId -> TeacherLevels.Id; CategoryId -> Categories.Id.
Functionele / logische verwijzingen zonder harde FK
- Buiten de expliciete foreign keys bevat deze tabel geen afzonderlijke functionele verwijzingen die als harde foreign key gemodelleerd hoeven te worden; overige velden zijn inhouds-, status- of auditwaarden.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.7 TeacherLevelCategoryExercises
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherLevelCategoryExercises | Docentstructuur | Concrete oefeningen binnen een docentniveau-categorie. | TeacherLevelCategories, Exercises |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| TeacherLevelCategoryId | uniqueidentifier | - | N | J | TeacherLevelCategories.Id | N | N | J | Bepaalt binnen welke niveau-categorie het item valt. |
| ExerciseId | uniqueidentifier | - | N | J | Exercises.Id | N | N | J | Verwijzing naar de concrete oefening die binnen deze categorie beschikbaar wordt gemaakt. |
| SortOrder | int | 0 | N | N | - | N | N | N | Volgorde binnen de categorie. |
| IsActive | bit | 1 | N | N | - | N | N | J | Maakt item actief of historisch. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
Validaties / constraints
- ExerciseId is verplicht.
- Eventuele aanvullende uniqueness-regels moeten voorkomen dat dezelfde oefening dubbel actief wordt gekoppeld binnen exact dezelfde niveau-categorie-context.
Business rules
- Een docent kan binnen een categorie één of meerdere oefeningen koppelen.
- Een extra groeperingslaag onder oefeningen wordt op dit moment niet toegepast.
- De koppeltabel bepaalt binnen welke niveau-categorie een concrete oefening beschikbaar is en in welke volgorde deze wordt getoond.
Lifecycle / gedrag
- Koppelingen worden gedeactiveerd in plaats van verwijderd wanneer gekoppelde resultaten of historie behouden moeten blijven.
- Een inactieve koppeling haalt de oefening uit de zichtbare niveau-categorie zonder onderliggende historie te verliezen.
Designkeuzes
- De koppeltabel tussen niveau-categorie en oefening houdt structuur, volgorde en contextuele activatie los van de oefening zelf. Daardoor kan de oefening een eigen lifecycle en audittrail houden, terwijl de plaatsing binnen een niveau afzonderlijk bestuurbaar blijft.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: TeacherLevelCategoryId -> TeacherLevelCategories.Id; ExerciseId -> Exercises.Id.
Functionele / logische verwijzingen zonder harde FK
- Buiten de expliciete foreign keys bevat deze tabel geen afzonderlijke functionele verwijzingen die als harde foreign key gemodelleerd hoeven te worden; overige velden zijn inhouds-, status- of auditwaarden.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.8 Exercises
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| Exercises | Docentstructuur | Concrete, door een docent geconfigureerde oefening op basis van precies één technische module, inclusief opgeslagen configuratiepayload en eigen lifecycle. | ExerciseModules, TeacherLevelCategoryExercises, ExerciseRuns, ExerciseHistory, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| Name | nvarchar(150) | - | N | N | - | N | N | J | Door docent gekozen naam van de oefening. |
| IconKey | nvarchar(100) | - | N | N | - | N | N | N | Door docent gekozen icoon voor deze oefening. |
| ExerciseModuleId | uniqueidentifier | - | N | J | ExerciseModules.Id | N | N | J | Technische moduleversie waarop deze oefening gebaseerd is. |
| ParentExerciseId | uniqueidentifier | null | N | J | Exercises.Id | N | J | J | Optionele verwijzing naar de bron-oefening wanneer deze oefening als kopie of afgeleide variant is aangemaakt. |
| ModuleConfigurationJsonBase64 | nvarchar(max) | - | N | N | - | N | N | N | Modulespecifieke configuratiepayload, opgeslagen als JSON in base64-vorm. |
| IsActive | bit | 0 | N | N | - | N | N | J | Status van de oefening zelf: 0 = in onderhoud, 1 = actief en regulier inzetbaar. |
| CreatedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Gebruiker die de oefening oorspronkelijk heeft aangemaakt. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
| UpdatedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | J | Gebruiker die de laatste wijziging heeft uitgevoerd. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| UpdatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Laatste wijzigmoment. |
Validaties / constraints
- ExerciseModuleId en ModuleConfigurationJsonBase64 zijn verplicht.
- ParentExerciseId mag niet naar hetzelfde record verwijzen.
- Een oefening verwijst altijd naar precies één record in ExerciseModules, terwijl één moduleversie door meerdere oefeningen gebruikt kan worden.
Business rules
- Een oefening is de concrete opgeslagen configuratie van een technische module.
- Nieuwe oefeningen starten standaard in onderhoud (IsActive = 0).
- Zodra de docent de oefening afrondt en activeert kan deze in een niveau-categoriecontext gebruikt worden.
- Bij een modulemigratie wordt de oefening als bronobject aangepast voor toekomstig gebruik; de actie kan productiebreed of docentgericht worden uitgevoerd.
- Voor gecontroleerd testen wordt geen fijnmaziger selectie dan docentniveau afgedwongen in de UI.
Lifecycle / gedrag
- Oefeningen worden in principe niet hard verwijderd.
- Historie, samenwerking tussen collaborators en afgeleide kopieën maken het wenselijk om oefeningen te deactiveren of administratief uit te faseren.
- Bij migratie naar een andere technische module blijven bestaande resultaten functioneel intact en moet delen vanuit historie blijven werken; oude technische modules mogen daarom pas na een overbruggingsperiode en gecontroleerde opschoning verwijderd worden.
Designkeuzes
- De configuratiepayload blijft bewust als JSON in base64 opgeslagen omdat iedere technische module een eigen configuratie-DTO en eigen antwoord-/vraagstructuur kan hebben.
- De configuratiepayload bevat binnen de module-specifieke JSON minimaal een moduleherleidbare sleutel en schema-aanduiding, zoals
moduleKeyenschemaVersion, zodat de technische module oude configuratievormen gecontroleerd kan interpreteren. Dit introduceert geen extra databasekolommen; de herleidbaarheid hoort bij de payload zelf. - Tegelijk wordt uitgegaan van uitbreidbare payloads volgens het open/closed principe, bijvoorbeeld door toekomstige extra configuratie-ingangen nullable toe te voegen zodat bestaande configuraties blijven werken.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
ExerciseModuleId -> ExerciseModules.Id;ParentExerciseId -> Exercises.Id.
Functionele / logische verwijzingen zonder harde FK
CreatedByUserIdenUpdatedByUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.IconKeyfunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.ModuleConfigurationJsonBase64bevat vrije of modulespecifieke payload en is bewust niet relationeel uitgesplitst naar harde foreign keys.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.9 ExerciseHistory
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| ExerciseHistory | Audit en historie | Append-only audittrail van wijzigingen op oefeningen, inclusief configuratie-items, oude en nieuwe waardes en actorinformatie. | Exercises, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| ExerciseId | uniqueidentifier | - | N | J | Exercises.Id | N | N | J | Oefening waarop de wijziging betrekking heeft. |
| ActionType | nvarchar(50) | - | N | N | - | N | N | J | Gesloten domeinwaarde die het soort wijziging benoemt. |
| ConfigItem | nvarchar(200) | null | N | N | - | N | J | J | Unieke verwijzing naar het configuratie-item dat door de technische module als gewijzigd is aangeleverd. |
| OldValue | nvarchar(max) | null | N | N | - | N | J | N | Vorige exacte waarde vóór de wijziging. |
| NewValue | nvarchar(max) | null | N | N | - | N | J | N | Nieuwe exacte waarde na de wijziging. |
| ChangedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Gebruiker die de wijziging uitvoerde. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| ChangedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Moment waarop de wijziging is vastgelegd. |
Validaties / constraints
- ActionType gebruikt een gesloten vaste waardeset in code en database.
- ChangedByUserId en ChangedAtUtc zijn verplicht.
- ConfigItem mag leeg blijven voor acties op recordniveau zoals create, copy_from_parent of update_is_active, maar is verplicht zodra een concreet configuratie-item wordt gewijzigd.
Business rules
- ExerciseHistory registreert wie wat wanneer heeft aangepast aan een oefening.
- Dit is essentieel omdat meerdere docenten als collaborator aan hetzelfde niveau en daarmee aan dezelfde oefeningen kunnen werken.
- Ook modulemigraties van een oefening moeten hier zichtbaar worden gemaakt via een expliciete action type, zodat later te reconstrueren is dat een beheerder de oefening technisch heeft overgezet.
Lifecycle / gedrag
- ExerciseHistory is append-only en wordt niet aangepast of verwijderd door reguliere functionele processen.
- Een gecombineerde wijziging levert meerdere historyrecords op, zodat per aspect zichtbaar blijft wat wanneer veranderd is.
- Voor een modulemigratie is een aparte action type verplicht; JSON-details zijn daarvoor niet nodig.
Designkeuzes
- ConfigItem wordt bewust door de technische module aangeleverd, omdat alleen de module zelf eenduidig weet welk configuratieonderdeel gewijzigd is.
- Hierdoor blijft de auditlog leesbaar, ook bij sterk modulespecifieke instellingen.
- Voor modulemigraties blijft ConfigItem leeg en wordt de betekenis volledig gedragen door de action type en de aparte migratietabel.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
ExerciseId -> Exercises.Id.
Functionele / logische verwijzingen zonder harde FK
ChangedByUserIdis een soft link naarUsers.Id; er wordt geen harde database-FK naar het identity-domein gelegd.ActionTypefunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.10 ExerciseModules
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| ExerciseModules | Technische modules | Door beheer vrijgegeven technische oefenmodules waar oefeningen expliciet naar verwijzen. Bevat de vertaallaag tussen weergavenaam, code-referentie en versie, plus activatie- en testzichtbaarheid. | Exercises, ExerciseRuns, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel van de technische moduleversie. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| DisplayName | nvarchar(150) | - | N | N | - | N | N | J | Leesbare naam die in selectielijsten voor docenten wordt getoond. |
| CodeReference | nvarchar(200) | - | N | N | - | N | N | J | Technische referentie waarmee via strategy pattern de juiste module-implementatie wordt gevonden. |
| Version | nvarchar(50) | - | N | N | - | N | N | J | Door beheer vastgelegde versiereferentie, bijvoorbeeld 1.2 of 2.0-preview. |
| IsActive | bit | 1 | N | N | - | N | N | J | Bepaalt of deze moduleversie regulier beschikbaar is voor gebruik in nieuwe oefeningen. |
| IsVisibleForTesting | bit | 0 | N | N | - | N | N | J | Bepaalt of deze moduleversie zichtbaar mag zijn voor docenten met de rol TestDocent, ook wanneer de versie nog niet regulier actief is. |
| CreatedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Beheerder die deze moduleversie heeft geregistreerd of vrijgegeven. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| CreatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Aanmaakmoment. |
| UpdatedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | N | Beheerder die de laatste wijziging heeft uitgevoerd. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| UpdatedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Laatste wijzigmoment. |
Validaties / constraints
- De combinatie CodeReference + Version is uniek.
- Een moduleversie kan tegelijk actief, inactief of uitsluitend zichtbaar voor testdoeleinden zijn, maar de precieze zichtbaarheid voor docenten volgt altijd uit combinatie van de modulevlaggen en de roltoekenning van de gebruiker.
Business rules
- Een oefening verwijst expliciet naar precies één record in ExerciseModules. Hierdoor ligt per oefening en per run vast welke technische moduleversie verantwoordelijk is voor beschrijving, configuratie en generatie.
- Een beheerder moet oefeningen kunnen migreren van een oude naar een nieuwe beschikbare technische module; dit kan productiebreed of beperkt tot alle oefeningen van één geselecteerde docent die die module gebruiken.
- De vlag IsVisibleForTesting mag functioneel onafhankelijk van IsActive worden beheerd.
- Een technische module mag pas inactief worden gemaakt wanneer er geen records meer bestaan in Exercises waarbij Exercises.ExerciseModuleId naar deze module verwijst.
Lifecycle / gedrag
- Beheer registreert handmatig de DisplayName, CodeReference en Version van een moduleversie. Reguliere docenten zien alleen actieve moduleversies; docenten met de rol TestDocent mogen desgewenst ook testzichtbare versies configureren of testen. Bij het registreren van een moduleversie moet via de strategy-interface een eenvoudige connectiviteitstest mogelijk zijn, zodat typefouten of foutieve technische koppelingen vroeg ontdekt worden. Het afzonderlijk wijzigen van testzichtbaarheid mag de productieve inzetbaarheid van een module niet impliciet wijzigen.
Designkeuzes
- Er is bewust geen aparte extra autorisatielaag voor testmodules geïntroduceerd. De combinatie van de bestaande rollenstructuur, de rol TestDocent en de vlag IsVisibleForTesting houdt het model klein. Migratie tussen moduleversies wordt bewust als beheerderactie toegestaan zonder harde database- of codevalidatie op backwards compatibility; dit is een geaccepteerd beheerrisico zolang downgrade of herstel mogelijk blijft.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: niet van toepassing binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
CreatedByUserIdenUpdatedByUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.CodeReferencefunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.10.1 ExerciseModuleMigrations
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| ExerciseModuleMigrations | Audit en historie | Registreert migraties van oefeningen van een bronmodule naar een doelmodule, inclusief scope, actor, moment en module-naamsnapshots. | Exercises, ExerciseModules, Users, ExerciseHistory |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| ExerciseId | uniqueidentifier | null | N | J | Exercises.Id | N | J | J | Optioneel gevuld wanneer de migratie administratief wordt vastgelegd voor één specifieke oefening als afzonderlijke proef- of testmigratie vóór bredere uitrol. Dit veld introduceert geen extra bulk-scope naast Global en PerTeacher, maar markeert een concrete enkelvoudige migratiehandeling. |
| SourceExerciseModuleId | uniqueidentifier | - | N | J | ExerciseModules.Id | N | N | J | Bronmodule van waaruit gemigreerd wordt. |
| TargetExerciseModuleId | uniqueidentifier | - | N | J | ExerciseModules.Id | N | N | J | Doelmodule waarnaar gemigreerd wordt. |
| SourceExerciseModuleNameSnapshot | nvarchar(200) | - | N | N | - | N | N | N | Naam- en versiesnapshot van de bronmodule ten tijde van de migratie. |
| TargetExerciseModuleNameSnapshot | nvarchar(200) | - | N | N | - | N | N | N | Naam- en versiesnapshot van de doelmodule ten tijde van de migratie. |
| ScopeType | nvarchar(30) | - | N | N | - | N | N | J | Functionele scope van de migratie, bijvoorbeeld Global of PerTeacher. |
| TeacherUserId | uniqueidentifier | null | N | N | Users.Id | N | J | J | Optioneel gevulde docent wanneer de migratie alleen alle oefeningen van één docent raakt. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| MigratedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Beheerder die de migratie uitvoerde. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| MigratedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | J | Moment waarop de migratie administratief is vastgelegd. |
| Reason | nvarchar(1000) | - | N | N | - | N | N | N | Verplichte toelichting of aanleiding voor de migratie. |
Validaties / constraints
- SourceExerciseModuleId en TargetExerciseModuleId moeten verschillend zijn en naar beschikbare of historisch bekende moduleversies verwijzen.
- TeacherUserId is verplicht wanneer ScopeType = PerTeacher en moet leeg blijven bij ScopeType = Global.
Business rules
- Een migratie kan productiebreed worden uitgevoerd voor alle oefeningen die een bronmodule gebruiken, of beperkt blijven tot alle relevante oefeningen van één geselecteerde docent.
- Fijnmaziger selectie dan docentniveau wordt functioneel niet afgedwongen.
- Het uitgangspunt is dat backwards compatibility vooraf getest is; dit risico wordt niet hard in code of database afgevangen.
Lifecycle / gedrag
- Bestaande resultaten en geschiedenis blijven functioneel intact na migratie.
- Oude technische modules mogen na een overbruggingsperiode en gecontroleerde opschoning verwijderd worden; de naam- en versiesnapshots in deze tabel zorgen dat auditinformatie leesbaar blijft.
Designkeuzes
- Een aparte migratietabel legt bronmodule, doelmodule, scope en actor compact vast.
- ExerciseHistory blijft daarnaast de leesbare audittrail op oefeningniveau, met een expliciete action type voor modulemigratie.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau:
ExerciseId -> Exercises.Id;SourceExerciseModuleId -> ExerciseModules.Id;TargetExerciseModuleId -> ExerciseModules.Id.
Functionele / logische verwijzingen zonder harde FK
TeacherUserIdenMigratedByUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.ScopeTypefunctioneert als code-, enum- of sleutelwaarde en verwijst bewust niet via een harde foreign key.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.11 LiveViewAudit
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| LiveViewAudit | Audit en historie | Audittrail van live meekijksessies, inclusief wie meekijkt, vanuit welke rol, bij welke gebruiker en bij welke oefenrun. | Users, Roles, ExerciseRuns |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| ViewerUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Gebruiker die de meekijksessie startte. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| ViewerRoleId | uniqueidentifier | - | N | N | Roles.Id | N | N | J | Rol waarmee de meekijksessie is gestart. Soft link naar Roles.Id; geen harde database-FK vanwege modulegrens. Rolnaam wordt vastgelegd in ViewerRoleNameSnapshot. |
| ViewerRoleNameSnapshot | nvarchar(100) | - | N | N | - | N | N | N | Snapshot van de rolnaam / context op het moment van meekijken, zodat audit leesbaar blijft als rolnamen later wijzigen. |
| ObservedUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Gebruiker die tijdens de sessie is bekeken. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| ExerciseRunId | uniqueidentifier | - | N | N | ExerciseRuns.Id | N | N | J | Oefenrun waarop de meekijksessie betrekking had. Soft link naar ExerciseRuns.Id; geen harde database-FK vanwege modulegrens. De oefenrun en run-snapshots blijven bron voor inhoudelijke context. |
| StartedAtUtc | datetime2 | - | N | N | - | N | N | J | Startmoment van de meekijksessie. |
| EndedAtUtc | datetime2 | - | N | N | - | N | J | J | Eindmoment van de meekijksessie. Mag leeg blijven zolang de sessie nog actief is of onvolledig is afgesloten. |
Validaties / constraints
- ViewerUserId, ViewerRoleId, ObservedUserId, ExerciseRunId en StartedAtUtc zijn verplicht.
- EndedAtUtc moet gelijk aan of later dan StartedAtUtc zijn wanneer het veld gevuld is.
Business rules
- LiveViewAudit registreert auditbaar wie, vanuit welke rol, bij welke gebruiker en bij welke oefenrun live heeft meegekeken.
- Dit ondersteunt privacy, navraag, beveiligingscontrole en reconstructie van meekijksessies.
- Meerdere gelijktijdige meekijkers zijn toegestaan, waaronder combinaties van meerdere ouders/voogden en meerdere docenten bij dezelfde actieve oefensessie.
Lifecycle / gedrag
- Iedere meekijksessie levert één auditrecord op.
- De sessie wordt bij start geopend met StartedAtUtc en bij afsluiten of timeout aangevuld met EndedAtUtc.
- Live meekijken verloopt via SignalR. Bij tijdelijk verbindingsverlies wordt een beperkt retrymechanisme toegepast; wanneer de verbinding daarna niet herstelt, wordt de sessie functioneel als verbroken behandeld.
- Records worden niet hard verwijderd door reguliere functionele processen.
- De sessie eindigt functioneel wanneer de leerling de actieve oefensessie afsluit of wanneer de verbinding definitief wordt verbroken.
Designkeuzes
- Het starten van een ouder-/voogd-live-meekijksessie maakt altijd precies één
LiveViewAudit-record aan; online-overzicht en beschikbaarheidsbepaling doen dat niet. - Beëindigen van een live-meekijksessie is idempotent: wanneer
EndedAtUtcal gevuld is, wordt geen tweede beëindiging geregistreerd en wordt de oefenrun niet geraakt. - Verbindingsverlies, logout, run-einde of weg navigeren beëindigen de auditbare live sessie zonder antwoord-, score-, voortgangs- of runstatusmutatie.
- Browse-modus en terugkeren naar live zijn lokale UI-state en krijgen geen eigen databasekolommen.
- ViewerRoleNameSnapshot wordt bewust naast ViewerRoleId vastgelegd zodat auditregels begrijpelijk blijven wanneer rolnamen of roltoewijzingen later wijzigen.
- De auditlaag registreert de meekijksessie zelf en niet de afzonderlijke realtime updates; de actuele inhoud wordt tijdens de sessie via SignalR vanuit de lopende run ontsloten.
- De audit legt sessies vast en niet elk afzonderlijk live event of iedere schermrefresh.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: niet van toepassing binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
ViewerUserIdenObservedUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.ViewerRoleIdis een soft link naarRoles.Id; rolcontext wordt daarnaast vastgelegd inViewerRoleNameSnapshot.ExerciseRunIdis een soft link naarExerciseRuns.Id; de live-audit verwijst functioneel naar de oefenrun zonder harde database-FK naar het practice-domein.
Soft link + snapshot
ViewerRoleId+ViewerRoleNameSnapshotleggen de rolcontext historisch leesbaar vast zonder harde database-FK.- De inhoudelijke runcontext blijft beschikbaar via de soft link naar
ExerciseRuns.Iden de run-snapshots in het practice-domein.
3.12 TeacherStudentLevelAccess
| Tabelnaam | Categorie | Doel / verantwoordelijkheid | Gerelateerde tabellen |
|---|---|---|---|
| TeacherStudentLevelAccess | Autorisatie | Toegang van een leerling tot een of meer niveaus van een docent. | UserRelationships, TeacherLevels, Users |
| Veldnaam | Type | Default | PK | FK | Verwijst naar | Unique | Nullable | Index | Opmerking |
|---|---|---|---|---|---|---|---|---|---|
| Id | uniqueidentifier | - | J | N | - | J | N | J | Primaire sleutel. GUID wordt in de applicatiecode gegenereerd; geen database-default. |
| TeacherStudentRelationshipId | uniqueidentifier | - | N | N | UserRelationships.Id | N | N | J | Actieve docent-leerlingrelatie waarop de toegang rust. Soft link naar UserRelationships.Id; geen harde database-FK vanwege modulegrens. |
| TeacherLevelId | uniqueidentifier | - | N | N | TeacherLevels.Id | N | N | J | Niveau waarvoor toegang wordt verleend. Soft link naar TeacherLevels.Id; geen harde database-FK omdat niveau-autorisatie in het autorisatiedomein ligt en het niveau in het catalogusdomein. |
| StudentUserId | uniqueidentifier | - | N | N | Users.Id | N | N | J | Leerling met toegang tot het niveau. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| GrantedAtUtc | datetime2 | sysutcdatetime() | N | N | - | N | N | N | Moment van autoriseren. |
| GrantedByUserId | uniqueidentifier | - | N | N | Users.Id | N | N | N | Gebruiker die toegang heeft verleend. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| IsActive | bit | 1 | N | N | - | N | N | J | Actieve toegang tot het niveau. |
| RevokedAtUtc | datetime2 | null | N | N | - | N | J | N | Moment van intrekken. |
| RevokedByUserId | uniqueidentifier | null | N | N | Users.Id | N | J | N | Gebruiker die toegang heeft ingetrokken. Soft link naar Users.Id; geen harde database-FK vanwege modulegrens. |
| RevocationReason | nvarchar(500) | null | N | N | - | N | J | N | Optionele toelichting op intrekken. |
Validaties / constraints
- Actieve combinatie TeacherStudentRelationshipId + TeacherLevelId + StudentUserId is uniek.
- TeacherLevel moet eigendom zijn van de docentzijde van de relatie.
Business rules
- Een leerling kan toegang hebben tot meerdere niveaus van dezelfde docent én van meerdere docenten.
- Een leerling kan tussen geautoriseerde niveaus wisselen in het profiel of op de frontpage.
- Collaborators op een niveau krijgen hierdoor geen leerlingtoegang; daarvoor blijft een expliciete docent-leerlingrelatie nodig.
- Vanuit beheer kan een leerling alleen aan een niveau worden toegevoegd wanneer tussen de betreffende docent en leerling al een actieve docent-leerlingrelatie bestaat.
- Deze autorisatieflow zet dus geen nieuwe relatie-uitnodiging of acceptatieproces op, maar kent uitsluitend niveau-toegang toe binnen een reeds bestaande relatiecontext.
Lifecycle / gedrag
- Bij beëindiging van een docent-leerlingrelatie worden alle actieve toegangen die aan die relatie gekoppeld zijn ingetrokken.
- Wanneer een beheerder vanuit ondersteuning niveau-toegang toevoegt of intrekt, moet deze actie auditbaar zijn en voorzien worden van een reden.
Designkeuzes
- Autorisatie is bewust los gemodelleerd van de relatietabel.
- Daardoor blijft de relatie een pure koppeling en blijft niveau-toegang uitbreidbaar.
Foreign keys op databaseniveau
- Harde foreign keys op databaseniveau: niet van toepassing binnen deze tabel.
Functionele / logische verwijzingen zonder harde FK
TeacherStudentRelationshipIdis een soft link naarUserRelationships.Id; de actieve relatie wordt applicatief gevalideerd via het relatiedomein.TeacherLevelIdis een soft link naarTeacherLevels.Id; het niveau blijft eigendom van het catalogusdomein.StudentUserId,GrantedByUserIdenRevokedByUserIdzijn soft links naarUsers.Id; er worden geen harde database-FK's naar het identity-domein gelegd.
FK + snapshot
- FK + snapshot: niet van toepassing binnen deze tabel.
3.13 Profiel- en meldingencontext binnen docentstructuur
3.13.1 Geselecteerd niveau in gebruikersprofiel
UserSettings.SelectedTeacherLevelIdverwijst naar een docentniveau dat voor de gebruiker functioneel toegankelijk moet zijn.- Voor leerlingen wordt de toegestane set bepaald door actieve
TeacherStudentLevelAccess-records en de onderliggende actieve docent-leerlingrelatie. - Het kiezen van een niveau in het profiel maakt geen nieuw
TeacherStudentLevelAccess-record aan. - Wanneer een eerder geselecteerd niveau later niet meer toegankelijk is, moet de applicatie de actieve niveaucontext opnieuw bepalen of de gebruiker opnieuw laten kiezen.
- Een verplichte niveaukeuze mag niet worden opgeslagen wanneer de gekozen waarde niet meer toegankelijk of niet meer actief is.
3.13.2 Doorzetten van meldingen naar docent
- Wanneer een melding naar een docent wordt doorgezet, moet de geselecteerde docent functioneel passen binnen de actuele context van de melding en de gebruiker.
- De doorzetactie maakt geen nieuwe docent-leerlingtoegang aan en wijzigt geen
TeacherStudentLevelAccess. - Eventuele privécommunicatie die door de doorzetactie ontstaat, gebruikt het communicatiedomein en niet de niveau-autorisatietabellen als opslaglocatie.
3.14 Account-lifecycle binnen docentstructuur
3.14.1 Accountverwijdering en docentniveau-eigenaarschap
Wanneer een docent het eigen account verwijdert of wanneer het account wordt geanonimiseerd, mag niveau-eigenaarschap niet onbepaald achterblijven. Voor niveaus waarvan de gebruiker actuele eigenaar is gelden de volgende regels:
- zonder actieve collaborator wordt het niveau historisch of inactief gemaakt;
- met precies één actieve collaborator wordt eigenaarschap automatisch aan die collaborator overgedragen;
- met meerdere actieve collaborators moet vóór definitieve selfservice-verwijdering een geldige opvolgende eigenaar worden gekozen.
De opvolger moet altijd een actieve collaborator op het betreffende niveau zijn. Eigendomsoverdracht door accountverwijdering moet auditbaar blijven en mag geen nieuwe docent-leerlingrelaties of niveauautorisaties aanmaken.
3.14.2 Afhankelijke toegang na anonimisering
Leerling-, docent-, ouder-/voogd- en collaborator-toegang die afhankelijk was van het verwijderde account wordt beëindigd of niet langer toegepast volgens de bestaande domeinregels. Historische eigendomsoverdrachten, afgeronde oefenruns en auditrecords blijven reconstructeerbaar onder geanonimiseerde identiteit wanneer dat functioneel nodig is.
Actieve live-meekijksessies waarbij het geanonimiseerde account betrokken is, worden beëindigd. De bestaande LiveViewAudit-informatie blijft historisch beschikbaar en wordt waar nodig voorzien van een eindmoment.
3.15 Beheerder- en ouder-/voogdcontext binnen docentstructuur
| Onderwerp | Aanscherping |
|---|---|
| Docentondersteuning | Beheerder-docentondersteuning werkt als supportcontext boven bestaande docentstructuren. De beheerder selecteert eerst één docentcontext en inspecteert daarna niveaus, categorieën, oefeningen, leerlingen, collaborators, eigenaarschap en geschiedenis. |
| Geen duplicatie centraal beheer | Docentondersteuning vervangt geen centraal categoriebeheer en geen centraal modulebeheer. Categorie-identiteit, modulemetadata en modulemigratie blijven in de daarvoor bedoelde beheerdomeinen. |
| Leerlingtoegang corrigeren | Een beheerder mag binnen docentondersteuning leerlingtoegang op niveau corrigeren wanneer de geldige docentcontext en relatie-/autorisatievoorwaarden server-side zijn vastgesteld. |
| Collaboratorcorrectie | Collaborators kunnen door beheer worden toegevoegd of verwijderd binnen een expliciete supportflow. Collaboratorrechten blijven beperkt tot onderwijsinhoud binnen het niveau en geven geen leerling-, resultaat- of live-meekijktoegang. |
| Eigenaarschapsoverdracht door beheerder | Eigenaarschapsoverdracht vanuit beheer volgt dezelfde kernregel als de docentflow: alleen een actieve collaborator kan nieuwe eigenaar worden en een reden is verplicht. |
| Ouder-/voogdinzage | Een ouder/voogd ziet resultaten en geschiedenis van alle niveaus van gekoppelde kinderen. Deze inzage is onafhankelijk van de docent die de oefening oorspronkelijk heeft aangeboden. |
| Ouder-/voogd live meekijken | Ouder-/voogd-live-meekijken gebruikt dezelfde opgeslagen voortgangsbron als docent-live-meekijken, maar de autorisatiegrens is de actieve ouder-/voogdrelatie met het kind. |