我们以一个简单的“账户注册”为例,流程如下:
- 用户访问网站
- 点击“注册”按钮并填写表格,点击“保存”按钮
- MVC 控制器:通过读取 ReadModel 来验证用户名的唯一性
- RegisterCommand:再次验证用户名唯一性(这是问题)
当然,我们可以通过读取MVC控制器中的ReadModel来验证UserName的唯一性,以提高性能和用户体验。然而,我们仍然需要在RegisterCommand中再次验证唯一性,显然,我们不应该在命令中访问 ReadModel。
如果我们不使用事件溯源,我们可以查询域模型,所以这不是问题。但是如果我们使用事件溯源,我们将无法查询域模型,因此我们如何验证 RegisterCommand 中的用户名唯一性?
Notice:User 类有一个 Id 属性,而 UserName 不是 User 类的关键属性。使用事件溯源时,我们只能通过Id获取领域对象。
BTW:需求中,如果输入的用户名已被占用,网站应向访问者显示错误消息“抱歉,用户名XXX不可用”。不可接受向访问者显示“我们正在创建您的帐户,请稍候,我们稍后会通过电子邮件将注册结果发送给您”的消息。
有任何想法吗?非常感谢!
[UPDATE]
一个更复杂的例子:
要求:
下订单时,系统会检查客户的订单历史,如果他是有价值的客户(如果客户去年每月至少下了10个订单,他就是有价值的),我们对该订单给予10%的折扣。
执行:
我们创建PlaceOrderCommand,在命令中,我们需要查询订购历史记录,看看该客户是否有价值。但我们怎样才能做到这一点呢?我们不应该在命令中访问 ReadModel!饰演 米凯尔said https://stackoverflow.com/a/9496198/290617,我们可以在账户注册的例子中使用补偿命令,但是如果我们在这个排序的例子中也使用它,那就太复杂了,而且代码可能太难维护了。
如果您在发送命令之前使用读取模型验证用户名,那么我们正在讨论一个几百毫秒的竞争条件窗口,其中可能会发生真正的竞争条件,而在我的系统中不会处理这种情况。与处理它的成本相比,这种情况发生的可能性太小了。
但是,如果您觉得出于某种原因必须处理它,或者您只是想知道如何掌握这种情况,那么这里有一种方法:
使用事件源时,不应从命令处理程序或域访问读取模型。但是,您可以做的是使用域服务来侦听 UserRegistered 事件,在该事件中您再次访问读取模型并检查用户名是否仍然不重复。当然,您需要在此处使用 UserGuid,并且您的读取模型可能已使用您刚刚创建的用户进行了更新。如果发现重复,您有机会发送补偿命令,例如更改用户名并通知用户该用户名已被占用。
这是解决问题的一种方法。
正如您可能看到的,不可能以同步请求-响应方式执行此操作。为了解决这个问题,每当我们想要将某些内容推送到客户端时(也就是说,如果它们仍然处于连接状态),我们就会使用 SignalR 来更新 UI。我们所做的是让 Web 客户端订阅包含对客户端立即查看有用的信息的事件。
Update
对于更复杂的情况:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)