Roald, that’s not entirely true. It is very much possible to validate against “a” query model. In fact, based on information in your UI, you (or the machine) validates that a certain action can be taken. For example, it is useless to confirm an Order, if the query model already says the order is shipped and closed. The point of prevalidation is to not bother your user with options and buttons that fire commands that don’t make sense given the current state of things. However, since things change (we live in a concurrent world), what makes sense at a certain time, might not make sense anymore after 2 seconds, because another user has changed state. The UI will not immediately reflect that change and allow the user to submit “invalid” commands.
Sheak, to answer your question: there are several approaches when it comes to consistency. But your problem isn’t so much related to consistency, but more with dealing with concurrency and data staleness.
The best thing to do is to make sure commands can’t ever have a “negative” outcome. A failed command can only occur when two users simultaneously do something, without them knowing. Even with full consistency, one of the users will end up with a situation he didn’t expect. Preventing negative outcome is not easy, but certainly far from impossible. Udi Dahan is a proponent of this approach.
There is also a middle road: make command execution synchronous (i.e. your client will wait for the execution of a command). Command execution is done against a fully consistent model. Axon can make sure the necessary locks are in place (LockingRepository). If state has been changed by another user, you can use the conflict resolver to try to resolve the conflict. If that’s not possible, your client is still around to let him know his request was refused. A good client will update his view model and see a state that explains the failure of his command (e.g. somebody must have already approved the Order).
The ultimate form of consistency is to execute all event handlers within the same transaction that was used to update the command model. This approach severely limits scalability. And although it seems that everything is consistent at all times, don’t fool yourself here. Concurrent access to the application will still yield “stale” data from your query model. Data consistency is not a solution for data staleness.
So, where possible, I try to use the first approach. However, I let my clients wait for successful execution of a command. When something is wrong (which for some reason, I call him Murphy, always happens when least expected), you have the chance to notify the client.
Hope this answers your questions.