命令处理程序和DDD


10

我有一个ASP.NET MVC应用程序,该应用程序使用查询服务来获取数据,并使用命令服务来发送命令。我的问题是关于命令部分的。

如果有请求进入,则命令服务将使用命令调度程序,该命令调度程序会将命令路由到其指定的命令处理程序。该命令处理程序首先验证命令,如果一切都可以接受,它将执行命令。

具体示例:AddCommentToArticleCommandHandler接收一个AddCommentToArticleCommand,该命令具有ArticleId,CommentText和EmailAddress。

第一; 必须进行验证,例如:-检查文章是否存在-检查文章是否未关闭-检查注释文本是否在20到500个字符之间,以及是否填写了电子邮件地址-检查电子邮件地址是否具有正确的格式。

我想知道将验证放在哪里?

1 /在命令处理程序本身中。但是,它不能在其他命令处理程序中重用。

2 /在域实体中。但是,由于域实体不了解存储库或服务,因此它无法执行所需的验证(无法检查文章是否存在)。但是另一方面,如果实体不包含逻辑,则它将变成一个简单的数据容器,不遵循DDD原理。

3 /命令处理程序使用验证器,以便可以在其他命令处理程序中重用验证。

4 /其他机制?

我正在寻找此特定示例的责任链,以及哪些对象(实体,存储库,...)在其中起作用。

从命令处理程序到存储库,您是否有实现此想法的想法?


“但是,由于域实体不了解存储库或服务,因此它无法进行所需的验证”?为什么不?DDD的什么原理要求?
S.Lott

我到处都读到一个实体不应该知道的存储库或其他信息。
L-四

您能否提供您所读内容的报价,链接或示例?我们不知道您正在阅读哪些DDD描述。
S.Lott


吉米·博加德(Jimmy Bogard)就此主题分享了一些看法... lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world
Mayo

Answers:


4

我认为在这种情况下,您需要将两种验证类型分开;域验证应用程序验证

验证命令属性“文本”在20到200个字符之间时,您将进行应用程序验证。因此,您可以使用GUI以及在POST之后也在服务器上执行的view-model-validator对此进行验证。电子邮件也是如此(顺便说一句,我希望您认识到,根据RFC,像32.d +“ Hello World .42” @mindomän.local“这样的电子邮件是有效的)。

然后,您还有另一个验证;检查文章是否存在-如果确实存在从GUI发送的关于为其添加注释的命令,则必须问自己一个问题,为什么不应该存在该文章。您的GUI最终是否一致,并且您具有可以从数据存储中物理删除的汇总根(即文章)?在这种情况下,您只需将命令移至错误队列,因为命令处理程序无法加载聚合根。

在上述情况下,您将拥有处理有毒消息的基础结构-例如,它们将重试该消息1-5次,然后将其移动到Poision队列中,您可以在其中手动检查消息的集合并重新分发相关消息。监视是一件好事。

所以现在我们讨论了:

  • 应用验证

    • 在GUI中使用javascript
    • 通过Web服务器上的MVC验证
  • 缺少聚合根+毒物队列

与域不同步的命令呢?也许您在域逻辑中有一条规则说,对一篇文章发表5条评论后,只允许使用400个字符以下的评论,但是一个人对第5条评论为时已晚,而成为第6条-GUI没有抓住它,因为这与他发送命令时的域不一致-在这种情况下,您的域逻辑中有一个“验证失败”,您将返回相应的失败事件。

该事件可以是到消息代理或您的自定义调度程序上的消息形式。如果应用程序是单块的,则Web服务器可以同步侦听所提到的成功事件和失败事件,并显示适当的视图/部分视图。

通常,您具有自定义事件,这意味着许多类型的命令都会失败,并且从Web服务器的角度来看,正是您预订了此事件。

在我们正在使用的系统中,我们通过MassTransit + RabbitMQ消息总线+经纪人对命令/事件进行请求-响应,并且在此特定域中有一个名为的事件(部分模拟了工作流程)InvalidStateTransitionError。大多数尝试沿着状态图中的边缘移动的命令可能会导致此事件发生。在我们的案例中,我们根据最终一致的范例对GUI进行建模,因此我们将用户发送到“命令接受”页面,然后让Web服务器的视图通过事件订阅进行被动更新。应该提到的是,我们也在汇总根中进行事件源(也将对Sagas进行)。

因此,您看到,您正在谈论的许多验证实际上是应用程序类型的验证,而不是实际的域逻辑。如果您的域很简单,但您正在执行DDD,则使用简单的域模型没有问题。但是,当您继续对域进行建模时,您会发现该域可能不像最初那样简单。在许多情况下,聚集的根/实体可能只接受由命令引起的方法调用并更改其某些状态,甚至不执行任何验证-尤其是如果您像在网络服务器中验证命令那样信任命令,由您控制。

我可以推荐观看2011年挪威开发者大会上有关DDD的两个演讲以及Greg在Öredev2010上的演讲。

干杯,亨克


谢谢!关于验证的一件事:目前,该验证由UI触发(通过向服务发出Validate(command)请求),该UI使用Comment实体本身的Validate()。然后,如果该命令有效,则客户端发出Execute命令,该命令将再次执行Validate()以确保确定,并且如果有效,则执行实际命令。因此,主要验证由Comment实体完成,因为在每种情况下验证都是相同的(电子邮件始终必须有效,注释文本不能太长,等等),这是一种好方法吗?
L-四

在我看来,您似乎要描述的验证似乎并不能保证使用两阶段验证协议对其进行建模-当然,像在单元测试中进行测试一样,对实体上的方法调用参数进行验证,但是除此之外;应用程序层/ GUI可以轻松验证您正在描述的规则,而无需将命令发送到域。伊莫 除非客户端是恶意的,否则该命令将起作用。如果客户端是恶意的,则该命令将失败,并且您的读取模型将永远不会收到相应的事件,您可以检查错误队列中有问题的命令。
亨里克

在UI(ASP.NET MVC)中,我使用验证属性来执行所有验证。因此,如果此验证成功,那么我不必在域上再次进行验证,而只需执行命令?
4

1
是的,您执行该命令,但请确保该命令在应用程序层和域中均无效。
亨里克

0

编辑:WaybackMachine链接:http ://devlicio.us/blogs/billy_mccafferty/archive/2009/02/17/a-response-to-validation-in-a-ddd-world.aspx

没有轻拍的答案。

当我尝试回答验证应该放在哪里时,会想到两个明确的项目方案。首先是DTO层用于在视图层和域层之间传输信息。例如,您可能在您的域层中有一个Customer对象,而在另一层中有一个关联的Customer DTO,您将客户信息映射到该层,然后提供给视图以向用户显示客户信息。

第二种情况是一个项目,其中域层内的实体直接与视图共享,以将数据呈现给用户。例如,代替维护单独的DTO层并将域对象映射到要提供给视图的DTO,可以直接为视图提供Customer对象。因此,该视图无需通过单独维护的DTO来显示客户的姓名,而只是向Customer对象本身索取信息。

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.