1、引言

在前面四篇文章中,我們把物流中臺的基礎能力層構建了起來,接下來,我們就可以在這些基礎能力之上構建我們的產品服務,從而支撐各條業務線。
基礎能力層主要關注的是穩定可用的原子介面,因此在設計的時候重點關注了很多高併發高可用的技術。產品服務層主要是為了支撐不斷創新的業務,因此在設計的時候需要關注業務流程的可擴充套件性。下面我們會先從整體上設計出一套抽象模型,然後針對模型的各個模組深入介紹。

1、整體設計

寫過業務程式碼的同學一定知道,我們的系統往往是由service層、manager層和遠端服務層組成。這三層中,service層定義了對外的介面,manager層處理各種業務邏輯,主要是資料校驗、資料組裝、呼叫其他應用的(dubbo服務,遠端服務層是對dubbo服務的包裝,比較簡單。當業務不斷膨脹的時候,我們可能會遇到下面的情況:
1、service層的介面越來越多,相似的業務定義多套介面
2、manager程式碼越來越臃腫,各種if else巢狀,每次改一點都需要測試迴歸整塊業務
3、遠端服務,特別是查詢服務,在一條業務流程中的多個地方被重複呼叫,從而導致整體響應時間下降

對於第一個問題,我們需要仔細分析業務,然後抽象出通用的介面,比如建立物流單,業務上既可以建立快遞訂單,也可以建立倉儲訂單,我們需要定義一個通用的下單介面,而不是針對快遞和倉儲分別設計兩套訂單介面。然後,我們在入參和出參上進行可擴充套件的設計,比如使用繼承、組合的方式,在保證入參模型基本不變的情況下可以支撐不同的業務,這樣一來,新業務接入的時候,就不需要更改介面,只需要擴充套件入參物件就行。

對於第二個問題,我們把manager的業務邏輯抽象為三個步驟:校驗、資料轉換、執行。快遞有快遞的校驗、資料轉換、執行,倉儲有倉儲的校驗、資料轉換、執行,service呼叫manager的時通過策略模式路由。這樣一來,各個業務都是不同的程式碼流程,不存在修改一點會影響全域性,測試只需要關注被修改的流程,新增的業務線只需要新增一條業務流程然後配置一下路由就可以了,也不會影響老業務。

針對第三個問題,我們可以規定把所有的資料查詢都放在manager的資料轉換步驟,然後通過流程上下文去傳遞資料,這樣就不會把查詢服務擴散到整個流程了。

總結下上面的三個問題的解決方案,我們可以抽象出下面的業務流程模型:


2、介面層設計

產品服務層的介面服務遵循request-response風格,每個方法的入參都是一個request,出參都是response。request和response通過繼承和組合進行擴充套件。

2.1、通過繼承擴充套件

request往往可以通過繼承進行不同業務的擴充套件。以物流發貨來說,發貨方式有快遞發貨和倉儲發貨之分,這兩種發貨方式的入參不太一樣,這時候我們可以把公共的引數抽出來,然後不同的引數下沉。我們會定義一個LogisticsServiceDTO物件,快遞和倉儲分別繼承它,得到ExpressLogisticsServiceDTO和WarehouseLogisticsServiceDTO,然後各自定義引數,最後我們就可以得到如下圖所示的一個request物件:


以後如果還有其他新的發貨方式出現,我們只需要定義新的LogisticsServiceDTO就可以了,介面的基本結構幾乎不需要改變。

2.2、通過組合擴充套件

response往往可以通過組合來擴充套件不同的業務,比如使用者物流單詳情頁,需要展示物流單資訊、使用者資訊和地址資訊,我們可以定義一個OrderDetailResponse,這個物件組合了LogisticsOrderDTO、UserDTO、AddressDTO等物件。通過組合進行擴充套件的方式比較常見,在此就不展開講了。


2.3、如何定義方法

根據我們的經驗,定義方法往往需要結合業務的生命週期,拿物流介面LogisticsService來舉例,我們可以定義createLogistics、consignLogistics、logisticsCallback這三個方法。
a)、createLogistics
createLogistics是建立物流訂單方法,通過定義可擴充套件的request物件,可以支援快遞、倉儲、配送等等物流訂單的建立
b)、consignLogistics
consignLogistics是發貨方法,通過定義可擴充套件的request物件,可以支援快遞、倉儲、配送等等物流訂單的發貨
c)、logisticsCallback
logisticsCallback是物流回傳方法,通過定義可擴充套件的request物件,可以支援倉接單、倉分揀、倉出庫、配送攬收、配送派送、配送簽收等等物流回傳過程
三個方法基本上涵蓋了絕大部分業務場景,如果出現新的業務場景,首先需要評估現有的方法是否支援業務流程,如果不支援,再新增方法,比如有些物流場景存在多段發貨(典型的就是農村淘寶),這時候一個consignLogistics肯定是不夠的,這時候就需要擴充套件方法。在擴充套件方法的時候,一開始可以就為特殊的業務開特殊的介面,等後面業務越來越多後,將這些特殊介面統一整理一次整理成一個通用的介面,這個過程就是一個沉澱業務模式的過程,是無法通過提前設計設計出來的,必須經過業務的不斷沉澱才能跑出來。


3、介面實現層設計

說完了介面層的設計,接下來就要說說介面的實現層怎麼設計。按照整體設計的思路,我們把業務切分成一條一條的流程,然後把每條流程切分成校驗、資料轉換、執行三個節點,然後通過入參來決策走哪條流程。因此,一個介面的實現層其實就是一個流程決策+流程節點執行的過程。
流程決策其實就是在一張對映表裡獲得需要執行的節點列表,這裡獲取的方法可以是普通的雜湊對映,也可以通過複雜的規則引擎獲取。拿到節點列表(一般是節點類名)後,就可以通過spring容器獲取節點物件。所有的流程節點,都會繼承同一個抽象節點物件,然後實現同一個方法:execute方法,因此,通過spring容器獲取節點物件後,就可以通過反射執行節點。節點與節點之間往往需要傳遞資料,因此節點的execute方法需要傳入一個context物件,A節點在執行後將資料傳入context物件,B節點執行的時候通過這個context物件獲取A的資料,這就打通了節點之間的資料通道。

將上面的抽象模型帶入具體的業務,我們可以得到下面的架構:


以建立物流單為例,所有的業務程式碼都按照業務分散在不同的流程節點裡,入口處通過策略路由選擇流程,同一個流程規定了三個節點,三個節點之間通過流程上下文傳遞資料,三個節點通力合作完成不同業務的物流單的建立過程。
如果從更巨集觀的視角來看,產品服務層和基礎能力層組合起來就是下面的架構:


產品服務層對外提供service,每個service都定義了高度聚合的業務方法,每個業務方法通過策略路由和流程編排,把業務程式碼進行隔離,業務流程中的每個節點,呼叫基礎能力層的原子介面進行一系列業務操作。

4、總結

產品服務平臺總的思路其實就是一條非常古老的原則:對擴充套件開放、對修改關閉。我們通過繼承和組合,讓介面的出入參變得可擴充套件,通過策略模式和流程節點的組合,讓新老業務程式碼隔離,進而提升了擴充套件性。當然,凡是有利必有弊,產品服務平臺在提升了可擴充套件性的同時,增加了一定的程式碼編寫量,同時對分散式事務沒有太好的辦法。


更多文章歡迎訪問 http://www.apexyun.com/

聯絡郵箱:[email protected]

(未經同意,請勿轉載)