7. Communicatie en notificaties
Deze pagina is de ERD-inzoomlaag voor het databasehoofdstuk 04_communicatie-en-notificaties.md. De tabeldefinities in dat brondocument blijven leidend; deze pagina helpt vooral om de scheiding tussen systeemberichten, privéberichtthreads, mailboxweergave en sitebrede notificatieconcepten goed te lezen.
7.1 Afbakening van deze inzoomlaag
Het communicatiedomein bevat bewust twee verschillende vormen van communicatie:
- Mailbox-systeemberichten: individuele berichten aan een gebruiker via
SystemMessages, eventueel met een functionele verwijzing naar een relatie-uitnodiging, ticket, privéthread of gedeelde oefening. - Privéberichtthreads: conversaties met deelnemers, berichten, leespositie per deelnemer en thread-events.
Sitebrede systeemnotificaties, popupdefinities en systeemberichttemplates zijn functioneel communicatie-gerelateerd, maar staan database-technisch in 05_configuratie-en-contentbeheer.md. Zij worden daarom op deze pagina als context getoond en niet als harde interne FK-relaties van het communicatiedomein.
7.2 Leesroute
| Vraag | Begin bij | Reden |
|---|---|---|
| Waar komt een ongelezen badge vandaan? | Mailbox-readmodel | Badges worden afgeleid uit SystemMessages.ReadAtUtc, participant-readstate, berichten en thread-events. |
| Hoe opent een systeembericht een onderliggend object? | Systeemberichten en domeinverwijzingen | EntityType + EntityId vormt een bewuste logische verwijzing zonder harde FK. |
| Hoe werkt een privégesprek? | Privéberichtthread kern-ERD | Thread, deelnemers, berichten en events vormen samen de conversatie. |
| Waar zitten templates, popups en sitebrede notificaties? | Communicatiecontext buiten dit domein | Die tabellen horen bij configuratie/contentbeheer, maar sturen wel communicatiegedrag. |
| Wat gebeurt er tijdens een actieve leerling-oefening? | Afleidingsvrije oefencontext | Onderliggende data blijft bestaan, maar zichtbare signalering wordt tijdelijk uitgesteld. |
7.3 Privéberichtthread kern-ERD
Dit diagram toont alleen de relationele kern van privéberichten. Het is bewust kleiner dan de mailboxweergave, omdat systeemberichten technisch geen onderdeel zijn van een privéthread.
Soft links binnen en buiten het communicatiedomein worden apart getoond, zodat zij niet als harde database-FK worden gelezen:
Interpretatie
PrivateMessageThreadsis de container voor onderwerp en laatste berichtactiviteit.PrivateMessageThreadParticipantsbepaalt per gebruiker zichtbaarheid, deelname, soft-delete en leespositie.PrivateMessagesbevat de feitelijke privéberichten, inclusief de uitzonderingssituatie waarin een beheerder een bericht namens een gebruiker laat ontstaan viaSendAsUserId.PrivateMessageThreadEventsbevat threadinterne gebeurtenissen zoals onderwerpwijziging of deelnemerwijziging. Dit zijn geen mailbox-systeemberichten.LastReadMessageIdis een soft link naar het laatst gelezen normale privébericht;LastReadAtUtcblijft nodig omdat thread-events ook ongelezenstatus kunnen veroorzaken en omdat privéberichtretentie de readstate niet mag blokkeren.
7.4 Systeemberichten en domeinverwijzingen
SystemMessages is een aparte mailboxbron. RecipientUserId is een soft link naar identity.Users.Id en geen harde database-FK vanwege de modulegrens. De functionele doorklik naar een onderliggend object gebeurt via de combinatie EntityType + EntityId.
De logische verwijzing kan naar precies vier domeinobjecttypen wijzen:
Waarom geen harde FK?
Er is bewust geen database-FK op EntityId, omdat één kolom afhankelijk van EntityType naar verschillende tabellen kan verwijzen. Dit is geen vrij generiek polymorf platformmechanisme, maar een beperkte functionele domeinverwijzing binnen SystemMessages. Geldigheid wordt daarom bewaakt via enumvalidatie, applicatielogica, autorisatiecontrole en tests.
Consequenties voor het ERD
| Aspect | Betekenis |
|---|---|
| Harde database-FK | Niet van toepassing binnen SystemMessages. |
| Logische verwijzing | RecipientUserId als soft link naar identity.Users.Id; EntityType + EntityId naar RelationshipInvitations, Tickets, PrivateMessageThreads of SharedExercises. |
| Autorisatie | Openen van het bericht is niet voldoende; de vervolgactie controleert opnieuw of het doelobject nog toegankelijk is. |
| Retentie | Systeemberichten worden niet hetzelfde behandeld als privéberichten met drie maanden retentie. |
| Navigatie | Er wordt geen losse ActionUrl opgeslagen; de frontend routeert op basis van het domeintype. |
7.5 Mailbox-readmodel en ongelezenlogica
7.5.0 Threadpresentatie en timeline-readmodel
PrivateMessageThreads bevat de stabiele threadpresentatie voor mailbox en detail, zoals threadkleur en icon key. PrivateMessageThreadParticipants bevat de participantgebonden zichtbaarheid, readstate en threadspecifieke accentkleur. Het mailboxreadmodel leidt het label Privégesprek of Groepsgesprek af uit de zichtbare actieve participants.
Voor privéthreads toont het readmodel bij ongelezen activiteit de eerste nieuwe activiteit voor de ingelogde participant als preview. Zonder nieuwe activiteit toont het de laatste zichtbare activiteit. De detailweergave laadt een timelinevenster oud-naar-nieuw rond #new of #latest; oudere items worden met een beveiligde cursor opgehaald.
De mailbox in de GUI is geen aparte tabel. Het is een afgeleid readmodel over twee bronnen:
Belangrijk hierbij:
- Systeemberichten en privéthreads mogen in dezelfde mailboxweergave staan, maar blijven database-technisch gescheiden.
- Ongelezen systeemberichten worden bepaald via
SystemMessages.ReadAtUtc. - Ongelezen privéthreads worden per deelnemer afgeleid uit
PrivateMessageThreadParticipants.LastReadMessageId,LastReadAtUtc, nieuwePrivateMessagesen nieuwePrivateMessageThreadEvents. DeletedAtUtcop de participantregel verwijdert de thread alleen uit de mailbox van die deelnemer.- Filters, zoekresultaten, aantallen en badges worden niet in een aparte mailbox- of teller-tabel opgeslagen.
7.6 Privébericht lifecycle
Een nieuw privébericht en een antwoord op een bestaande thread hebben een andere database-impact.
7.7 Systeembericht lifecycle
Systeemberichten zijn informatief en kunnen doorklikken naar een onderliggend object, maar voeren de onderliggende domeinactie niet automatisch uit.
Voorbeelden:
| EntityType | Typische herkomst | Vervolgcontext |
|---|---|---|
RelationshipInvitation | Relatiebeheer / accountprovisioning | Uitnodiging accepteren of afwijzen na hercontrole. |
Ticket | Meldingen | Ticketdetail, discussie of oplossing na autorisatiecontrole. |
PrivateMessageThread | Privéberichten | Thread openen wanneer participantcontext geldig is. |
SharedExercise | Gedeelde oefeningen | Gedeelde-oefeningcontext openen; start niet automatisch een run. |
7.8 Communicatiecontext buiten dit domein
De volgende communicatie-gerelateerde tabellen staan niet in databasehoofdstuk 4, maar zijn wel belangrijk voor het totaalbeeld.
| Onderdeel | Staat in | Relatie met communicatie |
|---|---|---|
SystemMessageTemplates | Configuratie & contentbeheer | Beheerbare sjablonen voor verplichte systeemcommunicatie; geen runtime mailboxitem. |
SiteNotifications | Configuratie & contentbeheer | Sitebrede overlay na frontpageload; geen mailbox-systeembericht en geen popupregister-popup. |
PopupDetails | Configuratie & contentbeheer | Beheerbare popupteksten voor feedback en foutafhandeling; geen bericht of notificatiebron. |
SiteFeatureToggles | Configuratie & contentbeheer | Kan functies zoals privéberichten, live meekijken of meldingen beschikbaar/niet beschikbaar maken. |
7.9 Afleidingsvrije oefencontext
Tijdens een actieve leerling-oefenrun wordt zichtbare communicatie-uiting tijdelijk onderdrukt, maar de onderliggende data blijft correct opgeslagen.
Dit is een UI- en timingregel, geen databasemutatie die berichten tijdelijk anders opslaat.
7.10 Tabellen in dit domein
| Tabel | Categorie | Doel / verantwoordelijkheid |
|---|---|---|
SystemMessages | Communicatie | Mailbox-items voor systeemberichten die gebruikers informeren en vanuit de GUI kunnen doorverwijzen naar een concreet domeinobject of vervolghandeling. |
PrivateMessageThreads | Communicatie | Bovenliggende conversatie-entiteit voor privéberichten. Bevat threadmetadata zoals onderwerp en laatste activiteit. |
PrivateMessageThreadParticipants | Communicatie | Deelnemers per privébericht-thread, inclusief mailboxspecifieke zichtbaarheid en leespositie per gebruiker. |
PrivateMessages | Communicatie | Individuele privéberichten binnen een thread. Ondersteunt reguliere berichten en berichten die namens een andere gebruiker worden verstuurd. |
PrivateMessageThreadEvents | Communicatie | Systeemachtige gebeurtenissen binnen een privébericht-thread, zoals onderwerpwijzigingen of deelnemers die de thread verlaten. |
7.11 Relatie-inventaris binnen dit domein
| Brontabel | Veld | Verwijst naar | Nullable | Relatietype | Betekenis in ERD |
|---|---|---|---|---|---|
SystemMessages | RecipientUserId | Users.Id | N | Soft link | Ontvanger van het systeembericht; geen harde FK naar identity. |
PrivateMessageThreads | CreatedByUserId | Users.Id | N | Soft link | Initiator van de thread; geen harde FK naar identity. |
PrivateMessageThreadParticipants | ThreadId | PrivateMessageThreads.Id | N | Harde FK | Deelnemer hoort bij thread. |
PrivateMessageThreadParticipants | UserId | Users.Id | N | Soft link | Deelnemende gebruiker; geen harde FK naar identity. |
PrivateMessageThreadParticipants | LastReadMessageId | PrivateMessages.Id | J | Soft link | Laatst gelezen normaal privébericht; bewust geen harde FK vanwege berichtretentie. |
PrivateMessages | ThreadId | PrivateMessageThreads.Id | N | Harde FK | Bericht hoort bij thread. |
PrivateMessages | SenderUserId | Users.Id | N | Soft link | Technische afzender; geen harde FK naar identity. |
PrivateMessages | SendAsUserId | Users.Id | J | Soft link | Functionele afzenderweergave namens gebruiker; geen harde FK naar identity. |
PrivateMessageThreadEvents | ThreadId | PrivateMessageThreads.Id | N | Harde FK | Event hoort bij thread. |
PrivateMessageThreadEvents | ActorUserId | Users.Id | J | Soft link | Gebruiker die event veroorzaakte; geen harde FK naar identity. |
PrivateMessageThreadEvents | AffectedUserId | Users.Id | J | Soft link | Gebruiker waarop event betrekking heeft; geen harde FK naar identity. |
7.12 Bewuste uitzonderingen en aandachtspunten
SystemMessages.EntityType + EntityIdmoet visueel als logische verwijzing worden getoond, niet als harde database-FK.PrivateMessageThreadEventszijn geenSystemMessages; zij horen binnen de threadtijdlijn.- Ticketdiscussie staat in
TicketDiscussionMessagesen niet inPrivateMessages, behalve de expliciete doorzet-naar-docentflow die een normale privéthread kan genereren. - Systeemnotificaties staan in
SiteNotificationsen zijn geen mailboxitems. - Privébericht-retentie en systeembericht-retentie verschillen functioneel.
- Badge- en tellerwaarden blijven afgeleid en horen niet als aparte mailbox-tabel te ontstaan.
7.13 Status en raakvlakken
Het communicatiedomein is in eerste diepte uitgewerkt en vormt de brug tussen relatiebeheer, tickets, gedeelde oefeningen en configuratiegedreven notificaties. Vooral de koppelingen SystemMessages.EntityType = Ticket, TicketForwardedToTeacher.GeneratedPrivateMessageThreadId en TicketForwardedToTeacher.GeneratedPrivateMessageId moeten consistent blijven met Ticket- en meldingsbeheer.
Voor verdere review hoort SystemMessages.EntityType + EntityId steeds als logische verwijzing te worden beoordeeld en niet als harde FK. Nieuwe verwijstypen mogen pas worden toegevoegd wanneer zij ook in de communicatieroutering, popup-/templatebeheer en cross-domein documentatie zijn verwerkt.