例如,当您提交注册表单时,您必须在Domain Model
(WriteModel
中CQRS
)中检查表单是否处于有效状态(例如,电子邮件地址语法,年龄等)。
然后,您创建一个Command
,并将其发送到Command Bus
。
我知道Commands不应该返回任何东西。
那么,您如何处理超出范围的错误Command Bus
呢?(例如,用户在1秒钟之前使用进行了注册username/email
)。
您如何知道该命令失败,以及如何知道该错误?
例如,当您提交注册表单时,您必须在Domain Model
(WriteModel
中CQRS
)中检查表单是否处于有效状态(例如,电子邮件地址语法,年龄等)。
然后,您创建一个Command
,并将其发送到Command Bus
。
我知道Commands不应该返回任何东西。
那么,您如何处理超出范围的错误Command Bus
呢?(例如,用户在1秒钟之前使用进行了注册username/email
)。
您如何知道该命令失败,以及如何知道该错误?
Answers:
我知道Commands不应该返回任何东西。
这是一个观点,但并不完全是一成不变的。考虑一下HTTP中的写入(PUT,POST,DELETE)-所有这些消息都是命令,从某种意义上来说,它们是请求资源更改状态的消息,但它们都返回响应。
那么如何处理Command Bus以外的错误?(例如,用户在1秒钟前使用相同的用户名/电子邮件注册)。
您如何知道该命令失败,以及如何知道该错误?
因此,在直接与命令处理程序通信的情况下,返回的消息是确认命令已被接收和处理的完美合理的方式。
如果您正在使用某种中间件(例如总线)阻止您直接与目标进行通信,那么我建议您使用异步消息传递模式-如何获取命令处理程序以将消息发送回目标呼叫者?
一种想法是订阅命令的结果。这借鉴了Hohpe的企业集成模式中的一些想法。基本思想是,由于客户端熟悉已发送的命令消息,因此可以很好地定位为订阅由于命令消息而发布的任何新消息。在将数据保存到记录簿后,命令处理程序会发布事件,宣布更改成功,并且客户端订阅这些事件-通过考虑消息中各种标识符的重合来识别正确的事件(原因ID,相关性ID,依此类推)。
替代方法更为直接。一种是在消息中包含回调,可以在成功处理消息后由命令处理程序调用该回调。
一个非常相似的替代方法是在命令消息中保留空间以供命令处理程序写入确认-由于客户端已经有问题的命令消息,因此电路已经完成。认为“ 承诺 ”或“可完成的未来”。 该消息告诉命令处理程序在何处写入确认。这样做会向客户端(倒计时锁存器)发出信号,确认已可用。
当然,您还有其他选择,即删除似乎妨碍简单地完成正确操作的中间件。
例如,一个用户在1秒钟前使用相同的用户名/电子邮件注册
如果您要全权处理用户注册,那不一定就是错误-在观察到响应之前重复发送消息是确保至少发送一次的常用方法。
例如,当您提交注册表单时,您必须在域模型(CQRS中的WriteModel)中检查其处于有效状态(例如,电子邮件地址语法,年龄等)
验证的类型很多。检查电子邮件地址语法和年龄格式时的验证是Command可以执行的一种验证。这不是真正的域问题。似乎是因为某些领域专家会告诉您这些规范,但是您应该在Command创建中进行这种验证。实际上,一般的想法是尽快进行验证,因为在创建命令并将其发送到BUS之后,采取操作会更加复杂。
那么如何处理Command Bus以外的错误?(例如,用户在1秒钟前使用相同的用户名/电子邮件注册)。
从CQRS的开始就在CQRS社区中讨论了很多此类验证。在哪里做?这是非常有争议的。我个人使用以下方法:在命令发送到BUS之前,我将用户名/电子邮件标记为集中使用(即,对用户名/电子邮件的唯一索引约束)。之后,我发送命令。缺点是,如果命令失败,则在很短的时间内将使用并且不使用用户名。这对我的业务来说是可以接受的,对您的业务来说也可能是可接受的。
您如何知道该命令失败,以及如何知道该错误?
如果异步命令由于域不变而被聚合拒绝,则必须通过发出某种补偿性命令采取某种措施,该补偿性命令以某种方式通知发出方(即,向解释性电子邮件发送该命令失败)。
重复电子邮件的问题在于您无法发送电子邮件,因为该电子邮件地址属于另一个人,因此我在将命令发送到总线之前先进行检查。
验证应在装饰器中执行。然后,任何需要验证的命令都可以这样修饰。
如果您的命令返回了void,则可以处理验证,因此可以通过同步或异步调用获取返回的任务结果来进行验证。
另一种可能性是将验证视为一种“查询”,它将返回验证结果。运行验证查询,然后(如果通过)运行命令。这将是装饰器方法的替代方法。