3
votes

We are using CQRS with EventSourcing.

In our application we can add resources(it is business term for a single item) from ui and we are sending command accordingly to add resources.

So we have x number of resources present in application which were added previously. Now, we have one special type of resource(I am calling it as SpecialResource). When we add this SpecialResource , id needs to be linked with all existing resources in application. Linked means this SpecialResource should have List of ids(guids) (List)of existing resources.

The solution which we tried to get all resource ids in applcation before adding the special resource(i.e before firing the AddSpecialResource command). Assign these List to SpecialResource, Then send AddSpecialResource command.

But we are not suppose to do so , because as per cqrs command should not query. I.e. command cant depend upon query as query can have stale records.

How can we achieve this business scenario without querying existing records in application?

4
Do you really need to materialize the list of existing resources? Can't the database provide this? (Also, I do not understand why to link "with all existing resources in application" - maybe, with select existing resources?)Roman Susi
Database has this all resources. Do you mean we should link all those resource in database, instead of in Domain layer. If we do so then state(in stateStore) of SpecialResource wont have all the resource IdsRoshan
Please, clarify what do you mean by "all existing resources in application". Is that special object really a catalog? If it is, why not delegate it to the database and efficiently calculate each time in the queries?Roman Susi
you can consider it as one object like Employee, When we add SpecialEmployee this should link to all existing employees in database/applicationRoshan
So, is it capturing "all existing employees" for that moment, or will it continue to be linked to "all existing employees", say, after more employees are added / deleted?Roman Susi

4 Answers

1
votes

But we are not suppose to do so , because as per cqrs command should not query. I.e. command cant depend upon query as query can have stale records.

This isn't quite right.

"Commands" run queries all the time. If you are using event sourcing, in most cases your commands are queries -- "if this command were permitted, what events would be generated?"

The difference between this, and the situation you described, is the aggregate boundary, which in an event sourced domain is a fancy name for the event stream. An aggregate is allowed to run a query against its own event stream (which is to say, its own state) when processing a command. It's the other aggregates (event streams) that are out of bounds.

In practical terms, this means that if SpecialResource really does need to be transactionally consistent with the other resource ids, then all of that data needs to be part of the same aggregate, and therefore part of the same event stream, and everything from that point is pretty straight forward.

So if you have been modeling the resources with separate streams up to this point, and now you need SpecialResource to work as you have described, then you have a fairly significant change to your domain model to do.

The good news: that's probably not your real requirement. Consider what you have described so far - if resourceId:99652 is created one millisecond before SpecialResource, then it should be included in the state of SpecialResource, but if it is created one millisecond after, then it shouldn't. So what's the cost to the business if the resource created one millisecond before the SpecialResource is missed?

Because, a priori, that doesn't sound like something that should be too expensive.

More commonly, the real requirement looks something more like "SpecialResource needs to include all of the resource ids created prior to close of business", but you don't actually need SpecialResource until 5 minutes after close of business. In other words, you've got an SLA here, and you can use that SLA to better inform your command.

How can we achieve this business scenario without querying existing records in application?

Turn it around; run the query, copy the results of the query (the resource ids) into the command that creates SpecialResource, then dispatch the command to be passed to your domain model. The CreateSpecialResource command includes within it the correct list of resource ids, so the aggregate doesn't need to worry about how to discover that information.

0
votes

It is hard to tell what your database is capable of, but the most consistent way of adding a "snapshot" is at the database layer, because there is no other common place in pure CQRS for that. (There are some articles on doing CQRS+ES snapshots, if that is what you actually try to achieve with SpecialResource).

One way may be to materialize list of ids using some kind of stored procedure with the arrival of AddSpecialResource command (at the database).

Another way is to capture "all existing resources (up to the moment)" with some marker (timestamp), never delete old resources, and add "SpecialResource" condition in the queries, which will use the SpecialResource data.

Ok, one more option (depends on your case at hand) is to always have the list of ids handy with the same query, which served the UI. This way the definition of "all resources" changes to "all resources as seen by the user (at some moment)".

0
votes

I do not think any computer system is ever going to be 100% consistent simply because life does not, and can not, work like this. Apparently we are all also living in the past since it takes time for your brain to process input.

The point is that you do the best you can with the information at hand but ensure that your system is able to smooth out any edges. So if you need to associate one or two resources with your SpecialResource then you should be able to do so.

So even if you could associate your SpecialResource with all existing entries in your data store what is to say that there isn't another resource that has not yet been entered into the system that also needs to be associated.

It all, as usual, will depend on your specific use-case. This is why process managers, along with their state, enable one to massage that state until the process can complete.

I hope I didn't misinterpret your question :)

0
votes

You can do two things in order to solve that problem:

  • make a distinction between write and read model. You know what read model is, right? So "write model" of data in contrast is a combination of data structures and behaviors that is just enough to enforce all invariants and generate consistent event(s) as a result of every executed command.

  • don't take a rule which states "Event Store is a single source of truth" too literally. Consider the following interpretation: ES is a single source of ALL truth for your application, however, for each specific command you can create "write models" which will provide just enough "truth" in order to make this command consistent.