mrt
10
2010

CQRS & de bijkomende architectuur

In mijn vorige blogpost deed ik in de voetnoten een voorstel om de architectuur die vaak meekomt met het patroon CQRS anders te noemen. Ik dacht aan een “Circular Architecture” om hem duidelijk te contrasteren met een “Layered Architecture”. Na een korte discussie met Greg Young en Alistair Cockburn hierover besloot ik om het idee nog eens even goed onder de loep te nemen. Zij claimden allebei dat de “Hexagonal Architecture” van Alistair Cockburn deze architectuur al voldoende beschreef. Dat zette me wel aan het denken, aangezien ik het oordeel van beide zeer respecteer. Eerst enkele definities waar ik vanuit ga.

Command-Query Responsibility Segragation (CQRS) – de ontwerpstijl (patroon) waarbij commands en queries onderscheiden worden en ook gescheiden worden over twee verschillende interfaces. De term wordt ook gebruikt voor de applicatie-architectuur die er vaak bij komt, maar zoals Greg Young zelf betoogt in de blogpost “CQRS and Event Sourcing”, is CQRS zelf een vrij eenvoudig patroon en geen architectuur. Het is een uitbreiding op een eerder patroon dat benoemd is door Bertrand Meyer als “Command-Query Separation”.

Base-CQS-CQRS

Figuur 1: Geen patroon, CQS en CQRS naast elkaar uitgezet.

In de bovenstaande figuur zien we achtereenvolgens 1) een referentie situatie, waarin een implementatie een interface implementeert waarin geen commands en queries onderscheiden worden, 2) Command-Query Separation als patroon, waarin een interface zowel commands als queries bevat en 3) Command-Query Responsibility Segragation, waarin commands en queries over twee verschillende interfaces verdeeld worden.

Event Sourcing – in essentie is dit de naam die gebruikt wordt voor het patroon dat elke verandering in de toestand (state) van een applicatie vastgelegd wordt in een event en dat deze event objecten opgeslagen worden in volgorde van optreden, gedurende dezelfde periode als dat de application state behouden blijft. Zie ook de Bliki over Event Sourcing.

Figuur 2: Event Sourcing, plus het gebruik als application state middels een Event Store
Figuur 2: Event Sourcing, plus gebruik als application state via Event Store

Dit combineer ik hierbij meteen met het gebruik van deze stream aan events als leidende drager van de application state. Hiervoor moet het mogelijk zijn om de events nogmaals ‘af te spelen’ om weer in de laatste toestand (state) te geraken na een applicatie stop/start. Hiervoor is in de architectuur die Greg Young voorstelt als implementatie achter het CQRS patroon gebruik gemaakt van een event store, die in samenwerking met de Aggregate Roots binnen het domein beide functionaliteiten ondersteunt.

Hexagonal Architecture (ook wel Ports and Adapters Architecture) – deze architectuur beschrijft een applicatie als een (domein-) model dat onwetend is van zijn omgeving en dus alleen ‘met zichzelf bezig is’. Alle communicatie met de buitenwereld gebeurt via adapters, die via een bepaald protocol als het ware een poort open zetten naar de buitenwereld. Binnen Sogyo kennen we dit ook wel als het satellietmodel of het zonnebloemmodel, maar waar de Hexagonal Architecture de ‘Application’ in het midden zet, zetten we in het satellietmodel expliciet het Domeinmodel in het midden. Zie hier een afbeelding die ik overgenomen heb uit de beschrijving van Alistair Cockburn van deze architectuur.

Figuur 4: de Hexagonal Architecture zoals beschreven door Alistair Cockburn
Figuur 3: de Hexagonal Architecture zoals beschreven door Alistair Cockburn
Figuur 4: Hexagonal Architecture in detail, met gebruikelijke structuur
Figuur 4: Hexagonal Architecture in detail, met gebruikelijke structuur

Karakteristieke elementen uit deze architectuur zijn het feit dat de ‘Applicatie’ niets van de buitenwereld weet en dat adapters de vertaling heen-en-weer met de buitenwereld verzorgen. Belangrijk detail, zoals je ook in bovenstaande afbeelding kunt zien, is dat de database die gebruikt wordt voor persistentie ook buiten de applicatie valt. In die zin is het dus anders dan de Layered Architecture, die alle elementen in een standaard applicatie boven elkaar plaatst. Verder zien we een scheiding vermeld worden tussen user-side API en een data-side API, wat wel enige overeenkomst vertoond met de scheiding tussen commands en events zoals die in het domeinmodel benoemd worden in het geval van event sourcing. Queries gaan echter wel naar de ‘applicatie’.

De architectuur die vaak bij CQRS gebruikt en verondersteld wordt
De bovenstaande  patronen, in combinatie met het gebruik van een Database die een datamodel bevat dat toegespitst is op uitlezen (querying) is een inmiddels vaker beschreven applicatie architectuur. Elk van de elementen erin is niet nieuw, maar in totaal is het een oplossing voor de problemen die ontstaan als het domein model direct gebruikt wordt voor uitlezen (querying). Door deze functionaliteit volledig aan de read-zijde te houden en te implementeren door middel van een database, is het mogelijk om het domeinmodel vrij van getters en setters te houden (uitlezen is niet noodzakelijk) en een event store te gebruiken als leidende opslag van applicatie toestand (application state) zonder performance problemen. Zie hieronder een afbeelding met alle elementen samen.

Figuur 3: de architectuur die nu verondersteld wordt als de term CQRS gebruikt wordt.
Figuur 5: de architectuur die nu verondersteld wordt als de term CQRS gebruikt wordt.

In deze architectuur zien we de volgende elementen:

– Scheiding van commands en queries over twee interfaces (CQRS)
– Een domeinmodel dat alleen commands ontvangt en events uitstuurt (opgooit)
– Het opslaan van deze events op volgorde (Event Sourcing)
– Het restoren van het domeinmodel op basis van de events via de Event Store
– Het bijwerken van de Read Database door Event Handlers (EH)
– De Task-Based User Interface die gevoed wordt met queries op de read database en commands uitstuurt

Gezamelijk vormen deze elementen een alternatief voor de overbekende Layered Architecture, waarvan de basisvorm bestaat uit de lagen User Interface, Business Layer en Persistentie. Binnen deze architectuur gaan zowel commands en queries door deze drie lagen heen, waardoor de business layer geschikt moet zijn voor zowel commands als queries en er vaak een sterke koppeling is tussen het domeinmodel en het onderliggende datamodel in de persistentie, omdat deze voor alles met elkaar te maken hebben.

Is de bovenstaande architectuur te beschrijven met de naam Hexagonal Architecture?
Volgens mij zou deze naam niet alle elementen vatten die we er normaal gesproken onder verstaan. De onderstaande onderdelen missen volgens mij namelijk dan nog:

1. De HA beschrijft niet een strikte scheiding tussen commands en queries (maar er wordt wel een aanzet in die richting gegeven)
2. De HA beschrijft niet het gebruik van een read database met een geoptimaliseerd read model voor alle queries
3. De HA beschrijft niet het gebruik van een event store als leidende opslag van de applicatietoestand

De ontbrekende elementen en patronen zouden prima als toevoeging op een Hexagonale architectuur gezien kunnen worden, ze sluiten elkaar op geen enkele wijze uit. Echter, met de naam Hexagonal Architecture denk ik alleen aan het gedeelte rond het domeinmodel. Het beschrijft de standaard architectuur die we hierboven zien dus niet volledig.

Conclusie
Of de configuratie van op zich al bekende elementen bij elkaar een architectuur genoemd kan worden is nog niet duidelijk geworden. Volgens mij is het wel duidelijk dat de naam ‘Hexagonal Architecture’ nog niet volledig de lading dekt. Tegelijkertijd wordt de hierboven beschrijven standaard CQRS architectuur wel vaak beschreven als met het heeft over CQRS. Terwijl CQRS an sich een eenvoudiger patroon is. Volgens mij worden deze met elkaar verward, omdat er nog geen naam is voor deze Architectuur/configuratie die een direct bruikbaar alternatief is voor de welbekende ‘Layered Architecture’. Kunnen we de bovenstaande architectuur daarom niet voortaan “Circular Architecture” noemen?

Figuur 7: Circular Architecture: een duidelijke naam?
Figuur 6: Circular Architecture: een duidelijke naam?
Geschreven door Rick | Tags: , , , |

4 reacties - Geef een reactie »

  • Hi Rick,

    Thanks for this clear, well-written article!

    I’m jus wondering: what about locking and transactions in the circular architecture?

     Reactie door Johan den Haan — donderdag, 11 maart 2010 om 13:12
  • Thanks for the kind words! Hope it helps you in getting a clearer picture of everything involved in CQRS.

    Well, normally one would make one command = one transaction. And given the nature of the event store, for transactions, we could use optimistic locking. This will mean events that are generated on an aggregate root that has already been versioned up (by another command), will have to throw a concurrency exception. The domain might actually still make something of this, because the intent of the action is not lost.

    Greg suggests keeping the read database in the tranaction scope too, until this gets too bothersome. This will ensure consistency of the data you’re reading, of course. When this puts too much burden on the transactions (because of scaling up the number of db’s, for instance), you can always retract the transaction boundary to only include the event store. Your read db will then only be eventually consistent. Which in the majority of cases is enough.

     Reactie door Rick — donderdag, 11 maart 2010 om 13:43
  • […] Technology weblog IT -ESSENCE, CQRS & the architecture coming along by Rick van der […]

     Pingback door Command Query Responsibility Segregation | Dialogues Technology — donderdag, 13 januari 2011 om 10:47
  • A bit late on the conversation, i’ve been working on the hexagonal architecture for a while now and as i see it, both CQRS and hexagonal do not exclude each other. They share some concept but the “read” part is different as in hexagonal it goes through the domain.

    Now i guess you could implement an hexagonal architecture while having the read part going around the model that would also fit the CQRS pattern.

    CQRS focus only on the separation of concern while exagonal focus on separation of concern but also on dependencies. In the later, you can completely replace any part outside the model without having any impact on the rest of the application.

    Anyway, having some sort of hybrid is not really a problem nor would it be to have a domain context for the read part inside the model … a bit more coding for not much but that would fit more the hexagonal architecture. But going around the model should be fine for the read part anyway since your database does communicate with the rest of the application through DTOs anyway.

     Reactie door Alexandre — woensdag, 8 juni 2016 om 17:08

Rss feed voor commentaar op deze post. TrackBack URL

Geef een reactie

Powered by WordPress | Aeros Theme | TheBuckmaker.com WordPress Themes

Better Tag Cloud