Skip to main content

Architectuuroverzicht en solution-opbouw

2.1 Doel van dit hoofdstuk

Dit hoofdstuk beschrijft de technische hoofdopbouw van OefenHub op solution- en runtime-niveau. Het legt vast hoe de applicatie als middel-zware modulaire monoliet wordt georganiseerd, welke projecten in de solution worden verwacht, hoe projecten zich tot database-schema's en DbContexts verhouden en welke runtimecomponenten samen de applicatie vormen.

Belangrijke inputbronnen en detailhoofdstukken zijn:

2.2 Architectuurkeuze

OefenHub wordt gebouwd als een middel-zware modulaire monoliet.

AspectKeuzeConsequentie
DeploymentEén deploybare applicatie.OefenHub wordt als één applicatie gebouwd, getest en uitgerold.
RuntimeEén applicatieproces voor de webapplicatie.Er is geen distributie over meerdere microservices in de eerste technische baseline.
DatabaseEén primaire relationele database.Er worden geen aparte databases of databaseservers per domeinmodule geïntroduceerd.
ConnectionstringEén applicatieconnectionstring voor de primaire database.Modulegrenzen worden niet afgedwongen via losse databasegebruikers in de eerste baseline.
DomeinindelingMeerdere moduleprojecten.Functionele domeinen worden als afzonderlijke projecten georganiseerd.
Data-eigenaarschapMaximaal één DbContext en één schema per persistent moduleproject.Vanuit de database is herleidbaar welk project eigenaar is van welke tabellen.
ModulecommunicatieVia publieke contracts, services, query-services, events of scheduling-contracten.Modules gebruiken elkaars interne entities, DbContexts of implementatieclasses niet rechtstreeks.
OefenmodulesConcrete technische oefenmodules als aparte projecten.Oefenlogica blijft vervangbaar en testbaar via het modulecontract.
Externe clients/APIGeen publieke functionele API-laag in de eerste baseline.Primaire interactie loopt via de Blazor-webapp; externe clients of mobiele apps worden niet als apart integratiekanaal ontworpen.
ClientscopeDesktop en tablet zijn leidend.Smartphonegebruik wordt niet als primair ondersteund gebruiksscenario geoptimaliseerd.

Deze keuze combineert de eenvoud van één deploybare applicatie met duidelijke interne grenzen. OefenHub vermijdt daarmee de operationele complexiteit van microservices, maar voorkomt tegelijk dat alle domeinen in één ongestructureerde monoliet terechtkomen.

2.3 Wat de modulaire monoliet wel en niet betekent

WelNiet
Duidelijke projectgrenzen per domein.Eén grote projectmap met alle domeinen door elkaar.
Publieke contracts als module-ingang.Willekeurige directe aanroepen naar interne classes van andere modules.
DbContext per persistent domeinmodule.Eén gedeelde DbContext waarin alle tabellen van alle domeinen zitten.
Schema per persistent domeinmodule.Losse databases of databaseservers per module.
Soft links en snapshots over modulegrenzen als uitgangspunt.Automatisch overal harde cross-module foreign keys.
Tests per moduleproject en aanvullende integratietests.Alleen end-to-endtests voor alle domeinlogica.
Interne scheduling met TickerQ.RabbitMQ, Kafka of een externe message broker in de eerste baseline.

De term monoliet gaat in dit Technisch Ontwerp dus uitsluitend over deployment en runtime. De interne structuur blijft modulair.

De scopegrens voor clients hoort bij dezelfde architectuurkeuze: OefenHub wordt niet ontworpen rond een publieke functionele API of aparte mobiele client. Interne endpoints, frameworkroutes, SignalR-hubs en infrastructuurinterfaces mogen bestaan waar de webapp of beheerinrichting die technisch nodig heeft, maar zij vormen geen publieke product-API voor externe applicaties.

2.4 Solutionstructuur

De solution wordt logisch verdeeld in src en tests.

OefenHub.sln
src/
OefenHub.Web/
OefenHub.SharedKernel/
OefenHub.Infrastructure/
OefenHub.Scheduling/

OefenHub.Identity/
OefenHub.Authorization/
OefenHub.Relationships/
OefenHub.Catalog/
OefenHub.Modules.Abstractions/
OefenHub.ExerciseModuleHost/
OefenHub.Practice/
OefenHub.Communication/
OefenHub.Support/
OefenHub.LiveMonitoring/
OefenHub.Admin/
OefenHub.Reporting/

OefenHub.Modules.<ModuleCategory>.<ModuleName>/

tests/
OefenHub.Identity.Tests/
OefenHub.Authorization.Tests/
OefenHub.Relationships.Tests/
OefenHub.Catalog.Tests/
OefenHub.ExerciseModuleHost.Tests/
OefenHub.Practice.Tests/
OefenHub.Communication.Tests/
OefenHub.Support.Tests/
OefenHub.LiveMonitoring.Tests/
OefenHub.Admin.Tests/
OefenHub.Reporting.Tests/
OefenHub.Scheduling.Tests/
OefenHub.Modules.<ModuleCategory>.<ModuleName>.Tests/
OefenHub.Web.Tests/
OefenHub.EndToEndTests/
OefenHub.ArchitectureTests/
OefenHub.IntegrationTests/

De fysieke mapstructuur mag deze indeling volgen. In Visual Studio of Rider mogen src en tests daarnaast als solution folders worden gebruikt zodat applicatieprojecten en testprojecten organisatorisch gescheiden blijven.

2.5 Projecttypen

Niet ieder project heeft dezelfde rol. De solution kent vier hoofdcategorieën.

ProjecttypeVoorbeeldenRol
Host/UIOefenHub.WebBlazor/Razor-host, routing, layouts, componenten, page composition, pipeline en applicatiestart.
DomeinmoduleOefenHub.Practice, OefenHub.Relationships, OefenHub.SupportEigen functioneel domein met contracts, services, eventueel DbContext, schema, entities en module-eigen readmodels.
Technisch platformprojectOefenHub.Infrastructure, OefenHub.Scheduling, OefenHub.ExerciseModuleHostTechnische ondersteuning, hostingintegratie, jobs, module-discovery of infrastructuurkoppelingen.
Contract-/sharedprojectOefenHub.SharedKernel, OefenHub.Modules.AbstractionsBeperkte gedeelde types of oefenmodulecontracten. Geen domeinlogica van één specifieke module.

2.6 Projecten en verantwoordelijkheden

ProjectVerantwoordelijkheidPersistent eigen schema?
OefenHub.WebBlazor/Razor-host, routing, componenten, layouts, viewmodels, page composition, middleware, pipelineconfiguratie en applicatiestart.Nee
OefenHub.SharedKernelZeer beperkte generieke bouwstenen zoals generieke resulttypes, tijd-/clock-abstractions, correlation-types en gedeelde primitives.Nee
OefenHub.InfrastructureTechnische infrastructuurkoppelingen, configuratiebinding, loggingintegratie, externe adapters en host-brede technische extensies.Nee, tenzij via technisch ontwerpbesluit expliciet nodig
OefenHub.SchedulingTickerQ-integratie, jobregistratie, joblifecycle, retries, jobstatussen, technische joblogging en interne jobbeheerinterface.Ja
OefenHub.IdentityIntern account, externe identity-providerkoppeling, provisioning, accountstatus, profielbasis en account lifecycle.Ja
OefenHub.AuthorizationRollen, rolcontext, policies, server-side contextcontrole en autorisatieondersteuning.Ja
OefenHub.RelationshipsRelaties, relatie-uitnodigingen, relatie-events, friend/guardian/teacher/admin-relaties en relatiegerichte toegangsvragen.Ja
OefenHub.CatalogNiveaus, centrale categorieën, oefeningen, oefeningconfiguratie, contentstatussen, collaborators en onderwijsinhoud.Ja
OefenHub.Modules.AbstractionsContracten voor technische oefenmodules, zoals descriptoren, configuratievalidatie, vraaggeneratie, antwoordcontrole, rendering en PDF-representatie.Nee
OefenHub.ExerciseModuleHostModule-discovery, module-registratie, strategy/factory-resolving en technische modulemetadata.Ja, indien modulemetadata persistent wordt beheerd
OefenHub.PracticeOefenruns, voortgang, resultaten, statistieken, gedeelde oefeningen, historische runcontext en oefenrun-readmodels.Ja
OefenHub.CommunicationInterne systeemberichten, privéberichten, threads, notificaties, systeemberichttemplates, badges en communicatiereadmodels.Ja
OefenHub.MailApplicatiegestuurde externe e-mail, mailtemplates, templatehistory, SMTP-dispatch en mailverzendpogingen.Ja
OefenHub.SupportMeldingen/tickets, ticketdiscussie, ticketstatussen, closures, reopen requests, doorzetten naar docent en ticketgeschiedenis.Ja
OefenHub.LiveMonitoringOnline-status, live meekijken, livebeschikbaarheid, live sessies en LiveViewAudit.Ja
OefenHub.AdminBeheercontent, popups, systeemnotificaties, site-instellingen, beheerteksten, handige links en managementlogs.Ja
OefenHub.ReportingPDF-export, rapportageopbouw, exportmodellen en rapportagegerichte rendering.Alleen als persistente rapportage-/exportstatus nodig is
OefenHub.Modules.<ModuleCategory>.<ModuleName>Concrete technische oefenmodule met moduleconfiguratie, validatie, vraaggeneratie, antwoordcontrole, rendering en PDF-representatie.Nee, tenzij via technisch ontwerpbesluit expliciet gemotiveerd

2.7 Projecten die bewust niet als basisproject worden opgenomen

Niet opgenomen basisprojectReden
OefenHub.AuditAudit en history zijn domeinspecifiek en blijven naast de primaire domeintabellen staan, zoals relatie-events, ticketgeschiedenis, live-view-audit en beheerlogs.
OefenHub.SecuritySecurityconfiguratie wordt verdeeld over Web, Infrastructure, Identity en Authorization. Er is nog geen zelfstandige securitymodule met eigen lifecycle of datalaag.
OefenHub.WorkflowsCross-module workflows worden ondergebracht bij de module die functioneel eigenaar is van de gebruikersactie.
OefenHub.ReadModelsModule-eigen readmodels blijven bij de eigenaar-module. Samengestelde UI-viewmodels worden in Web opgebouwd via publieke query-services.

Het ontbreken van deze projecten betekent niet dat audit, security, workflows of readmodels ontbreken. Het betekent alleen dat zij niet als afzonderlijke basisprojecten worden gemodelleerd zolang daar geen duidelijke zelfstandige eigenaar, lifecycle of datalaag voor bestaat.

2.8 Schema- en DbContext-overzicht

Een persistent domeinproject heeft maximaal één eigen DbContext en daarmee maximaal één eigen databaseschema. Schema's worden in kleine letters geschreven. Tabellen en kolommen gebruiken PascalCase.

ProjectDbContextSchemaVoorbeelden van tabellen
OefenHub.IdentityIdentityDbContextidentityUsers, UserProfiles, UserSettings, ProfileAvatars, AccountLifecycleLogs
OefenHub.AuthorizationAuthorizationDbContextauthorizationRoles, UserRoles, RoleContexts, AuthorizationGrants
OefenHub.RelationshipsRelationshipsDbContextrelationshipsUserRelationships, RelationshipInvitations, RelationshipEvents
OefenHub.CatalogCatalogDbContextcatalogLevels, Categories, LevelCategories, Exercises, ExerciseHistory, LevelCollaborators
OefenHub.ExerciseModuleHostExerciseModuleHostDbContextmodulesExerciseModules, ExerciseModuleMigrations, ModuleRegistryEntries
OefenHub.PracticePracticeDbContextpracticeExerciseRuns, ExerciseRunProgress, SharedExercises, RunSnapshots
OefenHub.CommunicationCommunicationDbContextcommunicationSystemMessages, PrivateMessageThreads, PrivateMessages, SystemMessageTemplates
OefenHub.SupportSupportDbContextsupportTickets, TicketAssignments, TicketClosures, TicketHistory, TicketReopenRequests
OefenHub.LiveMonitoringLiveMonitoringDbContextliveLiveViewAudit, OnlinePresence, LiveSessionStates
OefenHub.AdminAdminDbContextadminManagedContentBlocks, PopupDefinitions, SiteNotifications, ManagementLogEntries
OefenHub.SchedulingSchedulingDbContextschedulingTickerQ-tabellen, JobExecutionLogs, JobFailureRecords
OefenHub.ReportingReportingDbContext, alleen indien nodigreporting, alleen indien nodigAlleen persistente rapportage-/exportstatus als die via technisch ontwerpbesluit nodig blijkt

De tabelvoorbeelden zijn bedoeld als architectuuroverzicht. De exacte tabeldefinities, kolommen, enumwaarden, relaties en constraints blijven onderdeel van de database-informatie. Het Technisch Ontwerp beschrijft hoe deze technisch naar EF Core, schema's, migrations, constraints en modulegrenzen worden vertaald.

2.9 Naamgevingsregels op solution- en database-niveau

OnderdeelRegelVoorbeeld
SolutionPascalCaseOefenHub.sln
ProjectPascalCase met prefix OefenHubOefenHub.Practice
Concrete oefenmoduleOefenHub.Modules.<ModuleCategory>.<ModuleName>OefenHub.Modules.Arithmetic.AdditionSubtractionSimple
TestprojectProjectnaam + .TestsOefenHub.Practice.Tests
SchemaKleine letters, betekenisvol, geen afkortingen tenzij breed gangbaarcommunication
TabelPascalCaseSystemMessages
KolomPascalCaseCreatedAtUtc
DbContextProjectnaam + DbContextPracticeDbContext

De technische modulecategorie in een projectnaam is een technische ordening. Zij hoeft niet één-op-één gelijk te zijn aan een functionele categorie in de oefencatalogus. Een technische modulecategorie Arithmetic kan bijvoorbeeld meerdere functionele cataloguscategorieën ondersteunen of andersom. De eerste V1.0-categorie is Arithmetic; de eerste concrete module is OefenHub.Modules.Arithmetic.AdditionSubtractionSimple, uitgewerkt in het moduledossier Optellen & Aftrekken (simpel).

2.10 Runtime-overzicht

Onderstaande weergave geeft de hoofdcomponenten weer. Het diagram is ondersteunend en vervangt geen C4-diagram of gedetailleerde projectdocumentatie.

De belangrijkste runtimeafspraak is dat OefenHub.Web de applicatie host en UI-compositie uitvoert, maar geen module-interne data-eigenaarschap overneemt. Domeinmodules blijven eigenaar van hun eigen contracts, businessregels, DbContext, schema en data.

2.11 Webhost en UI-compositie

OefenHub.Web is het start- en hostproject van de applicatie. Het project bevat onder meer:

  • Blazor/Razor routing;
  • layouts;
  • pages;
  • components;
  • forms;
  • client- en server-side UI-state;
  • page composition;
  • middleware en pipelineconfiguratie;
  • koppeling van appsettings/env naar configuratie-DTO's;
  • registratie van dependency injection;
  • security headers, HSTS, CSP en rate limiting;
  • interne beheerroute naar TickerQ-dashboard wanneer deze technisch beschikbaar is.

OefenHub.Web mag samengestelde schermen opbouwen door publieke query-services van meerdere modules aan te roepen. Het project mag echter geen EF Core DbContexts of module-interne entities gebruiken.

Voorbeeld van toegestane UI-compositie:

Teacher frontpage
-> Catalog query-service voor niveaus en oefenaanbod
-> Practice query-service voor recente resultaten
-> Communication query-service voor badges
-> Support query-service voor actie-indicaties
-> Web bouwt één viewmodel voor rendering

Voorbeeld van niet-toegestane UI-compositie:

Teacher frontpage
-> Web opent CatalogDbContext rechtstreeks
-> Web joint direct op PracticeDbContext
-> Web leest communication.SystemMessages via EF entity

2.12 Modulecommunicatie op hoofdlijnen

Modules communiceren via expliciete publieke ingangen. De detailregels staan in hoofdstuk 03, maar op solutionniveau gelden al de volgende afspraken.

InteractieToegestane route
Mutatie binnen eigen domeinEigen command/application-service van de module.
Read-only vraag aan ander domeinPublieke query-service of reader van die module.
UI-readmodelModule-eigen query-service, samengevoegd in Web page composition.
Cross-module workflowOrchestratie in de functioneel eigenaar-module.
Uitgestelde verwerkingJob via OefenHub.Scheduling, uitvoering via domeincontract.
Technische oefenmoduleVia OefenHub.Modules.Abstractions en OefenHub.ExerciseModuleHost.

Niet toegestaan als standaardcommunicatie:

  • directe toegang tot een andere module-DbContext;
  • directe toegang tot entities uit Data/Entities van een andere module;
  • directe aanroep van interne services van een andere module;
  • cross-module SQL-queries vanuit willekeurige modules;
  • stille afhankelijkheden via gedeelde statische helpers waarin businessregels verdwijnen.

2.13 Cross-module workflows

Een cross-module workflow wordt geplaatst bij de module die functioneel eigenaar is van de gebruikersactie.

WorkflowEigenaarMogelijke betrokken modules
Relatie-uitnodiging aanmakenOefenHub.RelationshipsCommunication, Identity, Authorization
Accountprovisioning na externe loginOefenHub.IdentityRelationships, Communication, Authorization
Ticket doorzetten naar docentOefenHub.SupportCommunication, Relationships, Authorization
Oefening delen met vriendOefenHub.PracticeRelationships, Communication, Catalog
Live meekijksessie startenOefenHub.LiveMonitoringPractice, Relationships, Authorization

Er wordt in de basis geen apart OefenHub.Workflows project toegevoegd. Wanneer een toekomstige workflow ontstaat zonder duidelijke domeineigenaar of met zodanig veel domeinoverstijgende coördinatie dat module-eigenaarschap onduidelijk wordt, wordt dit opnieuw beoordeeld en als technisch ontwerpbesluit vastgelegd.

2.14 Transaction boundary per workflow

Het Technisch Ontwerp legt geen algemene regel vast dat alle cross-module stappen altijd los of altijd atomair worden uitgevoerd. Per workflow wordt bepaald welke stappen onderdeel zijn van de functionele transactie.

VraagGevolg
Leidt falen van deze stap tot een ongeldige functionele toestand?De stap hoort bij de kritieke transaction boundary.
Is deze stap de enige bruikbare ingang voor een gebruiker?De stap kan kritiek zijn, ook als het technisch een bericht of notificatie is.
Kan de stap veilig opnieuw worden uitgevoerd zonder dubbele effecten?De stap kan retrybaar worden verwerkt.
Is falen beheerbaar zonder dataverlies of misleiding van de gebruiker?De stap kan eventueel buiten de directe transactie vallen.
Is bij twijfel niet goed te bewijzen dat de stap niet kritiek is?Behandel de stap als kritiek totdat anders is onderbouwd.

Voorbeeld: een relatie-uitnodiging waarbij het systeembericht de primaire ingang voor de ontvanger is, kan het systeembericht als kritieke stap behandelen. Het bericht is dan niet zomaar een bijzaak, maar onderdeel van de functionele beschikbaarheid van de uitnodiging.

Voorbeeld: een badge-update na het aanmaken van een privébericht is doorgaans afgeleid gedrag. Als de badge via query of retry kan worden hersteld, hoeft die update niet de primaire transactie te blokkeren.

2.15 Scheduling en TickerQ op solutionniveau

OefenHub.Scheduling is het technische project voor background jobs, periodieke verwerking en retrybare uitvoering. Het project krijgt een eigen schema scheduling wanneer TickerQ-persistence en joblogging worden ingericht.

De eigenaarschapsscheiding is als volgt.

OnderdeelEigenaar
Job aanmaken vanuit een domeinflowInitiërende domeinmodule via scheduling-contract.
Job persistent opslaanOefenHub.Scheduling.
Job plannen en triggerenOefenHub.Scheduling.
Retrybeleid toepassenOefenHub.Scheduling.
Jobstatus en pogingenteller bewakenOefenHub.Scheduling.
Technische joblogging en correlationOefenHub.Scheduling.
Domeinactie uitvoerenBetreffende domeinmodule via publiek contract.
Businessregels van de actieBetreffende domeinmodule.

Een domeinmodule kan dus vragen om een job te plannen, maar zodra de job succesvol is aangemaakt, is OefenHub.Scheduling eigenaar van de technische lifecycle van die job. De domeinmodule blijft eigenaar van de businessactie die tijdens uitvoering wordt aangeroepen.

Voorbeeld:

Support sluit een ticket
-> Support vraagt Scheduling om een follow-up job te plannen
-> Scheduling bewaart job met JobType, payload, CorrelationId en retrybeleid
-> TickerQ triggert de job
-> Scheduling roept Support of Communication aan via publiek contract
-> Scheduling registreert poging, resultaat, fout of retry

De TickerQ-webinterface wordt alleen intern beschikbaar gemaakt. Zij mag niet publiek toegankelijk zijn en moet via hosting, netwerk, autorisatie of beheercontext worden afgeschermd.

2.16 Logging, correlation en herleidbaarheid op solutionniveau

Omdat OefenHub meerdere moduleprojecten en DbContexts gebruikt, moet technische herleidbaarheid vanaf het begin centraal worden meegenomen. Dat betekent niet dat er een apart auditproject komt, maar wel dat elke request-, workflow- en jobketen traceerbaar moet zijn.

Minimaal relevante identifiers:

IdentifierDoel
CorrelationIdVerbindt request, workflow, domeinactie, job en loggingregels.
RequestIdHerleidt technische HTTP-requestafhandeling.
JobIdHerleidt geplande en uitgevoerde jobs.
JobTypeMaakt zichtbaar welke joblogica is uitgevoerd.
AttemptNumberMaakt retrygedrag zichtbaar.
ActorUserIdLegt vast welke gebruiker een actie initieerde, indien van toepassing.
ActorRoleContextLegt vast vanuit welke rolcontext een actie plaatsvond, indien relevant.
ModuleNameMaakt zichtbaar welke module een logregel of fout produceerde.

Bij cross-module acties moet dezelfde CorrelationId worden doorgegeven aan de betrokken modules en eventuele jobs. Daardoor moet achteraf te reconstrueren zijn welke gebruikersactie welke domeinmutaties, jobs, notificaties en fouten veroorzaakte.

2.17 Security zonder apart securityproject

Er wordt geen apart OefenHub.Security project opgenomen in de basis. Security wordt wel expliciet technisch uitgewerkt.

OnderwerpPrimaire plek
Rate limitingOefenHub.Web, pipelineconfiguratie.
HSTSOefenHub.Web, middlewareconfiguratie.
CSPOefenHub.Web, security headerconfiguratie.
Security headersOefenHub.Web, eventueel via extension methods.
SecretsOmgevingsvariabelen, secret store of hostingconfiguratie; binding in Web/Infrastructure.
AppsettingsConfiguratie-DTO's en options binding in Web/Infrastructure.
Identity-providerintegratieOefenHub.Identity en Web-authenticatieconfiguratie.
Server-side policiesOefenHub.Authorization.
Access-denied loggingAuthorization/Web-pipeline met domeincontext waar relevant.
Verdachte toegangspogingenWeb/Authorization logging, uitbreidbaar via technisch ontwerpbesluit.

Content Security Policy, afgekort CSP, is het beleid waarmee de applicatie vastlegt welke bronnen de browser mag laden of uitvoeren. Dit is relevant voor bescherming tegen onder andere ongewenste scripts en foutieve resource-injectie. HSTS zorgt ervoor dat browsers de applicatie via HTTPS blijven benaderen zodra het beleid actief is.

Als securityconfiguratie in een toekomstige wijziging zo groot wordt dat zij een eigen lifecycle, eigen configuratiemodel of eigen persistente datalaag krijgt, kan een apart securityproject opnieuw worden overwogen. Dat is geen uitgangspunt voor de eerste technische baseline.

2.18 Technische oefenmodules

Concrete oefenmodules worden als eigen projecten toegevoegd volgens de naamvorm:

OefenHub.Modules.<ModuleCategory>.<ModuleName>

Voorbeelden:

OefenHub.Modules.Arithmetic.AdditionSubtractionSimple
OefenHub.Modules.Arithmetic.Fractions
OefenHub.Modules.Language.Spelling

De modulecategorie is een technische ordening. Zij is bedoeld om technische modules vindbaar te houden en hoeft niet één-op-één gelijk te zijn aan de functionele categorieën in de oefencatalogus. Voor V1.0 is Arithmetic vastgesteld als eerste technische modulecategorie en OefenHub.Modules.Arithmetic.AdditionSubtractionSimple als eerste concrete technische oefenmodule.

Concrete oefenmodules gebruiken OefenHub.Modules.Abstractions als primaire afhankelijkheid. Gebruik van OefenHub.SharedKernel is toegestaan wanneer dit duplicatie voorkomt en het gebruikte type aantoonbaar generiek is. Concrete oefenmodules mogen niet afhankelijk worden van OefenHub.Web, OefenHub.Catalog, OefenHub.Practice of interne implementatieprojecten van het platform.

2.19 Testprojecten op solutionniveau

Testprojecten staan onder de solution folder of fysieke map tests. Tests worden primair per module georganiseerd.

TestprojectDoel
<Module>.TestsUnit- en integratietests voor de betreffende module.
<ConcreteExerciseModule>.TestsTests voor concrete technische oefenmodule, inclusief configuratie, generatie, evaluatie en rendering.
OefenHub.Web.TestsbUnit-componenttests voor OefenHub-wrappercomponenten, layouts, page composition, validatie- en fouttoestanden.
OefenHub.EndToEndTestsPlaywright .NET end-to-end smoke- en regressietests voor primaire gebruikersflows.
OefenHub.ArchitectureTestsArchUnitNET-tests voor dependencyregels, verboden projectreferences, modulegrenzen en interne/public grenzen.
OefenHub.IntegrationTestsBewuste cross-module integraties, transaction boundaries, schedulingflows en database-integratie.

Niet ieder moduleproject hoeft exact dezelfde testmappen te hebben. Per project wordt gekozen welke tests nodig zijn om de lading te dekken. Waar nuttig kunnen testprojecten mappen gebruiken zoals Unit, Integration, Contracts en TestData.

2.20 Integratiepunten

De solution bevat meerdere technische integratiepunten. Per integratiepunt leggen de detailhoofdstukken van het Technisch Ontwerp vast welke configuratie per omgeving nodig is, welk foutgedrag veilig is en of het integratiepunt onderdeel is van een kritieke gebruikersflow.

IntegratiepuntTechnische rolKritieke aandachtspunten
Keycloak / identity providerExterne authenticatie en credential lifecycle.OefenHub valideert geen wachtwoorden zelf; interne accountcontext moet na login server-side worden opgebouwd.
SQL databasePrimaire bron voor domeindata, voortgang, relaties, berichten, tickets, jobs en beheerdata.Eén database, schema per persistent moduleproject, migrations per module.
SignalRRealtime transport voor live meekijken, badges en updates.SignalR is transport, niet de bron van waarheid.
TickerQPeriodieke jobs, retries en uitgestelde verwerking.Persistentie in scheduling schema, interne beheerinterface, begrensde retries.
QuestPDFGenereren van resultaat-PDF's.PDF's worden opnieuw uit historische rungegevens opgebouwd en zijn geen permanente brondata.
Tijdelijke PDF-opslagTijdelijke opslag van gegenereerde exports.Cleanup, retentie en privacy moeten expliciet worden ingericht.
Browser storage/cookiesTechnische opslag voor bijvoorbeeld toegankelijkheidsinstellingen vóór login.Geen persoonsgegevens, tokens, autorisatiecontext of rolcontext opslaan.
Logging/monitoringTechnische diagnose, foutafhandeling, correlation en operatie.Geen credentials, tokens of gevoelige payloads loggen.
E-mail of systeemcommunicatiekanaalAlleen indien via Functioneel Ontwerp, Software Requirements Specification en technisch ontwerpbesluit vastgesteld voor notificaties of externe communicatie.Niet veronderstellen als primaire bron zolang functioneel niet vastgesteld.
Interne beheerinterfacesBijvoorbeeld TickerQ-dashboard of technische beheerpagina's.Alleen intern beschikbaar en afgeschermd tegen reguliere gebruikers.

2.21 Deploymentcontext

De eerste technische baseline gaat uit van één deploybare OefenHub-applicatie met één primaire database en een lokale of omgevinggebonden identity-providerintegratie.

OefenHub.Web deployable
bevat references naar moduleprojecten
gebruikt één primaire databaseconnectionstring
registreert module-DbContexts en modulecontracts
host Blazor/Razor UI
host SignalR hubs
registreert TickerQ jobs
configureert security headers, HSTS, CSP en rate limiting

De operationele hoofdstukken leggen de hostingomgeving, CI/CD-pipeline, rollbackstrategie, monitoring, backup en restore vast.

2.22 Afbakening

Dit hoofdstuk legt de solution-opbouw en technische hoofdcomponenten vast. Het beschrijft geen volledige tabeldefinities, geen gedetailleerde EF Core-configuraties, geen volledige Blazor-componentstructuur en geen uitputtend securitybeleid.

Die detaillering hoort in de betreffende detailhoofdstukken. Wanneer uitwerking daar aantoont dat de gekozen projectindeling onvoldoende is, wordt dat als technisch ontwerpbesluit geregistreerd voordat projecten of schema's structureel worden toegevoegd of hernoemd.