Ik heb een vraag m.b.t. het ontwerpen van events, waar ik namelijk vastloop is wat je in een event stopt en wat mogelijke metadata kan zijn. Om hier een beetje mee te spelen heb ik een CQRS+Event sourcing constructie opgezet, waarbij commands worden uitgevoerd op een model en deze events creëert die worden opgeslagen in een soort event store. Met deze events wordt later een read model gemaakt. Dit om een beetje te spelen met de concepten en te ondervinden wat er in een event moet komen, zodat later het read model goed opgebouwd kan worden (of opnieuw afgespeeld te worden in een ander model).
Voor nu heb ik de use case genomen van een takenlijst waarbij gebruikers een taak kunnen maken, hierop (en op die van andere) kunnen reageren en de staat kunnen aanpassen (bv, starten, in de wacht zetten, afronden of terugtrekken). Als ze de staat veranderen dan kunnen ze dit met of zonder commentaar doen. Dus als ik naar de requirement kijk van dat een gebruiker op een taak kan reageren dan lijkt het mij aardig simpel. Er moet een functie komen waarbij iemand een commentaar geeft op de taak, dus:
Het ValueObject\User bevat eigenlijk een referentie naar een AggregateRoot van de context User, op dit moment alleen een UserId, omdat meer opslaan niet nodig is. En uiteraard de string waarin de content van de reactie zit. In het event zitten dus deze twee waardes, samen met de GUID van het commentaar (of de class Task verantwoordelijk is voor het genereren van dit Id, daar heb ik ook nog twijfels bij). Maar het event geeft in ieder geval aan, wie heeft een reactie geplaatst en wat is de content hiervan.
Echter, in de front-end wil ik ook wel een datum/tijd plaatsen om aan te geven wanneer deze reactie is geplaatst. Nu kan dit in het event, maar dit wordt ook als metadata opgeslagen bij het event. Wat is nu het beste om hierbij te doen? Om de datum mee te nemen in het event, of kan het er buiten, want voor het model boeit hier echt iets.
En dan situatie nummer twee, een gebruiker veranderd de staat van een event en geeft hierbij aan waarom. Als ik gebruik maak van de metadata dan kom ik met de volgende constructie weg:
Als de comment een waarde heeft, dan wordt er in het event wat meer informatie opgeslagen, anders alleen het event dat de taak is gestart. Opzich hoeft de taak niet echt te weten wie de taak start, maar stel ik wil het op een tijdlijn zichtbaar krijgen dan wil ik wel weten wie dat heeft gedaan. Dus de gebruiker uit de metadata halen dat is dan wel voldoende.
Echter als ik later de requirement ga vaststellen: "Gebruikers mogen alleen hun eigen reactie's wijzigen", dan heb ik die informatie wel in mijn domein model nodig. Dus zal het ook weer een onderdeel moeten worden van mijn events. Is het dan zo dat als je het in je domein model nodig hebt, dat het daardoor ook leefrecht heeft in je events? En zoja, als ik dit event dan modelleer is structuur hierbij dan van belang? Het hele comment is eigenlijk een toevoeging aan het event, maar zijn wie commentaar geeft is niet direct een parameter van de staat verandering. Dus als voorbeeld:
Ergens zou ik de structuur terug willen zien, want dan weet je ook waar de attributen bij horen. Als ik hierboven kijk dan weet ik dat het Id bij het reactie hoort, dat bij de gebeurtenis is aangeleverd en niet verward wordt door dat hij op een verkeerd niveau staat. Of is deze structuur een overkill voor wat het event allemaal te vertellen heeft?
Zoals je ziet heb ik nogal wat vragen over hoe je een event opbouwt. Hoe maak je de beslissing wat er in een event komt en wat niet? Is het gebruik van metadata een correcte, of is dit eerder een bad-practice? Mogen events complexe structuren bevatten, of kan het best met een platte structuur functioneren? Zijn er regels hoe je events moet opbouwen, of best practices? Want zelfs op z'n relatief simpel voorbeeld loop ik op sommige vlakken vast.
Voor nu heb ik de use case genomen van een takenlijst waarbij gebruikers een taak kunnen maken, hierop (en op die van andere) kunnen reageren en de staat kunnen aanpassen (bv, starten, in de wacht zetten, afronden of terugtrekken). Als ze de staat veranderen dan kunnen ze dit met of zonder commentaar doen. Dus als ik naar de requirement kijk van dat een gebruiker op een taak kan reageren dan lijkt het mij aardig simpel. Er moet een functie komen waarbij iemand een commentaar geeft op de taak, dus:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class Task extends Aggregateroot { public function addComment(ValueObject\User $commentator, string $comment) { if ($commentator === null) { throw new \Exception("Attribute: 'commentator' may not be null."); } if (empty($comment)) { throw new \Exception("Attribute: 'comment' may not be an empty string."); } $this->applyChange(new Event\CommentAddedEvent( Guid::newGuid(), $commentator->getId(), $comment )); } } |
Het ValueObject\User bevat eigenlijk een referentie naar een AggregateRoot van de context User, op dit moment alleen een UserId, omdat meer opslaan niet nodig is. En uiteraard de string waarin de content van de reactie zit. In het event zitten dus deze twee waardes, samen met de GUID van het commentaar (of de class Task verantwoordelijk is voor het genereren van dit Id, daar heb ik ook nog twijfels bij). Maar het event geeft in ieder geval aan, wie heeft een reactie geplaatst en wat is de content hiervan.
Echter, in de front-end wil ik ook wel een datum/tijd plaatsen om aan te geven wanneer deze reactie is geplaatst. Nu kan dit in het event, maar dit wordt ook als metadata opgeslagen bij het event. Wat is nu het beste om hierbij te doen? Om de datum mee te nemen in het event, of kan het er buiten, want voor het model boeit hier echt iets.
En dan situatie nummer twee, een gebruiker veranderd de staat van een event en geeft hierbij aan waarom. Als ik gebruik maak van de metadata dan kom ik met de volgende constructie weg:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
| class Task extends Aggregateroot { public function startWithComment(string $comment) { // logic for valid state change if (empty($comment)) { $this->applyChange(new Event\TaskStartedEvent()); } else { $this->applyChange(new Event\TaskStartedEvent(Guid::newGuid(), $comment)); } } } |
Als de comment een waarde heeft, dan wordt er in het event wat meer informatie opgeslagen, anders alleen het event dat de taak is gestart. Opzich hoeft de taak niet echt te weten wie de taak start, maar stel ik wil het op een tijdlijn zichtbaar krijgen dan wil ik wel weten wie dat heeft gedaan. Dus de gebruiker uit de metadata halen dat is dan wel voldoende.
Echter als ik later de requirement ga vaststellen: "Gebruikers mogen alleen hun eigen reactie's wijzigen", dan heb ik die informatie wel in mijn domein model nodig. Dus zal het ook weer een onderdeel moeten worden van mijn events. Is het dan zo dat als je het in je domein model nodig hebt, dat het daardoor ook leefrecht heeft in je events? En zoja, als ik dit event dan modelleer is structuur hierbij dan van belang? Het hele comment is eigenlijk een toevoeging aan het event, maar zijn wie commentaar geeft is niet direct een parameter van de staat verandering. Dus als voorbeeld:
code:
1
2
3
4
5
| class TaskStartedEvent { private $occuredOn; // datetime private $comment; // object met als attribute: id, userId, content } |
Ergens zou ik de structuur terug willen zien, want dan weet je ook waar de attributen bij horen. Als ik hierboven kijk dan weet ik dat het Id bij het reactie hoort, dat bij de gebeurtenis is aangeleverd en niet verward wordt door dat hij op een verkeerd niveau staat. Of is deze structuur een overkill voor wat het event allemaal te vertellen heeft?
Zoals je ziet heb ik nogal wat vragen over hoe je een event opbouwt. Hoe maak je de beslissing wat er in een event komt en wat niet? Is het gebruik van metadata een correcte, of is dit eerder een bad-practice? Mogen events complexe structuren bevatten, of kan het best met een platte structuur functioneren? Zijn er regels hoe je events moet opbouwen, of best practices? Want zelfs op z'n relatief simpel voorbeeld loop ik op sommige vlakken vast.