How to get state of an aggregate in CQRS pattern?

1,028 views
Skip to first unread message

Caspar

unread,
Mar 9, 2021, 7:27:24 AM3/9/21
to DDD/CQRS

According the article of Martin Fowler and Microsoft CQRS Journey, the CQRS is a pattern applying in a BC, not the architecture for whole system. I get confused in how to get state of an aggregate from anything external in CQRS.

Should an aggregate have a command Get to return its state in write model, or a corresponding query in read model?

It's example of shopping cart service in Akka Platform Guide.

ShoppingCart is an aggregate, it has three commands: AddItem, Checkout and Get. In the command handler of Get, it replies summary of shopping cart to the command sender. In this way, each aggregate has a command Get to return its state in write model.

But I suppose the Get is a query exactly, not a command. Because in CQRS pattern, command changes the state of the aggregate and triggers events, but returns nothing. On the other side, query returns a copy of the current state of the aggregate, but changes nothing. All commands exist in write model, all queries exist in read model. If I want to get state, I shouldn't send a command to write model but a query to read model. The eventually consistence is maintained by the event projection from write model to read model.

So, the Get of ShoppingCart should be moved into read model. Anything external wants to get the state of ShoppingCart, it should send query Get to ShoppingCart and get reply Summary finally. But in this way, the state maybe is stale. Should it get problem in consistence?

Which design is necessary and better?

Putting Get in read model gets risk of consistence, putting it in write model gets semantic ambiguity otherwise. That's my confusion.

Thanks.

Harrison Brown

unread,
Mar 9, 2021, 7:55:11 AM3/9/21
to DDD/CQRS

If you’re going to use CQRS at all then yes, you’d want to ask the query model for state about the system, not the command model. I’d avoid querying the command model as it rather defeats the point of CQRS.

And yes, if you are asynchronously projecting your query model from the command model then you will indeed have eventual consistency meaning the two won’t always be in sync. That’s normal for CQRS with async projections so you’ll need to design around that (normally my recommendation) so your system can cope with it and handle the user experience gracefully. However, you don’t have to do async projections — if you want to keep your query and command models truly separate and autonomies then you will do async, but you can get lots of benefits of CQRS without going that far. My team have created many systems with separate command and query APIs which both connect to the same database, and we’ll either use semi-complex SQL queries to get data for the query model, or we might synchronously project a query model from the command model meaning we have immediate consistency. This is a perfectly viable approach and isn’t “bad CQRS” in any way. See https://udidahan.com/2009/12/09/clarified-cqrs/

So, to summarise: Yes, you’re right that putting a Get query in the command model is odd and to be avoided. You should query the query model, but you get to decide whether you need to do (a) async projections (which will come with eventual consistency but keeps your query and command models very cleanly separated and autonomous), (b) synchronous projections (which keeps the APIs of your query and command models cleanly separated but makes them less autonomous because they’ll both need to talk to the same database) or (c) no projections at all (which means you don’t have autonomous query and command models in a full sense but does still give you the semantic benefits of CQRS). None of these options are better or worse than each other; you get to decide which is most appropriate and which tradeoffs you can make. In general, I advise people to start with (c) because you learn the pattern without taking on the burden of thinking about projections, asynchronous processes and eventual consistency. For simple cases, those async patterns are overkill.

Harrison

--
You received this message because you are subscribed to the Google Groups "DDD/CQRS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dddcqrs+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dddcqrs/b5dc55ff-436b-40b5-9a69-e234d30bffabn%40googlegroups.com.

Caspar

unread,
Mar 9, 2021, 9:56:47 AM3/9/21
to DDD/CQRS
Thank you very much.

I suppose that the result of query always is stale in CQRS pattern. The most important thing is getting the eventually consistence between read model and write model. CQRS pattern fits for the BC, not the whole system.

From the external of aggregate, it doesn't know or care if the aggregate uses CQRS pattern. The query `Get` is a part of protocol or API of aggregate, event the aggregate uses CRUD pattern. 

So, put `Get` into read model or write model is a design choice in CQRS pattern. I prefer to put it into the read model.

Harrison Brown

unread,
Mar 9, 2021, 11:38:28 AM3/9/21
to DDD/CQRS

Er, no that’s not what I’m saying. If you apply CQRS with asynchronous projections then yes, your query model will be eventually consistent with your command model — however, you don’t have to use asynchronous projections. You can apply the pattern with synchronous projections, or just from the same persistence with CQRS applied at the API level (i.e., different code reading the same database tables for querying meaning your command model isn’t involved in queries at all).

And yes, CQRS is a service-/BC-level pattern, not a whole-system architecture. That means this sentence isn’t correct: “From the external of aggregate, it doesn't know or care if the aggregate uses CQRS pattern. The query Get is a part of protocol or API of aggregate” The aggregate pattern is to do with write consistency so only applies in your command model, so a query isn’t part of the API of an aggregate, it’s part of the API of your query model (which doesn’t have aggregates).

Thus, I’d say that whether you ”put Get into read model or write model is a design choice in CQRS pattern” is incorrect: putting your ‘get’ queries into a separate query model _is CQRS. If you put your ‘get’ queries into your command model then you have a single model for both queries and commands and are therefore not doing CQRS.

Harrison

Caspar

unread,
Mar 9, 2021, 10:02:09 PM3/9/21
to DDD/CQRS
"The aggregate pattern is to do with write consistency so only applies in your command model, so a query isn’t part of the API of an aggregate, it’s part of the API of your query model (which doesn’t have aggregates)."

Got it, thanks. 

Ben Kloosterman

unread,
Apr 6, 2021, 7:05:54 PM4/6/21
to ddd...@googlegroups.com
Be Careful with "synchronous" projections as it can cripple the domains throughput and involve a lot of complexity.

Best way to do this is to just have the api make the change then poll the read model till its done before returning ( you could also subscribe to some sort of correlation id stream from the read model but polling scales and is simpler).  That way consumers will see a sync api  though like sync projections / transactions the latency of such calls is much higher.  However unlike sync projections you're not blocking your domain.


Also note bank transactions / accounting ledgers  are a  form of event sourcing ( though rarely CQRS)  , they simply store the event rows in transactional storage ( roll them over a period)  and build the state from the events  dynamically each request,. 

Ben

On Wed, Mar 10, 2021 at 3:38 AM Harrison Brown <harr...@surupartners.com> wrote:

Er, no that’s not what I’m saying. If you apply CQRS with asynchronous projections then yes, your query model will be eventually consistent with your command model — however, you don’t have to use asynchronous projections. You can apply the pattern with synchronous projections, or just from the same persistence with CQRS applied at the API level (i.e., different code reading the same database tables for querying meaning your command model isn’t involved in queries at all)..

Mohan Radhakrishnan

unread,
Apr 7, 2021, 2:22:20 AM4/7/21
to ddd...@googlegroups.com
Hello,

>> So, put `Get` into read model or write model is a design choice in CQRS pattern. I prefer to put it into the read model.

I  think in an event-driven architecture the shape of the events ingested by a microservice's BC is different from the shape of the read projections. Not always but
if the read model is used to publish another event or generate a REST API response then the shapes are different. Of course the REST API is not tightly bound
to the read model.

Thanks,
Mohan

Ben Kloosterman

unread,
Apr 7, 2021, 2:43:04 AM4/7/21
to ddd...@googlegroups.com
"I  think in an event-driven architecture the shape of the events ingested by a microservice's BC is different from the shape of the read projections."

Write domain events tend to be many smaller business events with relevant information . 
Most read domains have no event but  half the time I often have a record state changed event.  I find this has several noticeable benefits 
1) You can check for updates / changes without a poll 
2) 3rd parties and externals do not want many events. It requires too much communication and some things, especially big data is troublesome. So I use the read model event to communicate changes to other parts of the organisation (where api is not sufficient) .. Big data/ intelligence departments especially.  This also allows us to change domain events without communicating to 3rd parties similar to an API gateway. 

Ben
Reply all
Reply to author
Forward
0 new messages