CQRS is niet zo moeilijk als je denkt

Geschreven door Dick van Hirtum en Sander Obdeijn op

Onlangs kwam bij een van onze interne gesprekken over tools en technieken wederom het CQRS pattern ter sprake en dan met name waarom het door veel developers waar wij mee spreken als complex wordt gezien. Naar ons idee is het namelijk een vrij simpel pattern. Het leek ons daarom een goed idee om een blogpost aan dit pattern te besteden.

Wat is CQRS?

CQRS staat voor “Command Query Responsibility Segregation”. Het idee is om acties die de staat van je systeem veranderen (die “commands” genoemd worden) te scheiden van acties die data ophalen uit je systeem (of “queries).

Deze twee verschillende type acties hebben namelijk vaak zeer verschillende architecturele eisen. Zo is het in de meeste systemen bijvoorbeeld heel gebruikelijk dat je veel vaker data ophaalt dan dat je mutaties uitvoert.

Door deze twee van elkaar te scheiden kan je ze afzonderlijk van elkaar optimaliseren of opschalen. Ook wordt het hierdoor makkelijker om het Single Responsibility Principle toe te passen en duidelijke benamingen te geven aan een command of query waardoor de code beter zichzelf documenteert.

Zo zijn er nog meer voordelen van het scheiden van commands en queries, maar daar zullen we hier niet diep op in gaan. Daar is genoeg literatuur over te vinden, waar we een aantal voorbeelden van zullen toevoegen als links onderaan deze blogpost. Waar wij hier vooral in geïnteresseerd zijn is waar de perceptie vandaan komt dat dit een complex pattern is. Want meer dan wat we hier beschreven hebben is CQRS niet.

CQRS != CQRS

In gesprekken over CQRS hoor je ook vaak de term CQS. Dit staat voor “Command Query Separation”. CQS en CQRS zijn aan elkaar gerelateerd, maar zijn niet exact hetzelfde. CQS is een principe waarbij elke methode van een class ofwel een command mag zijn, danwel een query, maar nooit beiden.

Het verschil zit hem in het niveau waarop ze werken: CQS werkt op class niveau, dus zegt iets over individuele methodes binnen een class. CQRS werkt op architectuur niveau en beschrijft dus een heel pad door je applicatie wat ofwel een command, danwel een query is.

CQRS != MediatR

Een andere eigenschap van CQRS implementaties in .NET is dat het vrijwel altijd geïmplementeerd wordt samen met het MediatR package. Daardoor ontstaat misschien het beeld dat MediatR een implementatie van CQRS is, maar dat is niet het geval.

MediatR is een implementatie van het Mediator pattern, een design pattern om te voorkomen dat objecten die met elkaar moeten samenwerken te veel aan elkaar gekoppeld raken. Bij het implementeren van CQRS wordt het gebruikt om de aanroep van een command of query te scheiden van de implementatie.

Het is prima mogelijk om CQRS te implementeren zonder MediatR te gebruiken, maar zoals in alle applicaties blijft het belangrijk om je objecten niet te veel aan elkaar te koppelen als je jouw applicatie onderhoudbaar wilt houden, dus we raden daarom het gebruik van MediatR zeker aan.

CQRS != Event Sourcing

En dan komen we nu bij wat volgens ons de voornaamste reden is waarom CQRS als een complex pattern wordt gezien: Event sourcing.

Bij de meeste applicaties wordt de alleen de huidige staat van de applicatie opgeslagen. Daardoor weet je hoe het systeem er nu voor staat, maar kun je nooit meer achterhalen welke stappen er zijn genomen om hier te komen. Dat is waar het event sourcing pattern voor bedacht is.

Bij event sourcing sla je niet de nieuwe staat op van je systeem bij een wijziging, maar juist die wijziging zelf. Om de huidige staat van je systeem te bepalen haal je dan alle wijzigingen op en voert die een voor een uit. Hierdoor kan je altijd precies achterhalen wat er is gebeurd met je systeem.

Event sourcing is een complex pattern en verdient een blogpost (of meerdere) op zich, dus we gaan er hier niet in veel detail op in. Wat wel belangrijk is om in te zien is dat event sourcing en CQRS heel goed op elkaar aan sluiten. Bij CQRS zijn het namelijk de commands die wijzigingen zullen opslaan en queries die de wijzigingen aggregeren tot de huidige staat. Dat is waarschijnlijk waarom ze vaak als een en hetzelfde pattern worden gezien.

Tot slot

Of je CQRS gebruikt in samenwerking met MediatR of event sourcing, of andere design principles en patterns, wij zien het als een goede manier om je applicatie overzichtelijk, schaalbaar en flexibel te houden. Daarnaast stelt het je in staat om makkelijker complexe patterns als bijvoorbeeld event sourcing te implementeren, maar dat is zeker niet noodzakelijk om CQRS te gebruiken.

Het vraagt wat discipline om CQRS goed te implementeren maar het geeft je veel voordelen als je jouw applicatie moet onderhouden of uitbreiden. En zoals we hopelijk hier hebben laten zien hoef je je niet te laten afschrikken door de complexiteit.

Links

Als je geïnteresseerd bent om je verder te verdiepen in een van de behandelde onderwerpen, dan zijn hier een aantal nuttige links:

Daarnaast hebben we op onze GitHub een demo API die we tijdens onze interne gesprekken hebben gebruikt om de verschillen te illustreren:

Sander ObdeijnSander is een .NET expert met een bovenmatige interesse in Domain-Driven Design. Momenteel werkt hij als software engineer aan de Basisvoorziening Identiteitsvaststelling (BVID) bij de Nationale Politie.
Dick van HirtumDick is een .NET expert met veel kennis van Design Patterns en Principles. Momenteel werkt hij als software engineer aan de Basisvoorziening Identiteitsvaststelling (BVID) bij de Nationale Politie.
← Terug
XPRTZ