saga中的saga(A Saga on Sagas)

时间:2022-04-24 14:14:24

此文翻译自msdn,侵删。

原文地址:https://msdn.microsoft.com/en-us/library/jj591569.aspx

Process Managers, Coordinating Workflows, and Sagas

分清术语

saga这个名词通常被用在CQRS的讨论中,它是指一段在限定上下文(bounded contexts )和聚合(aggregates)之间起协作和路由(coordinates and routes )消息作用的代码。然而,在这个指南中我们更喜欢用Process manager这个词语去表示saga。有两个原因:

  • 之前已经有了一个广泛被熟知的名词saga,这个saga和CQRS中的saga有着不同的含义。
  • process manager 是一个更合适用来描述saga在这里扮演角色的名词。

虽然saga经常在CQRS模式中见到,然而它已经在这之前有了其自己的含义。在这个指南中我们使用process manager 以便于和saga之前的含义区分开来。

saga,与分布式相关,最早被定义在Hector Garcia-Molina和Kenneth Salem的论文"Sagas"中。这篇论文提出了一个saga机制来作为分布式事务的替代品以解决长时间运行的分布式事务(long-running process)的问题。这篇论文认为业务过程经常由很多步骤组成,每个步骤都涉及一个事务,如果将这些事务组成一个分布式事务,就可以实现总体一致(overall consistency )。然而在长时间运行的分布式事务中,使用分布式事务会影响效率和系统的并发处理能力,因为在执行分布式事务的时候会有锁产生。

注意:

saga通过确保每一个业务过程都有修正事务(compensating transaction)来减少了系统对分布式事务的依赖。在这种方式下,如果业务过程遇到了错误的情况并且无法继续,它就可以执行修正事务来修正已经完成的步骤。这种在业务流程中去撤销已经完成的工作的方式保证了系统的一致性。

尽管我们已经选择使用process manager这个名词了,sagas仍然在实现CQRS系统中的限定上下文中扮演着一些角色。比如说,你可能会希望看到process manager在一个限定上下文中的聚合中路由消息,你也可能会希望看到saga管理一个在多个限定上下文中长时间运行的业务过程。

以下的几个部分描述了process manager,这个是我们在我们的CQRS之旅项目中对saga的定义。

注意:

在使用process manager之前,我们的团队曾经有一段时间使用coordinating workflow 这个名词。这种模式在Gregor Hohpe 和 Bobby Woolf的书"Enterprise Integration Patterns”中有所描述。

Process Manager

这一节概括了我们的process manager定义。在描述process manager之前有一段简短的关于CQRS使用消息(messages)在聚合和限定上下文中通讯的回顾。

消息和CQRS

当你实现CQRS模式的时候,你可能会思考两种类型的消息如何在你的系统中交换数据:command和事件。

command是一种请求,他们请求系统去执行一个任务或者动作。例如“预定两个X会议的座位”或者“把演讲者Y分配到Z室”。command通常只被一个接收者执行一次。

事件是一种通知,他们告诉系统中一些它们感兴趣的部分:有一些事情已经发生了。例如“支付被拒绝了”或者“产生了X类型座位”。注意他们使用的是过去式——事件已经被产生并且可能有许多订阅者。

通常来说,command被发送到同一个限定上下文中。事件的订阅者可能在它们发布的限定上下文中,或者在其他的限定上下文中。

引用指南中的"A CQRS and ES Deep Dive"章节详细地介绍了这两种不同的消息类型。

process manager是什么?

在一个复杂系统建模中,你可能已经使用了聚合和限定上下文,他们可能有一些包含了很多聚合的业务过程,或者在一个限定上下文中有很多的聚合。在这个业务过程中,许多不同类型的消息被交换。例如,在一个会议管理系统中,有一个预定座位的业务过程包含了order聚合,一个reservation聚合和一个payment的聚合。他们必须结合起来以保证一个客户能够完成预定交易。

图1表示了一个简化的消息场景,在这个场景中这些聚合互相交互来完成这个订单。数字表明了消息流转的顺序

注意:

这个图并没有展示这个实现如何处理订单

saga中的saga(A Saga on Sagas)

图1

不使用process manager的订单处理过程

在图1展示的例子中,每一个聚合都向与流程下一步相关的聚合发送一个相应的command。Order聚合首先发送一个MakeReservation 的command给Reservation聚合来预定客户需要的位置。在座位被预定之后,Reservation聚合就会发送一个SeatsReserved事件通知Order聚合,然后Order聚合就会向Payment聚合发送一个MakePayment的command。如果付款成功,Order聚合就会生成一个OrderConfirmed的事件通知Reservation聚合确定座位已经被预定,并且通知客户订单完成。

saga中的saga(A Saga on Sagas)

图2

使用process manager 的订单业务过程

图2中展示的例子描述了和图1一样的业务过程,但是这次使用了process manager 。现在,这些消息通过process manager 来管理,而不是聚合根直接向另外一个聚合跟发送消息。

这样明显地将整个过程复杂化了:这个过程现在多了一个对象(process manager )和一些消息。然而这些都是有益的。

首先,聚合不再需要知道流程的下一步是什么。之前Order聚合必须知道在完成预定之后,它需要尝试着去向Payment 聚合发送一个消息来完成一个支付。现在它只要报告一个订单已经生成就可以了。

然后,这个消息流的处理被限定在一个地方,那就是process manager,而不是分散到这些聚合中。

在一个类似于图1或者图2的简单业务过程中,这么做的好处很小。然而如果你有一个包含六个聚合和数十个消息的业务过程,这么做的好处就很明显了。这尤其在一个系统中某个业务容易发生变化的部分中更加明显:在这种情况下,业务的改变更容易被限定在有限的对象中。

在图3中,为了描述这个观点,我们向其中引入了一个等待列表。如果一些作为预定请求不能被预定,那么系统就会把这些请求添加到一个等待列表中。在发布SeatsReserved 事件(报告还有多少座位可以被预定)功能的基础上,我们为Reservation 聚合新增了一个发布SeatsNotReserved 事件的功能去报告还有多少座位不可以被预定。process manager可以向WaitList 聚合中发送一个command来添加一个待处理请求。

saga中的saga(A Saga on Sagas)

图3

包含一个process manager 和一个等待列表的订单业务过程

很重要的一点需要注意,process manager本身不包含任何业务逻辑。它仅仅路由消息,或者在一些情况下转换消息类型。例如,当它接受到一个SeatsNotReserved事件,它发布一个AddToWaitList的command。

我应该什么时候使用process manager?

有两个重要的使用process manager的原因:

  • 当你在一个限定上下文使用很多事件和command的时候,难以把聚合之间的交互(point-to-point interactions between aggregates)统一管理。
  • 当你想要在限定上下文中更容易地去修改消息的路由。一个process manager会提供一个单独的路由定义位置。

我应该什么时候不使用process manager?

下面列了不适用process manager的原因:

  • 如果你的限定上下文仅包含很少数量的使用少量的消息的聚合类型。
  • 你不可以在你的领域中使用process manager来实现任何业务逻辑。业务逻辑应该在相应的聚合类中。

sagas和CQRS

尽管我们在之前的章节中已经选择了process manager这个名词,saga仍然在使用CQRS模式的系统中扮演一定的角色。比如说,你可能会希望看到process manager在一个限定上下文中的聚合中路由消息,你也可能会希望看到saga管理一个在多个限定上下文中长时间运行的业务过程。