脈絡:Content-Based Router 與 Splitter 兩個模式中的訂單處理例子,處理的是由多個明細項目組成的進站訂單。每個項目都要向對應的庫存系統做庫存檢查;所有項目都驗證完之後,我們要把已驗證的訂單訊息交給下一個處理步驟。
先把已知模式接起來#
這個問題似乎包含了我們已定義的幾個模式的成分:
- Splitter 能把單一訊息拆成多份
- Content-Based Router 能依內容或型別把子訊息路由到正確的處理步驟
- Pipes and Filters 讓我們能把這兩者串接起來
於是每個訂單項目都被路由到適當的庫存系統驗證——庫存系統彼此解耦,而且各自只收到自己能處理的項目。
但還缺了一塊#
這個配置的不足在於:我們無法得知所有被訂購的項目是否都有庫存、能否出貨。我們還需要取得所有項目的價格(並計入量販折扣)並組成單一張發票——這要求我們在把訂單切成許多子訊息之後,仍然能像「它還是一則訊息」那樣繼續處理。
走不通的作法:各系統各自成單#
一種作法是把「流經某個庫存系統的所有項目」重組成一張獨立的訂單,此後各自獨立處理:各自履行、出貨、開帳單。
有時候我們對下游流程缺乏控制,這可能是唯一可行的解法。Amazon 對其銷售的大部分商品就採用這種作法——訂單被路由到不同的履行中心,從那裡各自管理。
但這對客戶體驗未必是好事:客戶可能收到不只一批貨、不只一張發票,退貨或爭議也可能難以處理。
消費者訂書時這不算大問題,但當個別訂單項目彼此相依時就麻煩了:假設訂單是一組層架家具的各個部件,客戶收到好幾個大箱子的家具元件,卻發現必需的安裝五金暫時缺貨、要等之後才寄——他不會太高興。
也走不通:逐一等待#
訊息系統的非同步本質讓任務分派比同步方法呼叫複雜。我們可以派出一個訂單項目、等回應回來再檢查下一個——這樣時序相依單純多了,卻讓系統極沒效率。我們希望善用「各系統能同時處理訂單」這個事實。
解法#
Composed Message Processor 用一個 Aggregator 來調解那些被派送到多個庫存系統的請求:每個處理單元回送一則說明該品項庫存量的回應訊息,Aggregator 收集這些個別回應,並依預先定義的演算法處理它們。
因為所有子訊息都源自單一訊息,我們可以把額外資訊(例如子訊息的數量)傳給 Aggregator,以定義更有效率的聚合策略。
儘管如此,Composed Message Processor 仍得面對訊息遺失或延遲的問題:
- 若某個庫存系統不可用,我們要延遲處理所有含有該系統品項的訂單嗎?
- 還是把它們路由到一個例外佇列,交由人工評估?
- 若只是少了單一個回應,該不該重送庫存請求訊息?
模式的可組合性#
這個模式展示了個別模式可以組合成更大的模式。
對系統的其餘部分而言,Composed Message Processor 看起來就像一個「單一輸入通道、單一輸出通道」的簡單過濾器——它為內部較複雜的運作提供了有效的抽象。