Question

I am currently researching if CQRS can be applied when building a particular system and have some questions I cannot easily find answers for.

Command availability/validation

What user can do with particular entity is usually tied not only to the user's role but the relationship this particular user has with this entity (e.g. author) and the state of this entity (public, archived, etc.).

To me seems like in CQRS user actions map to commands responsible for handling them but it is not clear how to determine which actions and thus commands are available.

Returning list of available commands with every read model seems inappropriate, given that due to need for consistency we have to check for commands that are only tied to the user role (e.g. for menu items).

Of course, the commands have to do the validation again while in transaction in case another user changed the state making the command invalid).

Resistance to requirement changes

In my experience it is a nightmare to maintain a system with verbose logic and too many classes/tables tied to particular business object.

In CQRS there might be several read models for one particular business entity. When that entity needs to be changed, all related read models should be modified as well.

For maintainability it is necessary to have them related in some way that can be easily apparent while refactoring.

On related note having a lot of overly specific commands would lead to maintainability issues as well - I am correct in assuming that one command per one use case should work best?

Read model and commands that do not modify domain model

In CQRS commands are supposed to update read model and users can access old version of it in the meantime.

There are two special cases that cause problem with that in my mind.

First, there are commands that do not modify the domain model itself (maybe just state) but rather perform some operation involving third party systems/frameworks/email etc and in some cases these commands can take quite a bit of time to run.

As I see here we need to have a read model that combines domain model with command execution state. This read model can be used as a history or list of items being currently processed.

Second some commands produce a result. This result needs to be shown for the user when the command has completed successfully and in some cases must be discarded after some time and even be a file. Therefore there must be a way to store the results of these commands in the database and associate them with the particular instance of command. In other words, have a temporary read model.

Read-model tables vs in-memory cache

Also I am correct in thinking, that with second-level ORM cache (for query results) it is not necessary to have database tables for read model but ORM instead can produce them when queries are executed for the first time, cache the results and invalidate them automatically when model entities are changed. This approach seems to be good starting point that enforces CQRS interface/pattern but can be changed and as a bonus can support dynamic projections (when the user chooses what columns etc to see).

Was it helpful?

Solution

Overall, I'm not sure the questions that you're asking specifically, so I'm taking my best shot at answering them. I hope this helps. I'll clarify if I missed the point of your question - just let me know.

Command availability/validation:

I'm not sure what your question is for this part, but essentially your user's behavior drives what commands you have available. Whether the command is valid or not for that particular user could be handled by your UI's controller (or whatever mechanism) that is submitting the command or it can be handled by the domain that receives the command. The command should contain enough information for the domain to evaluate it and then change its state and raise events.

Resistance to requirement changes:

Not all relevant read models may need to be changed if an entity changes - it's really dependent on what that read model's purpose is. Some ways to maintain your read models could be to use different schemas per related set of read models, or use a naming standard (e.g. ORDER_xxx). I'd prefer the former as this is cleaner (at least from my point of view).

Read model and commands that do not modify domain model:

Commands are not always supposed to update the read model. They can update the read model if the business rules in the domain raise the appropriate events that are handled and persisted. Just because a command is issued doesn't mean that the read model will be updated.

If you follow CQS (and CQRS), then your command should not return a result. Commands return void. Commands are you telling the system to do something. What you are describing is a two step process: issuing a command and then issuing a read request. It is very possible that the read request will produce "old" data (meaning, before the command has updated the read model). There are several ways to handle this. You could poll - not a very elegant way, but it works. You could take the user to an intermediate page after the command is issued ("thank you for your order, would you like to order something else?") and then when the user returns back, they see their updated list (since the command has now processed). Again, not ideal. But that is the nature of eventual consistency. There's no real answer to it -- it's really driven by how your users interact with the system.

For the commands that just update state or interact with third party systems, can't these be done offline without having the user wait for them to complete? I think this is another case of having your system reflect how your users will use the system.

Read-model tables vs in-memory cache:

If you're asking if this is a good idea, I'm not so sure. What is your starting point for this in-memory cache? What happens when the system is restarted -- what happens to your model? Do you rebuild it, and from where and how do you restore the state? Are you using Event Sourcing for this and you're going to replay all events from the last snapshot to get your domain model in place?

Without knowing the answers to those questions, I can't give an answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top