2

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!

  • Your question is a bit confusing. Are you talking about using the write model to query and perform data validation before changing the name? Or using the write model to display the most recent data as feedback to the user that the action went through? Because your second question with regards to optimistic concurrency makes little sense, unless you meant to ask how to deal with eventual consistency... – JDT Jul 14 '15 at 13:21
  • @JDT 'Or using the write model to display the most recent data as feedback to the user that the action went through?' - Yes, I'm asking about whether it's acceptable to use write model(source of truth) in order to display entity state to the user in UI, not entity state projection from read model (stale data). As I've understood there should be no queries to write model. The only responsibility of Write Model is that it should accept command from UI and eventually update Read Model. The Responsibility of The Read Model is to provide useful Read Model projections to UI. – Stanislav O Chernykh Jul 14 '15 at 15:22
  • @JDT ' Because your second question with regards to optimistic concurrency makes little sense, unless you meant to ask how to deal with eventual consistency' - I've tried to avoid eventual consistency when querying write model in the case of editing entity details – Stanislav O Chernykh Jul 14 '15 at 15:44
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.

  • Optimistic concurrency occurres in write model when two users edit the same entity details. The question was in order to get best user experience where should those details be queried from (write model or read model)? I don't want user make any decisions based on stale data – Stanislav O Chernykh Jul 14 '15 at 19:21
  • @Stanislav, sorry for the confusion, I have added some text on that. – Erik Eidt Jul 14 '15 at 21:51
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.

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.

  • Read model stores projection of write model in terms of UI (it's strongly correlated with aggregate versions persisted in write model). Then assume that user makes edits based on stale data. If user had dto returned from write model than it would have the most recent state. – Stanislav O Chernykh Jul 14 '15 at 19:06
  • I see what you mean. Like Erik specified in his answer, you should not necessarily expose your models to the client application. When an edit is made, you will only be accepting new values for specific fields and ID of the item to change. ID is persistent, thus can not go stale. New values for the fields will not have any idea of the previous values in the same fields but I doubt they need to. – Alexus Jul 14 '15 at 19:46
  • If I understand you correct UI should help user to choose right values, i.e capture user intent. Then UI should be task based not CRUD based. – Stanislav O Chernykh Jul 15 '15 at 7:10
0

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.

  • 'No, the read model is for reading, the write model for writing' - In CQRS you're already doing requests to Read Model in order to get Aggregates, no one prevents you from mapping aggregate state into dto and send it to UI – Stanislav O Chernykh Jul 15 '15 at 9:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.