如果使用CQRS并创建实体,并且其某些属性的值是生成其构造函数的一部分(例如属性的默认active
值status
或当前日期时间createdAt
),那么如何将其作为响应的一部分包含在内你的命令处理程序不能返回值?
您需要在创建实体之前创建guid,然后使用此guid进行查询.这样,您的命令处理程序始终返回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(); }
严格来说,我不认为CQRS对命令处理程序没有返回值有一个精确的硬性规则.格雷格杨甚至提到马丁福勒的stack.pop()
轶事是一个有效的反例.
CQS - 基于CQRS的命令查询分离 - 由Bertrand Meyer确实具有该规则,但它发生在不同的上下文中并且具有例外,其中一个例子对于手头的问题可能是有趣的.
CQS关于对象和例程(执行上下文)可以给出的指令种类的原因.发出命令时,不需要返回值,因为该例程已经具有对该对象的引用,并且可以随时查询它作为该命令的后续操作.
尽管如此,Meyer在CQS中做出的一个重要区别是发送到已知现有对象的命令和创建对象并返回它的指令之间的区别.
创建对象的函数
在我们研究命令 - 查询分离原则的进一步后果之前,需要澄清一个技术问题:我们是否应该将对象创建视为副作用?
答案是肯定的,正如我们所看到的,如果创建的目标是属性a:在这种情况下,指令!! a更改对象字段的值.如果目标是例程的本地实体,则答案为否.但是,如果目标是函数本身的结果,如!! 结果或更一般的形式!! Result.make(...)?
这种创建指令不必被视为副作用.它不会更改任何现有对象,因此不会危及引用透明性(至少如果我们假设有足够的内存来分配我们需要的所有对象).从数学的角度来看,我们可能会假装所有感兴趣的对象,无论过去,现在和将来,都已经被铭刻在物体伟大的书中; 创建指令只是获取其中一个的一种方式,但它本身不会改变环境中的任何内容.函数创建,初始化和返回此类对象是常见且合法的.
(在面向对象的软件构建中,第754页)
在本书的其他地方,迈耶将这种功能定义为创作者功能.
由于CQRS是CQS的扩展并且维持[命令和查询]应该是纯粹的观点,我倾向于说CQS中保持CQS的例外情况也是如此.
此外,CQS和CQRS之间的主要区别之一是将命令和查询具体化为自己的对象.在CQRS中,存在额外的间接级别,"例程"没有直接引用域对象.对象查找和修改委托给命令处理程序.它削弱了,IMO,使"命令无法返回"的原因成为可能的原因之一,因为上下文现在无法自行检查操作的结果 - 它基本上是高而干,直到其他一些对象让它了解结果.