Ga naar hoofdinhoud

Projecties

Het gevolgenjournaal legt nauwkeurig vast wat er is gebeurd, maar is in veel gevallen geen praktische bron voor bevragingen. Wie wil weten waar iemand nu woont, wil voor een antwoord niet alle adreswijzigingen sinds 1994 moeten afspelen. Voor het beantwoorden van zulke vragen maken we projecties.

Rol van projecties​

Een projectie is een uit het gevolgenjournaal afgeleide, op de informatiebehoefte van afnemers toegesneden verzameling van projectie-elementen. Een projectie-element is een individuele bewering binnen een projectie. Waar het gevolgenjournaal documenteert wat er is gebeurd, beantwoorden projecties vragen als "leeft persoon p1 nog?", "wat is zijn burgerlijke staat?" en "heeft hij gezag over p3?". Zulke vragen veronderstellen een toestand: een beeld van de wereld dat gedurende een bepaalde periode geldig is.

Niet alle projecties beschrijven een toestand. Een notificatiebericht informeert een afnemer over een verandering die door registratie van een gevolg is veroorzaakt. Net als het onderliggende gevolg is zo'n bericht temporeel atomair: het beschrijft wat er is gebeurd, niet welke toestand na dat gebeuren geldig werd.

Gevolgen en projectie-elementen zijn dus allebei beweringen. Toch hebben ze binnen het register niet dezelfde status. Het gevolg is een 'primair feit', ontstaan als uitkomst van performatief handelen en waarop een bevoegde actor kan worden aangesproken. Een projectie-element is daarvan slechts een afgeleide. Dit betekent dat projectie-elementen, anders dan gevolgen, wél opnieuw van hun invoer (gevolgen) af te leiden zijn. Door gevolgen opnieuw te verwerken (replay) kunnen projecties dus meer dan eens worden opgebouwd.

Afstemming van projecties op de informatiebehoefte van afnemers vereist dat één journaal meerdere projecties kan voeden, elk met een eigen bereik en een eigen vorm. De bijbehorende capability is Onderhouden van projecties op maat: het vermogen meerdere projecties te onderhouden die aansluiten bij de informatiebehoefte van specifieke doelgroepen.

Toesnijden betekent ook begrenzen. Niet elke afnemer mag alles inzien wat een gevolg vastlegt, ook als een deel ervan voor die afnemer wel relevant is. Een projectie kan daarom uit één gevolg alleen opnemen wat een afnemer nodig heeft en mag zien. Of het domein zo'n deel ook als zelfstandig gevolg onderscheidt, is een andere vraag, die het domein zelf beantwoordt, los van afnemerbehoeften (zie het adoptievoorbeeld bij Gevolgen).

Hoe projecties technisch worden bijgehouden, is een implementatiekeuze: ze kunnen vooraf worden berekend en opgeslagen, of op het moment van bevraging op basis van de opgegeven parameters worden gegenereerd. Het tweede deel van dit hoofdstuk, vanaf de ontwerpafwegingen, gaat over die keuze.

Projectieperspectief: toestand of gebeuren?​

Een projectie-element is een afgeleide bewering, en kan elk van de vier typen toestand of gebeuren beschrijven. Uit gevolgen, die altijd een gebeurtenis beschrijven, zijn toestandsbeweringen af te leiden: gebeurtenissen begrenzen toestanden, een lopend traject levert een toestand van het betrokken ding, en op een voltooiing volgt een nieuwe toestand. Ook gebeurtenissen en trajecten zijn eruit af te leiden. Het type van een projectie-element, en daarmee de vorm van de werkingstijd, wordt dus in de projectie toegekend en niet in het journaal.

Projectiedoel bepaalt perspectief​

Of een projectie-element een entiteit als toestand of als gebeuren beschrijft, staat los van de manier waarop de gevolgen zijn vastgelegd. Het perspectief wordt niet bepaald door het vastleggingsperspectief van het bronmateriaal, maar door de manier waarop we naar dat bronmateriaal willen kijken, en in het verlengde daarvan door de informatiebehoefte die de projectie invult. Uit dezelfde gevolgen kunnen voor de ene afnemer toestandsprojecties worden geproduceerd, en voor de andere gebeurenprojecties.

Afleiden is interpreteren​

Een projectie-element is geen kopie van een gevolg; er zit altijd bewerking tussen. Een toestandsbewering ontstaat door gebeurtenissen te interpreteren: wat geldt op een moment, volgt uit wat er tot dan toe is gebeurd, en ook uit wat er nog niet is gebeurd.

Ook een gebeurenprojectie neemt gevolgen niet één op één over, al beschrijven die zelf gebeurtenissen. Het verschil zit in het perspectief. Het journaal beschrijft vanuit bijhoudingsperspectief zo nauwkeurig mogelijk wat er binnen een bounded context gebeurt, rond het begrip dat bij de bijhouding centraal staat. Een projectie kan het gebeurde opnieuw ordenen rond een ander begrip, waar een afnemer naar kijkt. Een gevolg 'bezwaar ontvangen' wordt zo, in de levensloop van een vergunning, 'vergunning door bezwaar betwist'. Een gevolg dat vóór het begin van die levensloop ligt, zoals het ontvangen van de aanvraag, kan in zo'n projectie ontbreken.

Welke werkingstijd en systeemtijd voor afnemers relevant zijn, verschilt per bewering; zie Beweringen naar werkingstijd en systeemtijd.

Toestandsprojecties en gebeurenprojecties​

Het registermodel vertaalt het onderscheid tussen toestand en gebeuren naar twee hoofdvormen van projecties. In een toestandsprojectie beschrijven de projectie-elementen toestanden; in een gebeurenprojectie beschrijven de projectie-elementen gebeurens. Dit onderscheid bepaalt welke vragen een afnemer aan een projectie kan stellen. Op basis van een toestand kan worden gevraagd wat er op enig moment gold; van een gebeuren kan worden gevraagd wanneer het plaatsvond en wat eraan voorafging.

Wat een projectie verder bevat, legt de projectiespecificatie vast als het bereik van die projectie. Daarin zitten onder meer twee temporele keuzes: welke werkingstijd wordt meegenomen (alleen wat actueel geldt, of ook wat ooit gold) en welke onderschrijving (alleen wat het register nu onderschrijft, of ook wat het ooit onderschreef). Uit combinaties van die keuzes ontstaan vijf veelvoorkomende patronen, de projectie-archetypen.

Archetypen van toestandsprojecties​

ArchetypeDefinitieToelichting
Actueel geldige toestandToestandsprojectie bestaande uit de projectie-elementen die het register nu onderschrijft en die de in de werkingstijd actueel geldige toestand beschrijven.Beantwoordt de vraag wat nu geldt, zoals het huidige woonadres van een persoon. De meeste bevragingen kunnen door deze projectie worden beantwoord.
In domein geldige historische toestandenToestandsprojectie bestaande uit de projectie-elementen die het register nu onderschrijft en die in de werkingstijd ooit geldige toestanden beschrijven.Geeft de geschiedenis zoals het register die nu ziet: wat ooit gold, zonder wat later is onttrokken. Nodig voor vragen als "waar woonde deze persoon op 1 januari 2020?".
Alle historische toestandenToestandsprojectie bestaande uit de projectie-elementen die het register ooit heeft onderschreven en die in de werkingstijd ooit geldige toestanden beschrijven.Bevat ook wat later is onttrokken. Nodig voor verantwoording: een afnemer die handelde op basis van een gegeven dat later onjuist bleek, moet kunnen aantonen dat het register dat gegeven ooit juist achtte.

Archetypen van gebeurenprojecties​

ArchetypeDefinitieToelichting
Onderschreven verloopGebeurenprojectie bestaande uit de projectie-elementen die het register nu onderschrijft.Geeft weer wat er volgens het register nu is gebeurd. Geschikt als basis voor notificaties over wat in het domein is voorgevallen.
Volledig verloopGebeurenprojectie bestaande uit de projectie-elementen die het register ooit heeft onderschreven.Bevat ook de gebeurens die het register later niet meer onderschrijft. Daarmee is de bijhoudingsgeschiedenis van het register volledig te volgen, inclusief wat wanneer is onttrokken.

Hoe deze archetypen te implementeren zijn, staat verderop bij de implementeringsvormen per projectie-archetype.

Tijd in projecties​

Bij toestandsprojecties en gebeurenprojecties horen verschillende vormen van werkingstijd: geldigheidstijd voor toestanden en voltrekkingstijd voor gebeurens, elk in de vorm die bij het type van de bewering hoort. Een toestandsprojectie bevraag je met parameters als geldigOp, geldigVanaf of geldigTot; een gebeurenprojectie met een voltrekkingsperiode, zoals gebeurdVanaf en gebeurdTot. Daarnaast draagt elk projectie-element een eigen systeemtijd, het projectietijdstip. Het hoofdstuk Tijd werkt dit uit, inclusief de oorsprong van het begrip geldigheidstijd en het onderscheid met voltrekkingstijd.

Projecties opbouwen​

De functie Projecteren werkt een gevolg door in een projectie. Projecteren leest daarvoor drie dingen: het gevolg, de projectiespecificatie die per projectie beschrijft hoe gevolgen daarin doorwerken, en de projectie zelf, omdat het bepalen van de verandering die een gevolg binnen een toestandsprojectie teweegbrengt veelal kennis van de bestaande of 'huidige' stand vereist. De projectiespecificatie werkt de inwerkingsregel uit het bounded contextmodel uit.

De opbouw van projecties loopt daarmee parallel aan die van het gevolgenjournaal:

GevolgenProjecties
Aanleidingcommandogevolg
Specificatievormingsspecificatieprojectiespecificatie
Huidige standopnametoestandprojectie
Resultaatgevolg in het gevolgenjournaalprojectie-element in een reeks

Projecteren bestaat uit twee deelfuncties. Openen opent een reeks voor een entiteit die nog niet in de projectie voorkwam. Bijwerken produceert een projectie-element. Door het bijwerken van projecties altijd via deze twee standaardoperaties te laten verlopen, ongeacht het soort gevolg dat wordt verwerkt, verkleint het register de kans op verwerkingsfouten. Elke verwerking volgt hetzelfde patroon; alleen de projectiespecificatie bepaalt hoe een concreet gevolg doorwerkt. Deze standaardisatie is een aanbevolen ontwerppatroon voor robuuste projectieopbouw.

Een reeks is het geheel van projectie-elementen binnen één projectie die samen een entiteit beschrijven. De reeks groepeert wat over dezelfde entiteit is vastgelegd en is daarmee de plek waar de identiteit uit het gevolgenjournaal doorwerkt. Ook elementen die over een bewering gaan, horen bij de reeks. "Bewering b1 over de bewoning van adres a1 door persoon p1 is onttrokken" beschrijft p1 niet, maar zonder dat element klopt de bewoningsgeschiedenis van p1 niet.

Een projectie-element draagt alleen de tijd van zijn eigen productie en wat zijn grond over de werkingstijd zegt. Wanneer een toestand ophoudt, blijkt uit een volgend element in de reeks. Het einde in werkingstijd volgt uit de volgende toestand van de entiteit, het einde van de onderschrijving uit een element dat de onttrekking weergeeft. Een tabel met kolommen voor begin en einde registratie is een manier van opslaan die meerdere elementen uit één reeks samenbrengt.

Een gevolg legt één besluit of gebeuren vast. Dat besluit kan bestaan uit deelbesluiten, maar het blijft één besluit, dat (voor zover relevant voor de projectie) in zijn geheel wel of niet in een projectie wordt verwerkt. Zie transactionaliteit bij de ontwerpafwegingen.

Voor meer details, zie de verdiepingen temporaliteit in projecties en bijwerken van projecties.

Correctie en onttrekking in projecties​

In de meeste gevallen is een verstrekt projectie-element onjuist omdat één of meerdere gevolgen waarop het steunt onjuist waren. Herstel begint dan in het gevolgenjournaal, met een correctiegevolg. Projecteren werkt dat gevolg door zoals elk ander gevolg: het voegt projectie-elementen toe. Een element dat de onttrekking weergeeft, beëindigt de onderschrijving van het onjuiste element; nieuwe elementen beschrijven met terugwerkende kracht hoe het had moeten zijn.

Bestaande projectie-elementen blijven daarbij behouden; de nieuwe elementen worden ernaast geplaatst, met een later systeemtijdstip. Het register overschrijft nooit, als temporele consequentie van het append-only-patroon.

Ook kan uit onderzoek blijken dat de gevolgen juist waren en de fout zat in het projecteren. Dan wordt de projectiespecificatie of de software die haar uitvoert hersteld, waarna de gevolgen volledig of selectief opnieuw worden verwerkt (replay). Ook in dit scenario wordt bepaald vanaf welk moment een bestaande foutieve toestand niet langer wordt onderschreven; de foutieve projectie-elementen worden opnieuw opgebouwd, en de nieuwe elementen komen naast de oude te staan. Een nieuw gevolg is hiervoor niet nodig. Dat de nieuwe elementen met een herstelde werkwijze zijn afgeleid, blijkt uit hun herkomst. Zie het voorbeeld projectiefout hersteld via replay.

Of een afnemer de onttrekking ziet, hangt af van het archetype van de projectie. Projecties die alleen bevatten wat het register nu onderschrijft – de actueel geldige toestand, de in domein geldige historische toestanden en het onderschreven verloop – laten het onttrokken element weg. Voor wie zo'n projectie bevraagt, is het resultaat voor onttrokken gegevens hetzelfde als voor gegevens die nooit hebben bestaan. Projecties die ook bevatten wat het register ooit onderschreef – alle historische toestanden en het volledig verloop – behouden het onttrokken element, naast het element dat de onttrekking weergeeft. Daarmee blijft de herhaalbare vraag beantwoordbaar en kan een afnemer aantonen dat het register een gegeven ooit juist achtte. Toegang tot die projecties kan worden beperkt, bijvoorbeeld tot juridisch of historisch onderzoek.

Ontwerpafwegingen​

Temporaliteit​

Een belangrijke factor voor een projectie is de keuze voor wat betreft temporaliteit. Voor het kunnen beantwoorden van de herhaalbare vraag is bitemporaliteit een eis: als ingegrepen wordt op de tijdlijn, bijvoorbeeld als gevolg van een correctie, dient de projectie nog steeds de oorspronkelijke toestand te kunnen reproduceren.

Bitemporaliteit brengt op zichzelf echter complexiteit met zich mee die niet in alle gevallen nodig is. Wanneer het kunnen stellen van de herhaalbare vraag minder of niet van belang is, zou een specifieke temporele projectie op de geldigheidstijdlijn of alleen een actuele toestandsprojectie ook kunnen voldoen.

Een implementatiegerichte uitleg van verschillende vormen van temporaliteit in projecties is hier te vinden.

Eventual consistency als ontwerpoverweging​

Tussen het vastleggen van een gevolg in het gevolgenjournaal en de zichtbaarheid van het resultaat in een projectie zit verwerkingstijd. Bij synchrone verwerking is die verwaarloosbaar; bij asynchrone of gedistribueerde architectuur kan het venster significant zijn. Gedurende dat venster kijkt een afnemer naar een projectie die het meest recente gevolg nog niet weerspiegelt.

Deze eventual consistency gaat uitsluitend over tijdigheid, niet over logische consistentie. Gevolgen zijn er juist op gericht om een eenduidige handeling als één samenhangend geheel vast te leggen, en ondersteunen daarmee de logische consistentie van het gevolgenjournaal. Een projectie die het meest recente gevolg nog niet weerspiegelt, is dus niet tegenstrijdig met wat al is vastgelegd – de informatie is alleen nog niet doorgekomen.

Dit is geen fout maar een eigenschap van de architectuur: gevolg en projectie leven in gescheiden stores, elk met een eigen systeemtijdstip. De inherente beperkingen aan de herhaalbare vraag benoemen dit als grens van herhaalbaarheid; hier benoemen we het als ontwerpkeuze. Bij het ontwerp van een projectie moet worden afgewogen hoeveel verwerkingstijd acceptabel is en welke consequenties dat heeft voor de garanties die aan afnemers worden geboden.

Query-complexiteit versus query-flexibiliteit​

De keuze van gegevensrepresentatie van een projectie en de gewenste informatie die een afnemer wenst, bepalen de complexiteit van de (database)query die gedaan moet worden om in een informatiebehoefte te voorzien. In het voorgestelde patroon, waarin we commando's, gevolgen en projecties van elkaar scheiden, hebben we niet meer de 'ballast' van de commandokant, en kunnen we onze projecties specifieker optimaliseren voor de afnemer(s).

Hier zijn echter nog steeds afwegingen in te maken. Een projectie kan gebouwd worden op een manier dat de payload response van een query as is (in bijvoorbeeld een json- of xml-structuur) opgeslagen wordt in de database. We noemen dit de bitemporele opslag van snapshots, kortweg snapshotting. Elke snapshot combineert daarin de payload met metadata over herkomst en tijd. Deze wijze van persisteren heeft een aantal voordelen:

  1. Dit is de best mogelijke manier om de herhaalbare vraag te kunnen beantwoorden: de payload wordt as-is opgeslagen, en (later) veranderende code beïnvloedt deze payload niet.
  2. Omdat de structuur van het bericht vastligt, ook nadat er codewijzigingen gedaan worden, kan er in terugmeldingen en onderzoeken via bijvoorbeeld xpath of jsonpath hard verwezen worden naar entiteiten of velden waarover getwijfeld wordt.
  3. De (database) opvraging is eenvoudig: op basis van tijdstippen en een index dient een payload opgezocht te worden. Dit voorkomt grotendeels het verstrekken van foutieve informatie als gevolg van foutief geïmplementeerde queries.

Zeker in combinatie met bitemporaliteit zien wij snapshotting als een goede keuze voor wat betreft informatieverstrekking. Bitemporaliteit kan, in combinatie met bijvoorbeeld ingewikkelde correcties, zeer ingewikkelde situaties opleveren die zelfs voor experts moeilijk te doorgronden zijn. Het feit dat de query eenvoudig is en de payload statisch is bij snapshotting, maakt dat deze projectievorm uitermate robuust is voor het beantwoorden van de herhaalbare vraag.

Snapshotting heeft echter ook nadelen: omdat de datastructuur zo rigide vastligt kun je moeilijker, of niet, meerdere soorten vragen beantwoorden met dezelfde projectie. Ook leent deze vorm van opslag zich minder voor joins (het combineren van verschillende payloads/entiteiten). Dit alles maakt deze structuur minder flexibel, en dit dwingt je tot het maken van veel zeer afnemerspecifieke projecties.

Het bitemporele tabelmodel kan naargelang behoefte dan een goede keuze zijn. Een relationeel model, en zeker een bitemporeel relationeel model, is veel flexibeler in het kunnen beantwoorden van allerhande soorten vragen. De aard van de datarepresentatie maakt echter wel de queries en de mapping naar de doelstructuur van de payload complexer, wat het garanderen van herhaalbaarheid van vragen doet afnemen.

Betrouwbare modellen versus onbetrouwbare realiteit​

Een goed datamodel beschrijft structuren, entiteiten en properties van een domein. Een huisnummer is een getal, terwijl een adres een straatnaam bevat als string met een maximaal aantal karakters. Deze eigenschappen worden idealiter goed gevalideerd aan de commandokant, en door dat te doen kun je duidelijke contracten afspreken met je afnemers over wat voor data je verstrekt en in welke vorm. Dergelijke modellen bepalen databasestructuren en beperkingen voor wat betreft gegevensopslag.

De realiteit is echter vaak weerbarstig: ooit verkeerd geïmplementeerde validaties, oude data uit migraties of veranderingen in het datamodel naar verloop van tijd kunnen er in veel gevallen voor zorgen dat de data die je hebt niet altijd voldoet aan het voorgeschreven gegevensmodel. Huisnummers met letters, te lange straatnamen... Als je gebonden bent aan het principe van het kunnen beantwoorden van de herhaalbare vraag zal deze data altijd foutief blijven.

Een relationeel model biedt duidelijkheid over het datamodel, maar is minder flexibel als het gaat om data die niet aan dit model voldoet. Er moet altijd een keuze gemaakt worden: voor zeldzame uitzonderingen dient het model aangepast te worden, of deze uitzonderingen kunnen niet verwerkt worden. Hetzelfde probleem geldt vaak voor berichtvalidaties via bijvoorbeeld XSD's.

Een verhuizing niet doorgeven omdat een straatnaam te lang is, vinden wij zeer onwenselijk. Afhankelijk van de datakwaliteit kan het dan een betere keuze zijn om data-opslag en berichtverkeer flexibeler te maken zodat afwijkingen van het datamodel wel opgeslagen en gecommuniceerd kunnen worden. Richting een afnemer kan het dan wenselijk zijn deze communicatie te doen met een extra melding: "in dit bericht voldoen we niet aan het datamodel".

Generieke versus specifieke implementatie​

TODO

Dit moet nog verder uitgewerkt worden

Varianten​

Hieronder volgt een niet-uitputtende lijst van varianten van projecties die gebruikt kunnen worden. Elke projectievariant heeft voor- en nadelen die, afhankelijk van de use case, sterker of minder sterk gelden. Het is niet ons doel om te stellen welk van deze varianten de voorkeur geniet. Wel willen we voor- en nadelen van projectievarianten benoemen.

TemporaliteitRelationele databasePayloadsProjectiegevolgen
actueel
temporeel
bitemporeel

Transactionaliteit​

Een gevolg legt één besluit of gebeuren vast. Dit besluit kan bestaan uit deelbesluiten of anderszins opdeelbare elementen, maar uiteindelijk betreft het nog steeds één besluit, dat (voor zover relevant voor de projectie) in zijn geheel wel of niet verwerkt dient te worden in een projectie, wat in databasetermen betekent dat een gevolg altijd in precies één databasetransactie verwerkt dient te worden. Het verwerken van een door de business erkend gevolg in meerdere (database)transacties kan er namelijk toe leiden dat datacorruptie optreedt: als een gevolg in twee transacties verwerkt wordt, zou de tweede transactie mis kunnen gaan, waarbij een ongeldige situatie ontstaat.

De implementatie-uitdaging bij projecties​

Om van gevolg te komen tot een opvraagbare projectie hebben we een aantal transities nodig, waarbij meestal een vorm van opslag een tussenvorm is tussen het gevolg en wat de afnemer uiteindelijk op zijn scherm ziet. Een simpele flow chart van dit proces ziet er als volgt uit:

Uitgaande van deze architectuur hebben we in feite twee implementatie-uitdagingen:

  1. Hoe vertalen we de gevolgen naar een opgeslagen projectierepresentatie?
  2. Hoe vertalen we de opgeslagen projectierepresentatie (herhaalbaar) naar een leverbaar artefact?

Bij beide uitdagingen hebben we een implementatievorm nodig die robuust te bouwen is zodat deze een lange periode, mogelijk tientallen jaren, betrouwbaar herhaalbare vragen kan beantwoorden. En in die periode hebben we te maken met software die continu blijft evolueren, waar regelmatig (wekelijks, of zelfs dagelijks) nieuwe features aan toegevoegd worden, en de technologie je om de zoveel jaar stuurt naar software- of datamigraties.

Zowel een gevolgenjournaal als een bitemporele store bewaart data (grotendeels) append-only: terwijl software veranderlijk is, blijft onze data behoorlijk statisch. We hebben dus vooral te maken met de veranderlijkheid van software. Hoe garandeer je dan de herhaalbare vraag? Bugs in deze software zijn ook onvermijdelijk. Hoe garandeer je hetzelfde (corrupte) antwoord na het oplossen van een bug? Zoals eerder benoemd kent herhaalbaarheid zijn grenzen, maar welke implementatiekeuzes passen het beste bij dit vraagstuk?

Door bij het opslaan van de transformatie van de gevolgen in de projectiestore te streven naar standaardisatie kunnen we fouten voorkomen: een bitemporele store kunnen bijwerken met standaardoperaties, onafhankelijk van het soort gevolg, en aan de andere kant willen we herhaalbare, simpele queries hebben zodat de kans op softwarefouten bij het beantwoorden van de herhaalbare vraag zo klein mogelijk is. De standaardoperaties voor het opbouwen van een projectie zijn hierboven besproken; ze zijn implementatietechnisch uitgewerkt in de verdieping van het bijwerken van projecties. Als elke projectie en elk gevolg afgehandeld wordt door een reeds eerder geïmplementeerde functie verkleint dat de kans op softwarefouten. Als we de transitie van gevolg naar projectie zoveel als mogelijk doen aan de 'linkerkant', kunnen we beter herhaalbaar leveren: ook de meeste softwarefouten 'persisteren'.

Aan de rechterkant van onze store kunnen we streven naar 'zo min mogelijk' software voor de verstrekking van ons product. Als opvragingen uit onze store eenvoudig zijn, is de kans dat daar softwarefouten in zitten kleiner. Onze architectuur ziet er dan als volgt uit:

Het vereenvoudigen van de opvragingssoftware is niet zonder prijs. Er zal per projectie afgewogen moeten worden welke criteria het zwaarst wegen. De volgende paragraaf gaat daarover.

Implementeringsvormen per projectie-archetype​

We onderscheidden hierboven vijf archetypische projecties: aan de toestandskant de actueel geldige toestand, in domein geldige historische toestanden en alle historische toestanden, aan de gebeurenkant het onderschreven verloop en het volledig verloop. Deze archetypen zeggen wat een projectie bevat; ze zeggen niet hoe je die bouwt. Per archetype zijn meerdere implementeringsvormen mogelijk, die langs twee assen variëren:

  • Temporaliteit: alleen de actuele stand, werkingstijd zonder systeemtijd, of werkingstijd en systeemtijd samen (bitemporeel).
  • Opslag- en leveringsvorm: rijen in relationele tabellen, payloads, een hybride vorm van cellen en payload, of een stream die het register zelf uitgeeft.

Implementeringsvormen zijn geen indeling van beweringen: ze gaan over de bouwvorm van de projectie, niet over wat de projectie-elementen beschrijven (zie Beweringen naar werkingstijd en systeemtijd).

Deze paragraaf geeft een overzicht van de implementatievormen per archetype. De afwegingen tussen de vormen staan in de ontwerpafwegingen hierboven; de beslisboom hieronder leidt van de ontwerpvraag naar één van deze vormen.

Actueel geldige toestand​

Voor de afnemer die alleen de stand van nu nodig heeft, komen twee vormen in aanmerking:

  • Actuele tabellen: één toestand per entiteit die bij elke nieuwe toestand wordt overschreven. De eenvoudigste en snelste vorm; historie ontbreekt volledig. Deze vorm kan volledig relationeel gemaakt worden.
  • Actuele payload: een payload per entiteit, eveneens herschreven bij elke nieuwe toestand.

Geen van deze vormen kan de herhaalbare vraag beantwoorden: een correctie in de werkingstijd schrijft het verleden stilzwijgend over en het eerdere antwoord is weg.

In domein geldige historische toestanden​

Wie enkel vragen over de domeinhistorie wil beantwoorden – "wat gold er op moment t?" – legt geldigheidsperiodes vast:

  • Temporele tabellen: per rij een geldigVanaf- en geldigTot-waarde, met bij elke bijhouding een gesloten oud-periode en een nieuw-op-volg-periode. Vragen naar wat er toen gold zijn flexibel te beantwoorden; vragen naar wat het register toen onderschreef niet, want een correctie met terugwerkende kracht herschrijft de historie.
  • Temporele payloads: payloads per geldigheidsperiode, in dezelfde hoedanigheid als hierboven, maar dan per geldigheidsmoment bewaard.

Deze vorm is passend zolang het antwoord op "wat gold er?" mag meebewegen met wat het register op dit moment voor juist houdt.

Alle historische toestanden​

Dit archetype beantwoordt zowel "wat gold er?" als "wat onderschreef het register toen?", en is per definitie bitemporeel. De keuze uit de beslisboom over opslagvorm komt hier op neer:

  • Bitemporeel tabelmodel (zie bitemporele relationele databases): alle gegevens in cellen, geldigheid en registratie naast elkaar. Maximale query-flexibiliteit; herhaalbaarheid leunt dan voor een (groot) deel op query's en code die over de jaren hetzelfde gedrag moeten blijven vertonen.
  • Bitemporele opslag van snapshots (zie bitemporele opslag van snapshots): per toestandsprojectie-element wordt de payload zoals deze verstrekt kan worden, vastgelegd, per geldigheids- en systeemtijdstip. De query wordt eenvoudig en reduceert tot het opzoeken van één payload op basis van id en tijdstippen; de prijs is een eigen projectie per informatievraag.
  • Hybride oplossing: opslag van payloads, maar dan met extra cellen om in queries te kunnen filteren en te aggregeren.

Volledig verloop​

Het volledig verloop bevat ook de elementen die het register ooit onderschreef maar inmiddels niet meer. Dit verloop kan op twee verschillende manieren verstrekt worden:

  • Als continue stroom van notificaties: elk gevolg (of een subset daarvan) leidt tot één of meerdere notificaties die direct aan afnemers geleverd worden.
  • Als opvraagbare reeks per entiteit of entiteiten.

Omdat al het gebeuren gecommuniceerd wordt inclusief correcties, resulteert deze wijze van leveren in een bitemporeel beeld van het register.

Deze vorm lijkt het sterkst op het gevolgenjournaal zelf – en is het toch niet. Het journaal beschrijft vanuit bijhoudingsperspectief wat er binnen de bounded context gebeurde; een projectie vertaalt die naar een afgeleid gebeuren waar de afnemer naar kijkt, zoals Afleiden is interpreteren beschrijft.

Onderschreven verloop​

Het onderschreven verloop lijkt qua vorm sterk op het volledige verloop, en dus ook op het gevolgenjournaal, maar is dit niet. De vorm is hetzelfde, maar de inhoud niet. Niet langer onderschreven gevolgen worden gefilterd, correcties voegen nieuwe gebeurens toe. Het onderschreven verloop is dus expliciet een gebeuren-projectie.

Het verloop van gebeurenprojectie-elementen die het register nu onderschrijft kan je niet streaming leveren, enkel op verzoek (per entiteit of entiteiten). Indien er behoefte is aan herhaalbaarheid voor deze vraag dienen deze onderschreven gebeurenprojecties bitemporeel vastgelegd te worden bij elk gevolg dat met terugwerkende kracht werkt.

Beslisboom: perspectief, temporaliteit en opslagvorm​

Nu we verschillende implementatievormen en hun kenmerken beschreven hebben is het zaak om te weten welke variant we onder welke omstandigheden zouden kiezen. Om hierbij te helpen hebben we een 'beslisboom' geformuleerd, die leidt tot een projectiekeuze.

Vanuit uit betrouwbare bron kunnen wij niet alle domeinspecifieke context meewegen. Beschouw deze boom daarom als een hulpmiddel om tot een keuze te komen en niet meer dan dat.

Beslisboom: welke projectievorm kies ik?​

Boom 2: bitemporele toestandsprojectie​

Boom 3: opvragen van gebeurenprojectie​

Beweringen naar werkingstijd en systeemtijd​

Werkingstijd en systeemtijd zijn niet voor elke bewering even relevant. Zet je de vorm van de werkingstijd (geldigheid, voltrekking of geen) af tegen de vraag of systeemtijd voor afnemers ertoe doet, dan ontstaan acht combinaties. De bovenste rij bevat de vier typen beweringen; het herlevende en het niet-herlevende ding in de afbeelding zijn de herlevende en de eenmalige toestand. Daarnaast zijn er vier bijzondere gevallen, die hieronder kort worden getypeerd.

Beweringen naar werkingstijd en systeemtijd

De indeling gaat over wat in het normale verkeer aan afnemers wordt getoond. Betrouwbaar beheer en navolgbaar foutherstel maken het in vrijwel alle gevallen gewenst systeemtijd bij te houden – dus ook voor specificatiegegevens, automatische observaties en vaste waarden.

Systeemevent​

Een gebeurenbewering over een gebeurtenis die uitsluitend binnen het systeem plaatsvindt en geen tegenhanger heeft in de werkelijkheid daarbuiten. Werkingstijd ontbreekt; alleen systeemtijd is relevant. Voorbeelden: een logregel, een audit trail entry, een systeemmelding.

Specificatiegegeven​

Een toestandsbewering over een entiteit met beperkte veranderlijkheid, die de kaders beschrijft waarbinnen andere, meer volatiele gegevens zich bewegen. De systeemtijd daarvan hoeft afnemers daarom doorgaans niet te worden getoond. Voorbeelden: een zaaktype, een producten- en dienstencatalogus, een classificatie.

Automatische observatie​

Een gebeurenbewering die zonder menselijke tussenkomst wordt vastgelegd op het moment van plaatsvinden, waardoor systeemtijd en voltrekkingsmoment per definitie samenvallen. Voorbeelden: een sensormeting, een GPS-track, een automatische transactie.

Vaste waarde​

Een toestandsbewering zonder werkingstijd. De waarde staat onveranderlijk vast en kent voor afnemers geen geldigheids- of registratiehistorie. Voorbeelden: een landcode, een geslachtsaanduiding, een natuurconstante.