CQRS 中的验证是否必须在 UI 中和业务域中分别进行一次?
我最近读过这篇文章 CQRS à la Greg Young,我仍在努力了解 CQRS。
我不确定输入验证应该在哪里进行,以及是否可能必须在两个不同的位置进行(从而违反了“不要重复自己”规则,也可能违反了“关注点分离”)。
给定以下应用程序架构:
# +--------------------+ ||
# | event store | ||
# +--------------------+ ||
# ^ | ||
# | events | ||
# | v
# +--------------------+ events +--------------------+
# | domain/ | ---------------------> | (denormalized) |
# | business objects | | query repository |
# +--------------------+ || +--------------------+
# ^ ^ ^ ^ ^ || |
# | | | | | || |
# +--------------------+ || |
# | command bus | || |
# +--------------------+ || |
# ^ |
# | +------------------+ |
# +------------ | user interface | <-----------+
# commands +------------------+ UI form data
该域对命令总线后面的 UI 是隐藏的。也就是说,UI 只能向域发送命令,但永远不会直接访问域对象。
验证不得在聚合根对事件作出反应时发生,但要更早。
命令被转化为域中的事件(通过聚合根)。这是可能发生验证的地方:如果命令无法执行,则不会将其转换为相应的事件;相反,(例如)抛出一个异常,该异常通过命令总线向上冒泡,返回到 UI,并在那里被捕获。
问题:
如果命令无法执行,我想禁用用户界面中相应的按钮或菜单项。但是,在发送命令之前,我如何知道命令是否可以执行呢?查询端在这里对我没有帮助,因为它不包含任何业务逻辑;我在命令方面所能做的就是发送命令。
可能的解决方案:
对于任何命令DoX,引入一个相应的虚拟命令CanDoX,它实际上不会执行任何操作,但让域给出命令X是否可以无错误执行的反馈。
在 UI 中复制一些验证逻辑(真正属于域中的)。
显然,第二种解决方案并不有利(由于缺乏关注点分离)。但第一个真的更好吗?
I've recently read the article CQRS à la Greg Young and am still trying to get my head around CQRS.
I'm not sure about where input validation should happen, and if it possibly has to happen in two separate locations (thereby violating the Don't Repeat Yourself rule and possibly also Separation of Concerns).
Given the following application architecture:
# +--------------------+ ||
# | event store | ||
# +--------------------+ ||
# ^ | ||
# | events | ||
# | v
# +--------------------+ events +--------------------+
# | domain/ | ---------------------> | (denormalized) |
# | business objects | | query repository |
# +--------------------+ || +--------------------+
# ^ ^ ^ ^ ^ || |
# | | | | | || |
# +--------------------+ || |
# | command bus | || |
# +--------------------+ || |
# ^ |
# | +------------------+ |
# +------------ | user interface | <-----------+
# commands +------------------+ UI form data
The domain is hidden from the UI behind a command bus. That is, the UI can only send commands to the domain, but never gets to the domain objects directly.
Validation must not happen when an aggregate root is reacting to an event, but earlier.
Commands are turned into events in the domain (by the aggregate roots). This is one place where validation could happen: If a command cannot be executed, it isn't turned into a corresponding event; instead, (for example) an exception is thrown that bubbles up through the command bus, back to the UI, where it gets caught.
Problem:
If a command won't be able to execute, I would like to disable the corresponding button or menu item in the UI. But how do I know whether a command can execute before sending it off on its way? The query side won't help me here, as it doesn't contain any business logic whatsoever; and all I can do on the command side is send commands.
Possible solutions:
For any command DoX, introduce a corresponding dummy command CanDoX that won't actually do anything, but lets the domain give feedback whether command X could execute without error.
Duplicate some validation logic (that really belongs in the domain) in the UI.
Obviously the second solution isn't favorable (due to lacking separation of concerns). But is the first one really better?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为我的问题刚刚被另一篇文章解决了, Udi Dahan 澄清了 CQRS。 “命令和验证”部分的开头如下:
我认为这意味着 - 考虑到我有一个基于任务的 UI,正如经常建议的 CQRS 工作良好(命令作为域动词) - 我仅当由于命令所需的某些数据仍然丢失或无效而无法发送命令时,按钮或菜单项才会变灰(禁用); IE。 UI 对命令本身的有效性做出反应,而不是对命令对域对象的未来影响做出反应。
因此,不需要 CanDoX 命令,也不需要将域验证逻辑泄漏到 UI 中。然而,UI 将具有一些用于命令验证的逻辑。
I think my question has just been solved by another article, Clarified CQRS by Udi Dahan. The section "Commands and Validation" starts as follows:
I take this to mean that — given that I have a task-based UI, as is often suggested for CQRS to work well (commands as domain verbs) — I would only ever gray out (disable) buttons or menu items if a command cannot yet be sent off because some data required by the command is still missing, or invalid; ie. the UI reacts to the command's validness itself, and not to the command's future effect on the domain objects.
Therefore, no CanDoX commands are required, and no domain validation logic needs to be leaked into the UI. What the UI will have, however, is some logic for command validation.
客户端验证基本上仅限于格式验证,因为客户端无法知道服务器上数据模型的状态。现在有效的内容可能在 1/2 秒后就失效了。
因此,客户端应仅检查是否填写了所有必填字段以及它们的格式是否正确(电子邮件地址字段必须包含有效的电子邮件地址,例如 (.+)@(.+) 形式。 (.+) 等)。
然后,所有这些验证以及业务规则验证都会在命令服务中的域模型上执行。因此,在客户端验证的数据仍可能导致服务器上的命令无效。在这种情况下,一些反馈应该能够返回到客户端应用程序......但那是另一个故事了。
Client-side validation is basically limited to format validation, because the client side cannot know the state of the data model on the server. What is valid now, may be invalid 1/2 second from now.
So, the client side should check only whether all required fields are filled in and whether they are of the correct form (an email address field must contain a valid email address, for instance of the form (.+)@(.+).(.+) or the like).
All those validations, along with business rule validations, are then performed on the domain model in the Command service. Therefore, data that was validated on the client may still result in invalidated Commands on the server. In that case, some feedback should be able to make it back to the client application... but that's another story.