此文翻譯自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表示了一個簡化的消息場景,在這個場景中這些聚合互相交互來完成這個訂單。數字表明了消息流轉的順序
注意:
這個圖並沒有展示這個實現如何處理訂單
圖1
不使用process manager的訂單處理過程
在圖1展示的例子中,每一個聚合都向與流程下一步相關的聚合發送一個相應的command。Order聚合首先發送一個MakeReservation 的command給Reservation聚合來預定客戶需要的位置。在座位被預定之后,Reservation聚合就會發送一個SeatsReserved事件通知Order聚合,然后Order聚合就會向Payment聚合發送一個MakePayment的command。如果付款成功,Order聚合就會生成一個OrderConfirmed的事件通知Reservation聚合確定座位已經被預定,並且通知客戶訂單完成。
圖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來添加一個待處理請求。
圖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管理一個在多個限定上下文中長時間運行的業務過程。