虽然启发式的内容肯定比这里所提到的要多,但是这里的这些内容很可能足够证明了。即使讨论交互也是一个相当漫长、繁琐的过程。这很容易造成误解。您可以通过这种方式理解各种命令/事件消息传递交互,但是这种方式不是很有效。
备注:一般来说,一个人在任何时候都只能在脑子里保持四到八个不同的想法。为了说明这一概念,让我们保守地计算一下你需要在短期记忆中同时保持的东西的数量,同时遵循上面的启发: 进程类型+进程状态属性+初始状态(NotStarted) + new()的位置+消息类型+中间路由类类型+ 2 *N^ N命令发出(位置、类型、步骤)+判别规则(逻辑也是数据!) > 8当基础设施需求混合到等式中时,信息饱和的问题会变得更加明显。作为我们都是有能力的开发人员(对吧?),我们可以开始寻找方法来优化这些步骤,并提高相关信息的信噪比。
总之,我们有两个问题:
我们被迫记在脑子里的东西太多,无法有效理解。
用于消息传递交互的讨论和文档冗长、容易出错且复杂。
幸运的是,使用MIL(消息传递中间语言)可以一举两得。
MIL一开始是一系列LINQPad脚本和代码片段,我创建这些脚本和代码片段是为了在回答问题时帮助处理所有事情。最初,这些脚本完成的所有工作都是通过一个或多个项目程序集反映并输出各种类型的消息和处理程序。在与团队成员的讨论中,很明显其他人也遇到了与我相同的问题。在与模式和实践团队成员进行了几次聊天和头脑风暴会议之后,我们提出了引入一种小型领域特定语言(DSL)的想法,该语言将封装所讨论的交互。暂时命名为SawMIL toolbox,它位于,它提供了实用工具、脚本和示例,使您能够将MIL用作开发和分析流程管理器的一部分。
在MIL中,消息传递组件和交互以特定的方式表示:命令(因为它们是系统执行某些操作的请求)用?表示,比如DoSomething?。事件表示系统中发生的确定的事情,因此获得一个!后缀,如SomethingHappened!
MIL的另一个重要元素是消息发布和接收。从消息源(如Azure服务总线、NServiceBus等)接收的消息总是在前面加上“->”符号,为了让示例暂时保持简单,有一个可选的nil元素(句号.)。用于显式地指示no-op(换句话说,没有接收到任何消息)。下面的代码片段展示了nil元素语法的一个例子:
SendCustomerInvoice? -> . CustomerInvoiceSent! -> .一旦发布了命令或事件,就需要对其进行处理。命令只有一个处理程序,而事件可以有多个处理程序。MIL通过将处理程序的名称放在消息传递操作的另一侧来表示消息与处理程序之间的这种关系,如下面的代码片段所示:
SendCustomerInvoice? -> CustomerInvoiceHandler CustomerInvoiceSent! -> -> CustomerNotificationHandler -> AccountsAgeingViewModelGenerator注意,命令和命令处理程序位于同一行,是因为命令和命令处理程序是1对1的。事件因为可能有多个事件处理程序,所以把他们放到多行上。
聚合根以@符号作为前缀,使用过twitter的人都会很熟悉它。聚合根从不处理命令,但偶尔可能处理事件。聚合根是最常见的事件源,它引发事件以响应在聚合上调用的业务操作。但是,关于这些事件应该清楚的一点是,在大多数系统中,有其他元素决定并实际执行领域事件的发布。这是一个有趣的案例,其中业务和技术需求模糊了边界,由基础设施逻辑而不是应用程序或业务逻辑来满足需求。旅程代码就是一个例子:为了确保事件源和事件订阅者之间的一致性,持久化聚合根的存储库的实现才是负责将事件实际发布到总线的。下面的代码片段显示了AggregateRoot语法的一个示例:
SendCustomerInvoice? -> CustomerInvoiceHandler @Invoice::CustomerInvoiceSent! -> .在上面的示例中,一个名为Scope上下文操作符的新语言元素出现在@AggregateRoot旁边。范围上下文元素由双冒号(::)表示,它的两个字符之间可能有空格,也可能没有空格,用于标识两个对象之间的关系。上面,聚合根 '@Invoice'生成CustomerSent!事件来响应CustomerInvoiceHandler调用的逻辑。下一个例子演示了在聚合根上使用Scope元素,它生成多个事件来响应单个命令:
SendCustomerInvoice? -> CustomerInvoiceHandler @Invoice: :CustomerInvoiceSent! -> . :InvoiceAged! -> .