我将简化我的问题:
我的LightsState API可以接收两种类型的输入:lightOn {lightId: ##}
和lightOff {lightId: ##}
。 (AMQP输入,但此处无关)
这些输入可以很好地转换为2个命令:TurnLightOnCmd
和TurnLightOffCmd
。
这些命令将创建2个事件:LightTurnedOnEvent
和LightTurnedOffEvent
。
这些事件将应用于Light Aggregate
,并且持久的投影将成为state of the light
。
一切顺利,直到这里。
但是因为没有输入:create light
,所以我不能从中输入CreateLightCmd
。我只能在收到带有新CreateLightCmd
的{{1}}输入时调用lightOn
来创建lightId
,然后在其上应用Light Aggregate
。>
我不确定如何处理此问题以及是否遵循良好的CQRS惯例。
是否可以从命令方调用查询方以检查是否TurnLightOnCmd
,然后根据需要首先调用light exists by id
?
还是应该在Command端进行数据库查询,并保持Command和Query端分离?
还是对此有其他解决方案?
谢谢
答案 0 :(得分:2)
可以从命令端调用查询端以检查light是否存在id,然后根据需要首先调用CreateLightCmd吗?
并非如此-引入了比赛条件,其后果可能不会使您满意。
评论:DDD + CQRS + ES在架构上与单独的DDD非常相似。基本概念是我们将信息持久存储到存储设备(也称为“数据库”)中。该模型在一个过程中运行,该过程从数据库加载当前状态,使用该命令计算新状态,然后将该新状态存储在数据库中。
在进行事件源搜索时,也采用相同的模式-我们从用于写入的数据库中读取历史记录,计算新事件,并将这些事件附加到历史记录中。
但是:创建模式很奇怪。
当我们尝试查询以前从未见过的标识符的历史记录时,我们将得到一个null或None
或其中没有任何事件的历史记录,或其他内容这样。
惊奇的是:那是很好。
在您的用例中,您不一定要使用CreateLightCmd
;相反,当您获得应隐式创建光源的其他命令之一时,您想生成一个新的LightCreatedEvent
。
使用伪代码:
TurnLightOn (cmd) {
history = getHistory(cmd.lightId)
if (history.isEmpty) {
history.append(LightCreatedEvent.from(cmd))
}
history.append(LightTurnedOnEvent.from(cmd))
save(cmd.lightId, history)
}
答案 1 :(得分:0)
看看Udi Dahan的开创性文章“不要创建聚合根” [0]。关键点是:
这意味着发出命令时,应始终具有一个已将实际的聚合根实体初始化为默认状态的聚合。在您的情况下,默认状态为“熄灭”。您在此指示灯上执行命令,现在它处于“点亮”状态。现在,将其保存到数据库中并创建它。除非您的域关心LightCreatedEvent,否则不需要对其建模。
[0] http://udidahan.com/2009/06/29/dont-create-aggregate-roots/