In CQRS, how do you build the response when creating an entity?
Asked Answered
S

5

10

If using CQRS and creating an entity, and the values of some of its properties are generated part of the its constructor (e.g. a default active value for the status property, or the current datetime for createdAt), how do you include that as part of your response if your command handlers can’t return values?

Scylla answered 26/1, 2017 at 6:50 Comment(3)
why can't your command handlers return values?Dmz
Because of the CQS principleMidis
@ConstantinGALBENU not entirely accurate (see my answer)Heehaw
A
8

You would need to create guid before creating an entity, then use this guid to query it. This way your command handlers always return void.

    [HttpPost]
    public ActionResult Add(string name)
    {
        Guid guid = Guid.NewGuid();
        _bus.Send(new CreateInventoryItem(guid, name));
        return RedirectToAction("Item", new { id = guid});
    }


    public ActionResult Item(Guid id)
    {
        ViewData.Model = _readmodel.GetInventoryItemDetailsByGuid(id);
        return View();
    }
Adalbert answered 26/1, 2017 at 8:2 Comment(3)
+1. I follow the same general approach, albeit my commands return a specific command status object that contains an ack/nack so that I can immediately fault on a malformed/invalid command. No domain validation at that point, since it does not belong there. Granted, this approach is best for eventual consistency, but it does prevent bleeding the domain back into places it does not belong (such as the application layer).Blood
What if I'm not using a GUID but auto-increment id? I can't ask server for a new id because that's also a "command". I can't "query" latest item id and +1 by it because race condition.Winn
@YamiOdymel then you can be flexible and return ID from your command. Here is an example of using .net EF core where both queries and command return values: github.com/kedzior-io/fast-architectureAdalbert
H
8

Strictly speaking, I don't believe CQRS has a precise hard and fast rule about command handlers not returning values. Greg Young even mentions Martin Fowler's stack.pop() anecdote as a valid counter-example to the rule.

CQS - Command Query Separation, upon which CQRS is based - by Bertrand Meyer does have that rule, but it takes place in a different context and has exceptions, one of which can be interesting for the question at hand.

CQS reasons about objects and the kinds of instructions the routine (execution context) can give them. When issuing a command, there's no need for a return value because the routine already has a reference to the object and can query it whenever it likes as a follow-up to the command.

Still, an important distinction made by Meyer in CQS is the one between a command sent to a known existing object and an instruction that creates an object and returns it.

Functions that create objects

A technical point needs to be clarified before we examine further consequences of the Command-Query Separation principle: should we treat object creation as a side effect?

The answer is yes, as we have seen, if the target of the creation is an attribute a: in this case, the instruction !! a changes the value of an object’s field. The answer is no if the target is a local entity of the routine. But what if the target is the result of the function itself, as in !! Result or the more general form !! Result.make (...)?

Such a creation instruction need not be considered a side effect. It does not change any existing object and so does not endanger referential transparency (at least if we assume that there is enough memory to allocate all the objects we need). From a mathematical perspective we may pretend that all of the objects of interest, for all times past, present and future, are already inscribed in the Great Book of Objects; a creation instruction is just a way to obtain one of them, but it does not by itself change anything in the environment. It is common, and legitimate, for a function to create, initialize and return such an object.

(in Object Oriented Software Construction, p.754)

In other places in the book, Meyer defines this kind of functions as creator functions.

As CQRS is an extension of CQS and maintains the viewpoint that [Commands and Queries] should be pure, I would tend to say that exceptions that hold for CQS are also true in CQRS.

In addition, one of the main differences between CQS and CQRS is the reification of the Command and Query into objects of their own. In CQRS, there's an additional level of indirection, the "routine" doesn't have a direct reference to the domain object. Object lookup and modification are delegated to the command handler. It weakens, IMO, one of the original reasons that made the "Commands return nothing" precept possible, because the context now can't check the outcome of the operation on its own - it's basically left high and dry until some other object lets it know about the result.

Heehaw answered 27/1, 2017 at 13:44 Comment(2)
Agreed about no hard and fast rules here. I have done both synchronous / return a value commands and asynchronous commands - trade offs either way IMO. Also interesting discussion here blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids about different rules when at the boundaries of your code vs when inside your domain.Dmz
Interesting indeed, thanks. M Seemann seems to diverge from Meyer's book when he says that creating an entity "mutates the state of the system".Heehaw
D
4

Some ideas:

  1. Let your command handlers return values. This is the simplest option - just return what was created inside the entity. There is some disagreement about whether this is 'allowed' in CQRS though.
  2. The preferred approach is to create your defaults (i.e.id) and pass them into your command - For example, https://github.com/gregoryyoung/m-r/blob/master/CQRSGui/Controllers/HomeController.cs in the Add method, a Guid is created and passed in to the CreateInventoryItem command - this could be returned in the response. This could get quite ugly if you have lots of things to pass in though.
  3. If you can't do 1 or 2, you could try having some async way of handling this, but you haven't said what your use case is so it's difficult to advise. If you're using some sort of socket technology you could do sync-over-async style where you return immediately, then push some value back to the client once the entity has been created. You can also have some sort of workflow where you accept the command then poll / query on the thing being created
Dmz answered 26/1, 2017 at 7:20 Comment(12)
Command must not return anything because of the CQS. If a command is not accepted then it must throw an exception. If it is accepted then it must not throw anything. That's how you know that it is accepted by the aggregate.Midis
The original question was how to get the values from auto created fields, not about how to handle errors. I don't consider a synchronous command that creates something and returns the ID (for example) to be a query in terms of CQS. You're still in the command side of things. You're still mutating state. Also I wouldn't subscribe to your assertion that exceptions must be thrown for unaccepted commands - you could equally return an error and a reason, depending on the conventions of the language you're programming inDmz
How do you spot the difference between a command method and query method if both of them return something? I tell you how: you read the entire method body or some comment associated with the method. That is the reason CQS exists: if you read the signature of the method you instantly see that the method is a query or a command. This is cleaner code.Midis
Please see the answer below regarding CQRS vs CQS. In particular the quote from Object Oriented Software Construction:'It is common, and legitimate, for a function to create, initialize and return such an object.' when discussing creating objects.Dmz
Also, the original post asked about returning values in a response. It is entirely possible to adhere to CQS on the inside of your application by doing a create-then-read flow, but from the outside to have a request/reply that returns something. Also interesting discussion here blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids where again returning in a response is seen as orthogonal to CQSDmz
I agree that at some high level functions you could return something in a response to a command request but at the level where the aggregate code lives, more exactly in the command handlers, I don't return anything. If you need some default/constant/static values that all the aggregate instances share you could read them statically from the aggregate class.Midis
I've implemented both approaches in the past and there are tradeoffs either way. In general I try and adhere to CQS but know there are places where there are exceptions to the rules :) The OP didn't seem to even consider returning a value, hence my answer :)Dmz
I agree. We are not robots and we must decide in every case what is the best approach but I try to follow the best practices.Midis
Not returning anything from commands is a critical part of CQRS. It's also not necessary to return anything (except exceptions). The domain should raise events which can be used to generate read models which represent any state which needs to be queried. If you need to know the ID before you create an aggregate root, pass it in in the command. I.e. generate it before you issue the command. GUID are helpful here but you can do it without them.Lycian
@Lycian There doesn't seem to be consensus around this assertion - for example consider the answer below. I believe there are different ways of doing this with trade-offs either way. I did suggest in my second and third points exactly what you're suggesting.Dmz
Also discussion on the CQRS group here groups.google.com/forum/m/#!topic/dddcqrs/Bie1OHBZbIADmz
@Dmz I agree you have given 2 other options. However, it is critical that command handlers do not return anything except exceptions. The impact on the complexity of the application is significant. And yes it may be pragmatic but the costs can and usually are, crippling. If you are suggesting in option 2 to return an object that represents what you put into the command that may be ok. I know I have returned aggregate id's like that. But I'm not sure why I would need the other info, given I will be able to access it via the read model. But there are always exceptions to any rule.Lycian
G
2

What I ended up doing is I created a 3rd type: CommandQuery. Everything gets pushed into either a Command or Query whenever possible, but then when you have a scenario where running the command results in data you need above, simply turn to the CommandQuery. That way, you know these are special circumstances, like you need an auto-id from a create or you need something back from a stack pop, and you have a clear/easy way to deal with this with no extra overhead that creating some random dummy guid or relying on events (difficult when you are in a web request context) would cause. In a business setting, you could then discuss as a team if a CommandQuery is really warranted for the situation.

Gen answered 5/12, 2020 at 21:2 Comment(0)
M
1

According to my understanding of CQRS, you cannot query the aggregate and the command handler could not return any value. The only permitted way of interogating the aggregate is by listening to the raised events. That you could do by simply querying the read model, if the changes are reflected synchronously from the events to the read model.

In the case the changes to the read model are asynchronous things get complicated but solutions exists.

Note: the "command handler" in my answer is the method on the Aggregate, not some Application layer service.

Midis answered 29/1, 2017 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.