如何处理命令中的验证后错误(DDD + CQRS)


18

例如,当您提交注册表单时,您必须在Domain ModelWriteModelCQRS)中检查表单是否处于有效状态(例如,电子邮件地址语法,年龄等)。

然后,您创建一个Command,并将其发送到Command Bus

我知道Commands不应该返回任何东西。

那么,您如何处理超出范围的错误Command Bus呢?(例如,用户在1秒钟之前使用进行了注册username/email)。

您如何知道该命令失败,以及如何知道该错误?


2
您不需要事件总线。我不知道为什么每个人都认为实现CQRS时需要事件总线。CQRS只是意味着您将写和读完全分开,分开考虑。只要您这样做,就可以拥有CQRS。您的命令甚至不需要异步。如果带有错误报告的同步命令对您有用,请执行此操作。
安迪

1
Commands should not return anything when successful. The idea started from CQS, which had the implication that the command could still throw an exception on error. What you're describing is a one-way command. See this answer in relation to that.
Kasey Speakman

Answers:


4

我知道Commands不应该返回任何东西。

这是一个观点,但并不完全是一成不变的。考虑一下HTTP中的写入(PUT,POST,DELETE)-所有这些消息都是命令,从某种意义上来说,它们是请求资源更改状态的消息,但它们都返回响应。

那么如何处理Command Bus以外的错误?(例如,用户在1秒钟前使用相同的用户名/电子邮件注册)。

您如何知道该命令失败,以及如何知道该错误?

因此,在直接与命令处理程序通信的情况下,返回的消息是确认命令已被接收和处理的完美合理的方式。

如果您正在使用某种中间件(例如总线)阻止您直接与目标进行通信,那么我建议您使用异步消息传递模式-如何获取命令处理程序以将消息发送回目标呼叫者?

一种想法是订阅命令的结果。这借鉴了Hohpe的企业集成模式中的一些想法。基本思想是,由于客户端熟悉已发送的命令消息,因此可以很好地定位为订阅由于命令消息而发布的任何新消息。在将数据保存到记录簿后,命令处理程序会发布事件,宣布更改成功,并且客户端订阅这些事件-通过考虑消息中各种标识符的重合来识别正确的事件(原因ID,相关性ID,依此类推)。

替代方法更为直接。一种是在消息中包含回调,可以在成功处理消息后由命令处理程序调用该回调。

一个非常相似的替代方法是在命令消息中保留空间以供命令处理程序写入确认-由于客户端已经有问题的命令消息,因此电路已经完成。认为“ 承诺 ”或“可完成的未来”。 该消息告诉命令处理程序在何处写入确认。这样做会向客户端(倒计时锁存器)发出信号,确认已可用。

当然,您还有其他选择,即删除似乎妨碍简单地完成正确操作的中间件。

例如,一个用户在1秒钟前使用相同的用户名/电子邮件注册

如果您要全权处理用户注册,那不一定就是错误-在观察到响应之前重复发送消息是确保至少发送一次的常用方法。


2

例如,当您提交注册表单时,您必须在域模型(CQRS中的WriteModel)中检查其处于有效状态(例如,电子邮件地址语法,年龄等)

验证的类型很多。检查电子邮件地址语法和年龄格式时的验证是Command可以执行的一种验证。这不是真正的域问题。似乎是因为某些领域专家会告诉您这些规范,但是您应该在Command创建中进行这种验证。实际上,一般的想法是尽快进行验证,因为在创建命令并将其发送到BUS之后,采取操作会更加复杂。

那么如何处理Command Bus以外的错误?(例如,用户在1秒钟前使用相同的用户名/电子邮件注册)。

从CQRS的开始就在CQRS社区中讨论了很多此类验证。在哪里做?这是非常有争议的。我个人使用以下方法:在命令发送到BUS之前,我将用户名/电子邮件标记为集中使用(即,对用户名/电子邮件的唯一索引约束)。之后,我发送命令。缺点是,如果命令失败,则在很短的时间内将使用并且不使用用户名。这对我的业务来说是可以接受的,对您的业务来说也可能是可接受的。

您如何知道该命令失败,以及如何知道该错误?

如果异步命令由于域不变而被聚合拒绝,则必须通过发出某种补偿性命令采取某种措施,该补偿性命令以某种方式通知发出方(即,向解释性电子邮件发送该命令失败)。

重复电子邮件的问题在于您无法发送电子邮件,因为该电子邮件地址属于另一个人,因此我在将命令发送到总线之前先进行检查。


1

验证应在装饰器中执行。然后,任何需要验证的命令都可以这样修饰。

如果您的命令返回了void,则可以处理验证,因此可以通过同步或异步调用获取返回的任务结果来进行验证。

另一种可能性是将验证视为一种“查询”,它将返回验证结果。运行验证查询,然后(如果通过)运行命令。这将是装饰器方法的替代方法。


我喜欢异常方法,因为这是最干净的方法。但是,难道这不是太重了吗?
EresDev

1
嗨@EresDev-是的,他们很重。但是,我不知道您的数据有多“有效”。可以说1000条记录中有2条无效。异常可能是可行的,因为它们确实是例外情况。现在考虑1000条记录,其中200条无效。因此,应避免出现异常,因为20%的数据无效。因此,由您决定是否根据您当前的数据有效性选择这种方法。:)
乔恩·雷诺

我要说的是,如果我们保留注册表单示例,我认为50%甚至更多的用户首先插入无效数据,那么例外仍然可以。在Web应用程序后端中,由于前端也在执行验证,因此您通常会获得有效数据。只有在某些事情由于某些罕见的机会错过了前端验证或某人在鬼混时,您才能得到。因此,在这种情况下,例外情况就可以了。
EresDev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.