3

Context: User wants to edit entity details, say user name.

Assuming the fact that the Read Model is eventually consistent with the Write Model is it conceptually wrong to query write model in order to get the most recent state of an entity for the page?

If I choose to query the Read Model (typical implementation according to CQRS Journey Book) how can I deal with optimistic concurrency?

Thank you in advance!

Christophe
  • 81,699

4 Answers4

3

I would say that the command interface isn't supposed support queries, and the write model should not necessarily be exposed to client applications for that purpose.

However, the notion you're looking for in the first question:

is it conceptually wrong to query write model in order to get the most recent state of an entity for the page?

seems to be "read your own writes", which can be very important to some applications. I suppose one could provide a buffer that remembers one's own session writes. However, what I do is a bit different.

I return a timestamp (or some other value that supports >= comparison) from the command interface, as well as the query interface. The timestamps are forwarded from the write model to the read model along with other (changed) content.

Then when necessary to read my own writes, my client queries asking for data of interest as of >= the timestamp returned by the last command it issued. This way I know the query is reporting content as of the client's latest change.

I don't understand the follow up question:

If I choose to query the Read Model (typical implementation according to CQRS Journey Book) how can I deal with optimistic concurrency?

As @Alexus says, the read model is read only so doesn't have to worry about multiple writers. However it is not totally read-only as it must be repeatedly updated somehow from the write model. The optimistic concurrency should be handled internally by the normalized write model SQL store and also by the denormalized read model SQL store. (If you're using a non-SQL store for the denormalized read model, you should be aware that there is a difference between responding to read model queries with stale values on the way to eventual consistency and responding with inconsistently mixed values on the way to eventual consistency.)


Update:

Sorry for the confusion on my part. The term optimistic concurrency is overloaded... This term normally (in my mind) refers to a kind of transaction overlap capability found in the internals of the database. However, CQRS uses this term to refer to a similar capability between client application and command server (and query server). You provide the timestamp (as I described above) also with commands, in a form that asks the command to fail/abort if the timestamp is old. Note that it is recommended that each aggregate have its own timestamp for fine granularity, so we're talking about using the logic I described above for each aggregate.

So this creates a full cycle or loop regarding the timestamps/version numbers. When your client application does its first query, it omits the timestamp, though it receives timestamps back; when it does a command that is (most likely) based on the query information it has at present, it passes in the query timestamps with a flag to abort on timestamp old. When the command completes, it provides a timestamp that the next query can use to see if the last command's results are in the read model yet (or to wait until so). The query returns new timestamps, and the next command is based on those latest timestamps (with abort if old set), and so on. (Sometimes, if the UI knows it hasn't refreshed in a while it may premptively do so.)

I think you could start with a single global timestamp/version number for the whole system; get that working, and later make the timestamp more granular (e.g. per aggregate instance) for better concurrency. As @JDT is answering, this is complicated stuff already, and more so if you take it all the way to replicas of command processors for the same aggregate.

Erik Eidt
  • 34,819
2

Conceptually wrong? I'd say: "Yes!"

Looks like you're willing to do CRUD. Nothing intrinsically wrong with that. But it's conceptually not CQRS.

If you just need CRUD to solve your problem, just use CRUD.

ZioBrando
  • 1,524
1

No, the read model is for reading, the write model for writing

If you have a system that requires you to use CQRS (and it is something that you really should think long and hard about), you cannot use the write model to get the most recent data in the UI. You design your application in such a way that it can handle the delay between the write operation and the eventually consistent read model. If the delay is short (say, two seconds or less), you could work with a page that simply displays a message to the user about the changes having been saved, thereby simply preventing the user from seeing inconsistent data. Longer delays could mean 'faking' the data by persisting it in the session and displaying it from there, hiding fields that have been edited and are not yet consistent, and so on.

Doing 'pure' CQRS with a read and write model has very, very big implications on the way you write your application. Should really should consider if the application is big enough to warrant this approach as a lot of things you take for granted in regular applications become less obvious or outright difficult.

JDT
  • 6,410
  • 21
  • 33
0

Read model will not be affected by optimistic concurrency because it is read-only and will get returned to you in whichever state it is at the moment of request. The only thing you have to worry about is making changes to the same write model simultaneously.

Alexus
  • 2,398