【问题标题】:Understanding the Command pattern in a DDD context了解 DDD 上下文中的命令模式
【发布时间】:2018-06-08 00:43:15
【问题描述】:

我最近在这里阅读这篇文章:https://cuttingedge.it/blogs/steven/pivot/entry.php?id=100。它似乎在谈论使用命令 (http://www.dofactory.com/net/command-design-pattern) 而不是应用程序服务。

请看下面的代码:

public sealed class ShipmentController
{
    private readonly ICommandDispatcher dispatcher;

    public void ShipOrder(ShipOrder cmd) => dispatcher.Dispatch(cmd);
}

sealed class CommandDispatcher : ICommandDispatcher
{
    private readonly Container container;

    public void Dispatch(dynamic cmd) => GetHandler(cmd.GetType()).Handle(cmd);

    private dynamic GetHandler(Type type) =>
        container.GetInstance(typeof(ICommandHandler<>).MakeGenericType(type));
}

替换如下代码:http://www.zankavtaskin.com/2013/11/applied-domain-driven-design-ddd-part-6.html

我有三个问题:

1) 这是否是说应用服务层中的每个命令请求都应该有一个命令?这不会导致班级爆炸,例如如果你有 100 个命令?

2) 您如何处理 CQRS 查询?您是否为这些创建常规应用程序服务?

3) 你如何处理从数据库中提取的场景(比如订单);对订单执行命令,例如CalculateTax 然后持久化到数据库?我假设流程是(对吗):

MVC 
Application Service (to extract order from database)
Command (Application Service calls CalculateTaxCommand)

【问题讨论】:

  • 1) No. Http 请求和command request 是两个完全不同的东西。是的,对于command request,您至少有 3 个类:commandhandlerresponse。 2) 不。你和commands 有同样的原则,除了查询是幂等的(他们不能修改任何东西)。这意味着您可能需要封装您的数据库访问权限,以便没有人可以编写修改某些内容的查询 3)您将:a)发送查询 b)执行命令到 calculatetax c)执行命令以更新数据库跨度>
  • @zaitsman,你能确认每个查询/命令都封装在自己的类中吗?
  • 这是一个理论 :) 所以是的,它应该是唯一能保证分离水平的东西。 DDD 和 CQRS 的全部意义在于它允许将您的应用程序分解为几行孤立的代码,这在 20 多名开发人员和数千个操作的团队中更容易重构。通常所有的命令、查询和处理程序都继承一些接口或基类。另请查看 MediatR (github.com/jbogard/MediatR)
  • 这只是一个 c# 问题吗?
  • @zaitsman 幂等并不意味着他们不能修改任何东西,这意味着一个函数可以多次执行但第一次执行后结果不会改变。

标签: c# domain-driven-design


【解决方案1】:

它似乎在谈论使用命令而不是应用程序服务。

不,它没有谈论命令设计模式。 Command design pattern 与我的博客和其他地方描述的类似 CQRS 的模式之间有一个非常明确且至关重要的区别。

Command 设计模式中的“命令”将 databehavior 结合在同一个类中。另一方面,对于 CQRS,命令只是一个消息,一个数据容器,没有任何行为。行为被移至“处理程序”类。处理程序与旧的“应用程序服务”相同,区别在于处理程序的范围非常狭窄。这种分离是实现此设计的可维护性和灵活性的驱动因素。

  1. 这是否是说应用服务层中的每个命令请求都应该有一个命令?这不会导致班级爆炸,例如如果你有 100 个命令?

开发人员很常见的误解是认为系统中的类型数量与其可维护性之间存在直接的反向关系;类的增加意味着可维护性的降低。

然而,SOLID design patterns 偏爱小而专注的类而不是大类,因为缩小类实际上可以极大地提高系统的可维护性。q

这正是这里发生的事情。这个设计应该从SOLID的角度来看。我的经验是,在我重构为该模型的系统中,我们看到可维护性大幅提高了一个数量级,尽管类的数量也可以增加一个数量级。

提示:不要担心系统中的课程数量。只需担心可维护性。

但这并不意味着项目结构无关紧要——它肯定是相关的。您应该为您的命令、它们的处理程序和它们的装饰器找到一个好的项目结构。在 this blog postthe comments 中,有一些关于如何构建项目的想法。

  1. 您如何处理 CQRS 查询?您是否为这些创建常规应用程序服务?

您对查询执行的操作与对命令执行的操作完全相同。每个查询都应该有自己的查询消息和处理程序,以及可选的结果消息类。 This blog post 描述了如何设计查询。

  1. 您如何处理从数据库中提取的场景(例如订单);对订单执行命令,例如CalculateTax 然后持久化到数据库?我假设流程是(对吗):

这是一个原子操作,都应该是命令的一部分。执行命令时,会根据在命令中捕获的 ID 从数据库中加载订单。税款被计算出来,订单作为该(业务)交易的一部分被保留。

【讨论】:

  • 感谢您的全面回答。 +1。是命令设计模式还是 CQRS 命令使用 Mediatr(由我的 OP 下的 cmets 中的 zaitsman 引用)?
  • MediatR 绝对是 CQRS 架构设计风格的一种形式,它是一个有趣的资源,可以从中学习并将其用作您自己设计的灵感。
  • 这个 CQRS 模式是否“正式”记录在任何地方?我知道您和 Jimmy Bogard(我经常阅读的两个博客)都提到过它,但是我在它的起源处徘徊。
  • 还有一个很棒的PluralSight course about CQRS and distributes systems design 值得一看,但请注意,我和 Jimmy 谈论的这个特殊设计本身就是 AFAIK,在该课程中没有提到(这证明它们是两个不同的东西) .
  • 我没有在我的博客上写任何关于命令验证的特别文章。有很多方法可以做到这一点,但我通常从在我的命令中使用 DataAnnotation 属性开始,并实现一个装饰器,在命令到达处理程序之前启动它的验证。对于更复杂的验证,请定义一个单独的 IValidator&lt;T&gt; 接口,以便您可以为特定命令提供验证器实现。装饰器也可以启动这些验证器的验证。
猜你喜欢
  • 1970-01-01
  • 2021-02-23
  • 2020-07-02
  • 2011-04-14
  • 1970-01-01
  • 2018-09-08
  • 2021-01-28
  • 2011-05-30
  • 2018-12-15
相关资源
最近更新 更多