如何用一套流程接入所有業務線?

陶然陶然發表於2024-04-15

  ToB業務沒有太多高併發的挑戰,但同一套流程往往可能需要承載各種差異化的複雜業務需求,所以如何讓系統具備良好的擴充套件性成為ToB業務系統最大的挑戰。本文將詳細講述如何用一套流程接入所有業務線?

  老系統改造不是一蹴而就的,從2022接手版權資產管理-財資系統之後,一直在進行架構重構和穩定性建設。將新吸納的優秀架構經驗融合,以業務為中心隨著需求迭代進行老系統的架構升級。過程中實踐和沉澱了一些不錯的B端開發方法論,整理成“老房改造系列”分享給大家。

  《老房改造系列--上線十年,81萬行Java程式碼的老系統如何重構》

  《老房改造系列--如何用一套流程接入所有業務線》

  《老房改造系列--穩定性摸排靈魂三問》

   前言

  ToB業務沒有太多高併發的挑戰,但同一套流程往往可能需要承載各種差異化的複雜業務需求,所以如何讓系統具備良好的擴充套件性成為ToB業務系統最大的挑戰。以版權資產管理-財資系統舉例,橫向需要承接財、法、商、boss四類角色,縱向要支援十幾條不同頻道業務線,這樣粗略計算會有近百維差異化業務需求,這會比C端的使用者畫像要複雜的多。如何用一套流程接入所有業務線?

  之前在《上線十年,81萬行Java程式碼的老系統如何重構》一文中有提到過,老系統重構的時候透過自上而下的方法進行流程拆解,再透過模板模式,用繼承重寫差異化method的方法進行差異化擴充套件。這種方法可以解決程式碼臃腫問題,也可以進行快速的擴充套件。但是當接入流程逐漸增多、流程差異化大小不一,會再一次引入一些壞味道。  

  

   問題

  還是以付款模組舉例,付款作為整個業務流程中最末端的節點,從流程圖上可以看出來付款本身並沒有特別複雜的業務邏輯,但是需要支援的業務卻特別的多,那就意味著付款這個服務必須要有很好的擴充套件能力才能支援快速接入。  

  第一版重構時使用的是模板模式進行擴充套件,但這樣設計會產生一些問題,首先從業務擴充套件實操上來說,使用模板模式進行擴充套件會出現倆個問題。

  1、繼承關係複雜:

  隨著接入業務逐漸增多繼承關係會越來越“胖”或者越來越“高”,當一個新的擴充套件品類來的時候,我們都需要決策一件事情,新來的品類是繼承最根部base類(變胖),還是找一個實現邏輯最相近的類來繼承(變高)。變胖帶來的後果就是複用性不好,一樣的實現邏輯可能會出現在多個流程中;變高帶來的後果是父類實現的修改有可能會影響子類的業務。  

  2、粒度過粗  

  當使用繼承重寫的方式進行擴充套件的時候,必須重寫整個方法。假設流程某一個步驟的method的實現中有3條規則,而新的流程步驟中只有第1條規則與父流程不同,但由於繼承關係的限制另外2條規則需要複製過來,這就又產生了重複邏輯程式碼,影響程式碼複用性。

  設計模式中也提到過“多用組合,少用繼承”的建議,原因如下  

  總結來說,“多用組合,少用繼承”強調的是以聚合的方式來組合物件的能力,這樣可以構造出更靈活、鬆散耦合、易於維護和擴充套件的設計模式。當然,在具體實踐中,並非完全排除繼承,而是倡導根據實際需求權衡使用,對於“is-a”的語義關係合理使用繼承,而對於“has-a”或者“can-do”的能力可以透過組合來實現。

   擴充套件體系建設

  為了解決上面的問題,做了幾件事情:梳理流程差異點,梳理領域模型,二次抽象隔離層,基於SPI的擴充套件體系建設。

  梳理流程差異點  

  還是前面那張圖,重新梳理後把有差異的部分標出來。

  業務流程:

  付款發起的來源很多,而且後續會越來越多;

  基於不同業務對於付款單的校驗邏輯也會有差異;

  提交審批後由於業務線不同,審批流初始化和審批流程肯定有差異。

  View層:

  付款單中要展示各自的業務單據資訊,用來輔助審批人決策;

  填寫表單的時候,下拉字典根據業務需求會有不同的選擇範圍;

  風險提示的卡片也會跟各自業務相關。

  許可權控制:

  基於身份的許可權控制是固定的,但是有業務單據許可權必然要有能檢視基於該單據發起付款的許可權。

  訊息同步:

  每個業務對於付款流程中的訊息的消費也各有不同。

  有了這張圖之後我們就知道流程中所有的業務差異點,可以輔助無遺漏的抽象擴充套件點。

  梳理領域模型  

  第二步進行了領域模型的梳理,梳理後可以看出來付款的核心域中,付款單據是聚合根。各個業務都可以發起付款,所以其他領域都算是付款的支撐域。但由於之前的付款單中會聚合引入其他域的單據作為付款依據,所以會導致每次接入新的業務型別,都需要重新引入新的業務單據,這也就導致了付款核心領域是不穩定的。核心域不穩定帶來的結果就是每次大量的升級和相容,那怎麼樣把不穩定因素隔離開?

  二次抽象隔離層  

  如上圖所示,這裡二次抽象出了付款憑證,付款單據的後續流程只依賴付款憑證,從其他業務域單據到付款憑證的Translator可以放在業務域來實現。這樣當付款域接入新業務時,核心域的程式碼是穩定不變的,即減少了相容邏輯程式碼也可以保證付款流程的穩定性。

  現在解決了核心域程式碼穩定的問題,但還是會有很多不同業務帶來的流程差異問題,如下:  

  基於SPI的擴充套件體系建設

  既然這些都是根據不同業務會帶來的差異,那我們可以將定義和實現倒置,由付款域定義介面,業務域來實現差異的部分,所以直接使用SPI的方式來擴充套件。行業上有很多SPI的實現方案,如何選擇?

  方案需要滿足倆個條件:

  1、根據業務身份獲取擴充套件實現

  2、接入成本:由於我們是老系統改造,所以改造要考慮ROI

  由於前三個不是天然滿足需求1,如果想用需要二次開發,付款中對於批次擴充套件點的需求很少,所以基於以上方案選型對比最後選擇COLA-SPI進行擴充套件體系建設。大家可以根據自身的業務來選擇合適的方案,沒有最完美只有最合適。

  最終擴充套件體系建設之後的架構圖如下:  

  付款的應用架構上在流程編排層和Domian層之間加入了擴充套件層,付款流程中呼叫的差異化部分,付款域來定義介面,接入業務域來做實現;在過程當中用到的一些付款域的通用方法,付款域來定義介面,付款域來實現。

  每個接入的業務域,都將業務差異化邏輯封裝到二方包裡,透過maven倉庫載入到付款域中。付款域應用啟動時會將掃描所有擴充套件點實現,並將業務身份與擴充套件點實現繫結( Map<Scenario,I***ExtPt> ),放入SpringContext中。一個付款流程執行時,會根據業務身份來mapping響應擴充套件點實現。cola-spi支援三維座標系,具體使用可以參考cola文件。

  擴充套件點的方案畢竟是要引入外部程式碼和呼叫外部服務,所以一定保證安全性和穩定性:  

  開放原則:如圖不詳述

  業務身份安全:二方包是透過maven倉庫引入進來,所以必須要保證二方包中有配置業務身份,並且保證引入的是release版本,這樣可以保證在不修改版本的前提下,業務身份是安全的。

  資料隔離:大部分B端業務系統都是要求有嚴格的資料隔離的,一旦被破壞會帶來資料洩露、合規等風險。

  MQ訊息隔離:傳統的解決方案是把所有訊息有生產方傳送,統一傳送到一個topic下,透過tag資訊來做隔離,但這樣就意味著可以接收到所有業務身份下的訊息。為了解決這個問題,這裡使用的方案是把訊息傳送的職責交給接入方,生產方只負責在可以發訊息的流程節點去呼叫擴充套件方法,傳入單據資訊,至於傳送到哪個topic、傳送哪些資訊、是否傳送都有接入方擴充套件實現。

  介面查詢隔離:由於擴充套件方法在實現過程中,一定會用到一些通用方法,比如反查歷史單據資訊等。此時如果介面不做資料隔離,那就有可能返回其他業務身份下的資料。所以需要攔截器進行方法引數的攔截,並且和業務身份進行校對。

  環境隔離:如圖不詳述

  邏輯穩定性:線上二方包引入release版本,可以保證包內邏輯的穩定和呼叫的RPC服務版本穩定,並且在所有擴充套件方法外進行異常處理。但還是會有RPC服務升級帶來的邏輯問題,目前沒有萬無一失的方法,只能從三方面來努力。

  1、前:嚴格遵守開放原則,並且在接入文件中說清楚返回結果的用途。

  2、中:對擴充套件點接入自動化測試手段

  3、後:加入擴充套件點座標系粒度的開關控制,發現問題及時止損

  流程依賴擴充套件點:如圖不詳述

   總結

  為了進一步解決流程差異化給B端服務帶來的挑戰,在老系統改造的場景下透過以下四個步驟來升級架構:

  ①梳理流程差異點,找到所有業務流程有差異的部分

  ②梳理領域模型,找出核心域和支撐域的邊界

  ③二次抽象隔離層,保證核心域的邏輯穩定

  ④基於SPI的擴充套件體系建設,實現基於不同業務身份的流程差異

  提升了業務流程的可擴充套件性,解決了之前架構使用模板模式帶來的變“高”變“胖”的問題。同時擴充套件體系將接入的業務邏輯交給接入方,也解決了對核心域開發人員對支撐業務域理解不審和核心域開發人力有限的問題。

  我相信這也不會是最完美的解決方案,隨著業務量的增多一定會帶來新的問題,我們需要做的就是不斷的學習和吸納,將新的思想融入到解決方案中,讓系統架構和開發者都處在一個良性的迴圈中。

來自 “ 阿里雲開發者 ”, 原文作者:墨詡;原文連結:https://server.it168.com/a2024/0415/6846/000006846147.shtml,如有侵權,請聯絡管理員刪除。

相關文章