作者:和谐啄木鸟 | 2023-08-12 13:27
我们正在尝试CQRS.我们有一个验证情况,其中CustomerService(域服务)需要知道客户是否存在.客户的电子邮件地址是唯一的.我们的客户存储库(通用存储库)仅具有Get(id)和Add(customer).CustomerService应该如何确定客户是否存在?
1> Kevin Swiber..:
看一下这篇博客文章:CQRS架构中基于集合的验证.
它解决了这个问题.在CQRS中处理这是一个复杂的问题.Bjarte建议的是查询报告数据库中的现有客户电子邮件地址,并CustomerEmailAddressIsNotUniqueCompensatingCommand
在找到电子邮件地址时向域模型发出补偿命令(例如).然后,您可以启动适当的事件,其中可能包括UndoCustomerCreationEvent
.
阅读上述博客文章中的评论,了解其他想法.
Adam D.在评论中建议验证是一个域名关注点.因此,您可以将ReservedEmailAddresses存储在便于客户创建的服务中,并通过事件存储中的事件进行补充.
我不确定是否能够轻松解决这个问题,感觉非常干净.让我知道你想出了什么!
祝好运!
如其他答案中所述,您不应该对视图模型执行查询作为处理命令的一部分.如果有的话,那些查询应该在发送命令之前由客户端执行,选择不根据结果发送命令.
第三方应用程序将通过一个行为良好的"客户端"连接,该客户端可以是一个Web服务,它是对视图模型进行检查的应用程序,如果检查通过,则将单向消息发送到"服务器" ".
@Udi Dahan我真的没有从这个用户名示例的SOA角度了解这是如何工作的.当我收到CreateNewUserCommand时,我当然需要检查用户名是否已经存在?我不能假设客户已经完成了这项检查,特别是如果客户端可能是第三方应用程序?我理解在处理命令时不执行查询的基本原理,但我可以想到一些场景,在处理命令时我需要分析命令要在其上运行的聚合之外的数据......
作为David评论基础的SOA的重点是支持集成和扩展等.因此,最基本的规则之一是永远不要信任.因此,我们应该在提交之前始终验证/验证.
@Udi Dahan关于一个表现良好的客户,这是一个公平的观点.考虑一下,这是一个非常明显的解决方案!
@ inf3rno或当您的投影或事件总线卡住时.想象一下周末的这种情况以及重复执行`CreateNewUserCommand`的用户,最终为同一用户提供100多个用户聚合.这真的是一个巨大的失败点,似乎没有人知道如何正确解决.(我也不知道,否则谷歌不会把我带到这里:p)
@Udi Dahan,但是如果有几个客户端,则必须在所有客户端中实现相同的行为.行为当然是域代码,不属于应用程序层.
@andho软件不仅仅是图层,而且可以将相同的组件部署到多个层.
我意识到这可能是一个陈旧的帖子,但是,在充分尊重Udi之后,我们可以而且应该编码信任客户已经执行了所有必要验证的想法与多年来在现实世界中教授和学到的许多课程相悖.只有你绝对,积极地,100%保证你可以信任来电者才真正适用.除非您在一个封闭的环境中开发,在那里您控制所有内容并且不支持集成或扩展(或团队开发),否则这是不现实的.在其他情况下(多数?),你必须采取防御措施.
@DavidMasters"当我收到CreateNewUserCommand时,我当然需要检查用户名是否已经存在?" - 您可以使用查询数据库进行检查,但由于最终的一致性,查询数据库不需要与事件存储同步,因此很少,但您可能会得到错误的结果.在这种情况下,2个用户将使用相同的电子邮件地址注册,因此您必须进行补偿,例如删除其中一个.这可能发生在例如一个糟糕的UI中,你可以通过2次点击两次发送相同的内容...
2> Mark Lindell..:
这个问题不一定非常复杂:
在提交UpdateCustomer命令之前,请检查报告存储区以获取客户唯一性.
向数据库添加约束以获取电子邮件地址的唯一性.执行命令时,处理异常并使用回复通道向用户发送通知.(因此永远不会将CustomerUpdated事件发送到报表存储区.
使用数据库可以获得有用的功能,并且不要对ORM限制感到困惑.
如果它不是数据库怎么办?
并发命令怎么样?可以在某些(罕见但可能)的情况下创建两个客户.
3> t0PPy..:
Udi Dahan的这篇文章http://www.udidahan.com/2009/12/09/clarified-cqrs/包含以下段落:
"此外,我们不需要访问查询存储来处理命令 - 任何需要的状态都应该由自治组件管理 - 这是自治意义的一部分."
我相信Udi建议简单地向数据库添加一个唯一约束.
但是如果你不想这样做,基于上面的陈述,我建议只是将"ByEmail"方法添加到存储库并完成它 - 但是再次Udi可能会有更好的建议..
这在所有情况下都不起作用,例如,我使用的NoSQL数据库不支持"约束"的概念.