Event-driven Architecture: CQRS aanpak

Geschreven door: Jilles van Oossanen
In samenwerking met: Luc Kleeven en Anatoly Zimakov

Naar aanleiding van het artikel van André Boonzaaijer “Het nieuwe paradigma?” is ons gevraagd om onderzoek te doen naar het steeds populairder wordende fenomeen, de zogeheten “Event-driven Architecture”.

De Event-driven Architecture (EDA) beschrijft een situatie waarin alle componenten communiceren via events. Een voordeel hiervan zou kunnen zijn dat deze stateless componenten op hun beurt elk event in een aparte thread behandelen en daarmee dus volledig asynchroon worden. Een voordeel van deze ontkoppeling kan zijn dat een systeem zeer schaalbaar wordt. De vraag die wij ons stellen komt voort uit de voorlopige conclusie van Andre Boonzaaijer: “…deze vorm van systeemontwerp en -bouw zeer goed kunnen werken, maar slechts voor een beperkte set aan toepassingen.” Want om welke toepassingen gaat het dan? En waarom is het beperkt tot die set?

Ons onderzoek is begonnen met het lezen van artikelen en forumtopics. Ondanks het feit dat de EDA al een in 2003 voor het eerst beschreven is, zijn er helaas nog geen boeken over geschreven. Dit zorgde ervoor dat de beschikbare informatie vaak vaag was en weinig concrete ideeën of voorbeelden bevatte. Het was dus lastig om aan de hand van deze informatie tot een ontwerp van een applicatie met een EDA te komen.

Om een goede vergelijking te kunnen maken tussen een OO implementatie en een EDA implementatie, hebben we allereerst een OO implementatie van de applicatie gerealiseerd. Deze applicatie diende als leidraad, voor de functionaliteit, tijdens de ontwerp- en realisatiefases van de EDA versie.  Daarnaast kon met behulp van die applicatie goed in de gaten gehouden worden welke aanpassingen gedaan moeten worden om van een OO naar een EDA implementatie te gaan.

Nadat de OO implementatie was gerealiseerd, stond er een applicatie waarin alle functionaliteit was vastgelegd zoals deze in de EDA implementatie terug moest gaan komen. Nu was het dus zaak om een aantal dingen te bepalen voor het EDA ontwerp:

  • Welke events heb je?
  • Welke componenten/objecten heb je?
    • Welke verantwoordelijkheden hebben deze componenten/objecten?
  • Hoe houd je de status van je applicatie bij?
  • Hoe om te gaan met eventuele vertragingen tussen aanvraag van een gebruiker en de reactie van het systeem?
    • En hoe geef je dan bijv. foutmeldingen weer?

Tijdens het onderzoek waren we al gestruikeld over de derde vraag: “Waar houd je de status bij?” Het is namelijk erg onduidelijk hoe hier op geantwoord moet worden in het geval van een CRUD applicatie. Deze gaat namelijk uit van een Request/Response. En juist dat principe wordt met behulp van de EDA om zeep geholpen. Je hoeft, volgens de EDA, namelijk niet te verwachten dat het systeem waar je wat aan vraagt, ook direct antwoord gaat geven. Voor de EDA op zich waren hier weinig ideeën over te vinden. Er is echter wel een aanpak uitgewerkt onder de noemer Command Query Responsibility Segregation(CQRS, zoals André Boonzaaijer ook benoemd) waarmee mogelijk een oplossing wordt geboden.

CQRS beschrijft een situatie waarin commando’s en queries, beide vanuit de GUI, apart worden behandeld. Een query zal, zoals we dat ook zagen in onze OO implementatie, direct data ophalen uit een database. Een command zal echter via een bus in het domain eindigen. Hier wordt het commando verwerkt en wordt er een event afgevuurd, waarin beschreven staat wat er gebeurd is. Hier komt de event-driven aard van de aanpak naar boven. Dit event kan namelijk door elk component, aangesloten op de bus, worden opgevangen. Zo ook door de eventhandler van de eerder genoemde database, welke het op zijn specifieke manier zal afhandelen.

Deze aanpak bied al gauw een voordeel, er zijn namelijk voorbeelden van. Een daarvan is de Lottery applicatie van Erik Rozendaal. Met behulp van een eigen framework heeft hij een event-driven applicatie ontwikkeld, maar wel volgens de CQRS aanpak. Om meer gevoel te krijgen voor deze aanpak en de EDA zelf, hebben we deze applicatie omgebouwd om zo onze eigen functionaliteit te implementeren.

Tijdens dit ‘trial en error’ proces, hebben we een hoop geleerd over de CQRS aanpak. Toen alle gewenste functionaliteit aanwezig was, hebben we nog een keer goed gekeken naar de nieuwe applicatie en hebben we er onze lessen uit getrokken. Aan de hand van de geleerde lessen hebben we vervolgens een aantal regels opgesteld:

  • Alle logica behoort in het domein te zitten (vasthoudend aan DDD principes).
  • Het domein kan gezien worden als kleine subdomeinen, die op zichzelf staan.
  • Onderlinge communicatie tussen componenten gaat alleen via messages.
  • We onderscheiden drie types messages: commands, queries en events.
  • Commands zijn externe invloeden op het systeem, bijv. gui
  • Queries zijn externe verzoeken om informatie, vanuit bijv. de gui.
  • Events zijn interne berichten over gebeurtenissen, bijv. om de reporting op de hoogte te houden.
  • Message handlers doen enkel aan routing en mogen dus geen side-effects hebben, een message handler is altijd voor één specifiek message en naar één specifiek component.

Om de regels te visualiseren, maken we gebruik van het volgende model:

Deze regels moeten als leidraad gelden bij het ontwikkelen van een CRUD applicatie welke de CQRS aanpak zo goed mogelijk volgt.

Aangezien de CQRS aanpak je in staat stelt om eenvoudig bij je data te komen, lijkt een groot probleem van de EDA opgelost te zijn. Dit geldt echter alleen voor de GUI. Het domain beschikt niet over deze functionaliteit. Hoe moet dat dan wanneer er gecheckt moet worden of een uniek item niet voor een tweede keer wordt toegevoegd? Moet je dan de event store terugspoelen om de status te achterhalen? En kost dit niet heel veel performance, voor een relatief kleine handeling? Naast deze vragen kan ook terecht worden opgemerkt dat de CQRS aanpak niet volledig EDA is wanneer je kijkt naar de GUI. Dus hoe zou een applicatie er uit zien welke een volledige EDA heeft?

Zoals André Boonzaaijer in zijn artikel al benoemde, is de EDA interessant, maar slechts voor een beperkte set aan toepassingen. Onze mening is dat een CRUD applicatie niet behoort tot deze set. Zoals we beschreven hebben is een dergelijke applicatie wel te realiseren met de CQRS aanpak. Het probleem is echter dat hier niet alles event-driven is (vandaar de naam). Wanneer je een volledig event-driven CRUD applicatie wil maken, blijven er een aantal vragen onbeantwoord.

Tot nog toe hebben wij deze vragen niet kunnen voorzien van een bevredigend antwoord. We gaan echter wel de applicatie weer opnieuw opbouwen, maar nu aan de hand van onze nieuwe regels. Hopelijk komen we dan tot nieuwe antwoorden en oplossingen, welke we dan ook zeker met jullie zullen delen. Uiteraard nodigen wij iedereen uit om mee te denken over deze vragen. Commentaar en/of opmerkingen zijn uiteraard ook van harte welkom.