2

I have a situation in a CQRS project where I have to log a user's request for information (query) then optionally start a workflow based on the response from the data store. The user is making a request for information which requires immediate feedback. At the same time, the system optionally starts a workflow to analyse the request. How do I implement this in CQRS since the request is neither a 'pure' query nor a 'pure' command?

Edit: To add some more context to this: The application is like a search application, where the user types in a query and the application returns with a result. But the application also logs the query and could start a workflow depending on the response from the server. The application also "remembers" the user's last few queries and uses it to give context to the new query.

Additionally, the query response may not be synchronous. A background worker may be responsible for delivering the result to the client.

Tolu
  • 1,081
  • 1
  • 8
  • 23
  • What state is the query changing? Do you mean the workflow started optinally will make a change? Is it possible to fire the workflow asynchronously? – Yugang Zhou Sep 06 '13 at 09:03
  • Yes, Hippoom, the query is changing the state of the system via the workflow. – Tolu Sep 07 '13 at 17:47

3 Answers3

1

Though you've given us little to work with I think this question has a simple answer:

I disagree with you that the request is neither a 'pure' query nor a 'pure' command. The request is a pure query, because the request is not a request for an analysis, but a request for information. The analysis that optionally gets triggered by the request is a command, but a command in the context of the query event. The system, or more specifically the event handler, is therefore the actor in the context of the command, not the user, which is the actor in the context of the query.

No query is ever side-effect free. It is the intention what makes it a query.

Lodewijk Bogaards
  • 19,777
  • 3
  • 28
  • 52
  • To my money, "No query is ever side-effect free. It is the intention what makes it a query." is a very expensive approach, in the long run. After a few turn-over in the developers' team, nobody will remember what was the "intention" of a message. – Giacomo Tesio Sep 09 '13 at 07:47
  • Good software reveals its intention. A select query on a database may also cause the database engine to start analyzing its cache, append to its logs and update some metrics. It still remains a query. The intention of the word 'select' is crystal clear. – Lodewijk Bogaards Sep 09 '13 at 17:58
  • @mrhobo I've added some more contextual information to the question. I also don't seem to undersatand what you mean by "No query is ever side-effect free ...". Isn't a simple query against a data store to return some data side effect free from the application's point of view? – Tolu Sep 09 '13 at 22:31
  • @Tolu this is a well known fact. The only way to prevent state changes is by not doing anything. The simple logging of a message is a common example of a side-effect. You are right that in the context of the application this is not a direct effect. Similarly in the context of this query your workflow is not to be considered a direct effect. – Lodewijk Bogaards Sep 10 '13 at 07:27
  • @Tolu Thanks. To answer your first question: yes, it is okay. About your second question: generally I would not think of a client _sending_ events. A client may trigger an event by doing something, but an event is always secondary to an action. A client may however subscribe to an event which it may even trigger itself (e.g. by a query) and which can be raised asynchronously. – Lodewijk Bogaards Sep 15 '13 at 12:56
  • You should note that there is a subtle difference between an event and an asynchronous response though. An asynchronous response is triggered directly by the caller and can be expected. An event can be triggered from multiple sources and can be unexpected. – Lodewijk Bogaards Sep 15 '13 at 12:58
0

Such request is a command.

In simple OOP, I've often modeled such kind of message as a void method with an out params.

For example, in a financial model, I had an advisory contract (entity and aggregate root) enforcing rules to build a financial recommendation (entity, immutable). The pubblication of the definitive recommentation was modeled with a command like this:

public interface IAdvisoryContract
{
    ContractNumber Number { get; }

    // lot of well documented commands and queries here...

    /// 90 lines of documentation here...
    void PublishRecommendation(
               IUser advisor, IAssetClassBreakdownAnalysis assetClassAnalysis,
               IVolatilityAnalysis tevAnalysis, Time time,
               out IRecommendation newRecommendation);

    event EventHandler<EventArgs<RecommendationNumber>> RecommendationPublished;
}

In CQRS it depends on your infrastructure: for example, in a similar situation over HTTP, I used a POST returning the relevant info to the client.

Giacomo Tesio
  • 7,144
  • 3
  • 31
  • 48
  • Just for curiosity, could you update with an simple oop example? I mean it's a good idea to keep the command method void but in what situations we need this? – Yugang Zhou Sep 06 '13 at 13:52
  • Added an example: an entity that creates a new entity whose livecycle is decoupled from the "mother" afterwards. – Giacomo Tesio Sep 06 '13 at 14:29
  • Given your edit, I confirm my answer. Logging is not enough to turn a query into a command, but starting a workflow is by far enough. At least if we are talking about a workflow that is part of the domain model. – Giacomo Tesio Sep 10 '13 at 07:27
0

What about after the execution of the query send a message for notification? I probably will use a decorator like:

public QueryRs query(QueryRq rq) {
     final QueryRs rs = target.query(rq);
     notifier.notifyQueryDone(rs);
}

And make the workflow subscribe and consume the message. I'm not sure is this query considered changing the state still in this solution?

Yugang Zhou
  • 7,123
  • 6
  • 32
  • 60
  • My answer is similar only described in abstract terms. The response is an event which may trigger a command. Since this command is not in the context of the query, but is simply a side-effect, the query still remains a pure query. – Lodewijk Bogaards Sep 12 '13 at 16:28