为什么用Handle()分隔类CommandHandler而不是Command本身的处理方法


13

我有一部分使用S#arp Architecture实施的CQRS模式,如下所示:

public class MyCommand
{
    public CustomerId { get; set; }

    // some other fields
}

public class MyCommandHandler<MyCommand> : ICommandHandler<MyCommand, CommandResult>
{
    Handle(MyCommand command)
    {
        // some code for saving Customer entity

        return CommandResult.Success;
    }
}

我想知道为什么不只Command包含数据处理方法的类?这是一种可测试性的好处,您需要与命令属性分开测试命令处理逻辑吗?还是某些频繁的业务需求,您需要由不同的实现来处理一个命令ICommandHandler<MyCommand, CommandResult>


我有一个同样的问题,值得一看:blogs.cuttingedge.it/steven/posts/2011/…–
rdhaundiyal

Answers:


14

有趣的是,这个问题使我想起了我与一位工程师就我正在研究的通信库进行的完全相同的对话。

除了命令之外,我还有Request类,然后有RequestHandlers。设计非常像您所描述的。我认为您感到困惑的部分原因是您看到英文单词“ command”,然后立即想到“ verb,action ... etc”。

但是在此设计中,请将Command(或Request)视为一个字母。或者对于那些不知道邮政服务是什么的人,请考虑发送电子邮件。它只是内容,与应该如何操作内容脱钩。

你为什么要这样做?在大多数简单情况下,“命令模式”没有理由,您可以让此类直接执行工作。但是,如果您的动作/命令/请求必须经过一段距离,则按照设计中的方式进行去耦是有意义的。例如,跨越套接字或管道,或者在域和基础结构之间。或者,也许在您的体系结构中,您的命令需要持久化(例如,由于某些系统事件,命令处理程序一次只能执行1条命令,因此有200条命令到达,并且在关闭前40个进程之后)。在那种情况下,只有一个仅用于消息的类,将消息部分序列化为JSON / XML / binary / whatever就非常简单,然后将其传递到管道中,直到其命令处理程序准备好对其进行处理为止。

将Command与CommandHandler分离的另一个优点是,现在您可以选择并行继承层次结构。例如,所有命令都可以从支持序列化的基本命令类派生。也许您在20个命令处理程序中有4个具有很多相似性,现在您可以从Comed处理程序基类派生这些命令处理程序。如果要将数据和命令处理放在一个类中,则这种类型的关系很快就会失控。

解耦的另一个示例是,如果您的命令只需要很少的输入(例如2个整数和一个字符串),而其处理逻辑就足够复杂了,您希望将数据存储在中间成员变量中。如果将50个命令排队,则不想为所有中间存储分配内存,因此将Command与CommandHandler分开。现在,您排队了50个轻量级数据结构,并且处理命令的CommandHandler仅分配了一次(或者,如果您有N个处理程序,则分配了N次)更复杂的数据存储。


关键是,在这种情况下,命令/请求不是远程的/持久的等。它是直接处理的。而且我看不出将两者分开对继承有何帮助。这实际上会使它变得更难。最后一段也很想念。创建对象不是昂贵的操作,而50个命令是可以忽略的数字。
欣快2014年

@Euphoric:您如何知道上下文是什么?除非S#arp Architecture有点特殊,否则我所看到的只是几个类声明,并且您不知道如何在其余的应用程序中使用它们。如果您不喜欢我选择的数字50,请选择每秒50的数字。如果这还不够,请每秒选择1000。我只是试图提供示例。还是您不认为在这种情况下他会有那么多命令?
DXM 2014年

例如,在这里可以看到确切的结构weblogs.asp.net/shijuvarghese/archive/2011/10/18/…。那里什么也没说你说的话。关于速度,问题在于您使用了“性能”参数而不进行性能分析。如果您确实对这种吞吐量有要求,那么您将不使用通用架构,而是构建更专业的架构。
欣快2014年

1
让我看看这是否是您的最后一点:OP询问示例,例如,我应该首先设计简单的方法,使应用程序正常工作,然后进行扩展,并扩展使用命令模式的位置,然后您上线使用后,您有10,000台计算机与服务器通信,并且服务器仍使用原始体系结构,然后对问题进行分析并确定问题,然后可以将命令数据与命令处理分开,但只有在进行分析之后。如果我把所有这些都包括在答案中,这真的会让你更快乐吗?他问一个例子,我给他一个。
DXM 2014年

...所以我只是浏览了您发布的那篇博客文章,它似乎与我写的内容保持一致:如果您的命令必须走一段距离,请将它们分开。在博客中,他似乎指的是一条命令总线,它基本上只是另一个管道,套接字,消息队列,esb ...等
DXM 2014年

2

普通命令模式是将数据和行为放在单个类中。这种“命令/处理程序模式”略有不同。与普通模式相比,唯一的优势是不让您的命令依赖于框架的其他优势。例如,您的命令可能需要数据库访问权限,因此它必须具有某种数据库上下文或会话,这意味着它取决于框架。但是此命令可能是您域的一部分,因此您不希望它根据Dependency Inversion Principle依赖于框架。将输入和输出参数与行为分开,并让一些调度程序将其连接起来可以解决此问题。

另一方面,您将失去继承和命令组合的优势。我认为这是真正的力量。

此外,小nitpick。仅仅因为它的名称中包含Command并不会使它成为CQRS的一部分。那是更基本的东西。这种结构甚至可以同时用作命令和查询。


我已经看到了weblogs.asp.net/shijuvarghese/archive/2011/10/18/…的链接,您已经指出,但是在S#arp Arch代码中没有看到总线的迹象。因此,我猜想,在我看来,这种分离只会散布类并飞溅逻辑。
rgripper 2014年


嗯,谢谢你指出。然后我的情况就更糟了,因为在我的代码中,我将ICommandProcessor进行了IOC并解析为CommandProcessor(它本身正在为命令处理程序制作IOC)-浑浊的组成。而且在该项目中,似乎没有任何业务案例可以为一个命令提供多个哈夫勒。
rgripper 2014年
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.