I projekt QuestGame fungerer en Quest som en redigerbar skabelon. En Teacher kan oprette en Quest, tilføje QuestItems, ændre spørgsmål, flytte lokationer og justere indholdet, indtil Questet er klar til brug. Og her opstår problemet:
Den samme Quest skal kunne afvikles flere gange.
Hvis en skoleklasse gennemfører en Quest mandag, og Teacher ændrer den samme Quest tirsdag, må ændringerne ikke påvirke den gennemførte afvikling. Besvarelser, point, lokationer og leaderboard fra mandagens spil skal fortsat kunne forstås ud fra den version af Questet, der faktisk blev spillet.
Det førte til spørgsmålet:
Hvordan kan QuestGame bevare historiske data uden samtidig at forhindre Teacher i at redigere og genbruge en Quest?
Problemet med at referere direkte til den redigerbare Quest
Den simpleste datamodel ville være at lade alle spilrelaterede data referere direkte til de eksisterende QuestItems.
En submission, altså en konkret besvarelse af et QuestItem, kunne eksempelvis pege direkte på:
Submission
↓
QuestItem
Det virker umiddelbart logisk, fordi QuestItem allerede indeholder de relevante oplysninger.
Men et QuestItem er jo også redigerbart.
Forestil dig, at en Student besvarer en Quiz med spørgsmålet:
Hvad er Danmarks hovedstad?
og at København giver 10 point.
Efter spillet ændrer Teacher quizzen til:
Hvad er Sveriges hovedstad?
Hvis den historiske besvarelse stadig refererer direkte til det samme QuestItem, vil databasen nu få det til at se ud som om Students gamle svar blev afgivet til det nye spørgsmål.
Dataene eksisterer stadig, men deres betydning er blevet ændret.
Det samme problem opstår, hvis en Location flyttes, en AnswerOption ændres eller et QuestItem slettes.
Det ændrede min opfattelse af problemet fra et spørgsmål om relationer mellem tabeller til sikring af dataintegritet over tid. Altså, hvordan sikrer vi at historiske data bibeholdes, har den rette kontekst og tildelte point forbliver uændrede?
Løsning: En QuestSession som konkret afvikling

Løsningen blev at skelne mellem en Quest og en QuestSession.
En Quest er den redigerbare skabelon:
Quest
↓
QuestItem
↓
Location
En QuestSession repræsenterer derimod én konkret afvikling af denne Quest.
Når en Teacher starter en QuestSession, oprettes der derfor snapshots af de relevante data:
Quest
↓ snapshot
QuestSession
QuestItem
↓ snapshot
QuestSessionItem
Location
↓ snapshot
QuestSessionLocation
Det samme princip gælder Quiz, Task, Activity og AnswerOptions.
En QuestSessionQuiz indeholder derfor det spørgsmål, som eksisterede, da sessionen blev startet, mens QuestSessionAnswerOption indeholder de svarmuligheder og korrekte svar, der gjaldt på det tidspunkt.
Efterfølgende ændringer i den oprindelige Quest påvirker ikke disse kopier.
Backendens ansvar
Snapshot-modellen er ikke alene en databasebeslutning. Backend skal bestemme, hvornår historiske data skal oprettes, og hvilke oplysninger der skal kopieres.
Når en Teacher starter en QuestSession, udfører backend derfor en operation, der konceptuelt kan beskrives sådan:
Teacher starter Quest
↓
Backend henter Quest
↓
QuestSession oprettes
↓
QuestItems kopieres
↓
Locations og subtype-data kopieres
↓
Snapshots gemmes i databasen
Backend håndhæver dermed forretningsreglen om, at en afvikling skal fryses i den tilstand, den havde ved starten.
Databasen sørger derefter for at bevare disse data og deres relationer.
Samspillet mellem de to lag er centralt. Databasen kan godt gemme en historisk kopi, men det er backendens forretningslogik, der afgør, hvornår kopien skal oprettes, og hvad der udgør den relevante historiske tilstand.
Dette relaterer sig til mit individuelle læringsmål om at kunne begrunde, hvordan ansvaret for forretningslogik og datahåndtering hensigtsmæssigt fordeles mellem backend og database.
Submissions skal knyttes til historikken
Snapshot-modellen får især betydning, når systemet senere gemmer submissions.
En QuizSubmission bør ikke pege direkte på den redigerbare Quiz.
Den bør knyttes til:
QuestSession
↓
QuestSessionQuiz
↓
QuizSubmission
På den måde kan systemet altid rekonstruere, hvad Student faktisk blev præsenteret for.
Hvis Teacher senere ændrer den oprindelige Quiz, eksisterer de historiske oplysninger stadig:
QuestSessionQuiz.Question
QuestSessionAnswerOption.Answer
QuestSessionAnswerOption.IsCorrectCode language: CSS (css)
Det gør det også muligt at genberegne eller dokumentere resultater ud fra den korrekte historiske kontekst.
Det samme gælder leaderboard og point. Resultaterne tilhører en konkret QuestSession og ikke den nuværende version af den Quest, som sessionen oprindeligt blev dannet ud fra.
Hvorfor ikke bare låse Quests efter første spil?
En alternativ løsning kunne være at gøre en Quest permanent skrivebeskyttet, så snart den havde været brugt.
Det ville beskytte historikken, men samtidig gøre systemet mindre fleksibelt.
Teacher ville ikke længere kunne rette en stavefejl, flytte et QuestItem eller forbedre en Quiz til næste klasse.
Man kunne også oprette en helt ny Quest manuelt hver gang noget ændres, men det ville være besværligt og skabe unødvendige dubletter.
Snapshot-modellen giver en bedre adskillelse, synes jeg:
Quest
→ må fortsat udvikles og redigeres
QuestSession
→ må ikke ændres historisk
Den redigerbare skabelon og den historiske afvikling får dermed hvert sit ansvar.
Overvejelserne understøtter mit læringsmål om at kunne analysere og begrunde arkitektur- og designvalg gennem sammenligning af alternative løsninger og deres konsekvenser.
En konsekvens for sletning
Arbejdet med QuestSession førte også til en anden opdagelse.
Hvis en Quest har været brugt til at oprette QuestSessions, bør den ikke bare kunne slettes på en måde, der ødelægger relationen til historiske data.
I vores implementering beskyttes historikken derfor mod cascading delete fra Quest.
Hvis der allerede findes QuestSessions, må systemet håndtere Questens livscyklus på en anden måde, eksempelvis gennem arkivering.
Det viser igen, hvordan dataintegritet ikke kun handler om selve snapshot-tabellerne. Backendens regler for redigering og sletning skal passe sammen med databasens relationer og constraints.
Konklusion
Indledningsvist opfattede jeg snapshot-modellen som en praktisk måde at repræsentere en konkret spilafvikling på. Men da jeg begyndte at konkretisere idéen, fandt jeg ud af at den også løser et mere grundlæggende problem: hvordan et system kan bevare sand historik, samtidig med at oprindelig data fortsat må ændres.
Ved at oprette snapshots ved starten af en QuestSession adskilles den redigerbare Quest fra den historiske afvikling.
Backend håndhæver tidspunktet og reglerne for snapshot-processen, mens databasen bevarer den historiske struktur og relationerne mellem data.
For mig er den vigtigste læring, at dataintegritet ikke kun sikres med foreign keys og databaseconstraints. Den afhænger også af, at backendens forretningslogik og datamodellen er designet omkring, hvordan data produceres og udvikler sig over tid.
QuestSession-modellen gør det med andre ord muligt at ændre fremtidige Quests uden utilsigtet at omskrive fortiden.
Denne erkendelse knytter sig til mit kompetencemål om selvstændigt at kunne vurdere og begrunde databasedesign med hensyn til blandt andet dataintegritet og historik samt forstå samspillet mellem databasedesign og backendudvikling.
