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

前端 未结 5 1872
心在旅途
心在旅途 2021-01-12 22:02

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

相关标签:
5条回答
  • 2021-01-12 22:08

    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
    0 讨论(0)
  • 2021-01-12 22:08

    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.

    0 讨论(0)
  • 2021-01-12 22:16

    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();
        }
    
    0 讨论(0)
  • 2021-01-12 22:29

    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.

    0 讨论(0)
  • 2021-01-12 22:30

    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.

    0 讨论(0)
提交回复
热议问题