只需要十分鐘,就可以教會你實現高層次的複用!

程式設計師阿軒發表於2020-12-13

作為開發人員,你對複用這個概念一定不陌生。在開發過程中,我們把系統中通用的程式碼邏輯抽取出來,變成公共方法或公共類,然後在多個地方呼叫,這就是最簡單的技術上的複用。

但一開始,我們不會過多地考慮複用,當一個新專案過來,我們會選擇最直接的方式來實現,結果往往是欲速而不達,比如說:

好不容易搞定了一個專案,接著又有新的類似專案過來,我們又要從頭再來;專案的程式碼是定製的,專案結束後,系統維護的噩夢剛剛開始。

如果專案缺乏沉澱,每個專案都是全新的開始,出現這些情況,一點都不意外。而要想解決這個問題,我們一開始就要考慮系統的複用性。

複用,它可以讓我們站在巨人的肩膀上,基於現有的成果,快速落地一個新系統

那麼,我們在做架構設計時,如何實現系統的高可複用呢?

今天,我就針對複用這個話題,首先和你介紹一下,複用具體都有哪些形式;然後,我會針對最有價值的業務複用,帶你瞭解如何劃分服務的邊界,讓你能夠在工作中,設計一個可以高度複用的系統。

複用的分類

複用有多種形式,它可以分為技術複用和業務複用兩大類。技術複用包括程式碼複用和技術元件複用;業務複用包括業務實體複用、業務流程複用和產品複用。

從複用的程度來看,從高到低,我們可以依次劃分為產品複用 > 業務流程複用 > 業務實體複用 > 元件複用 > 程式碼複用。

公司新來的阿里P7大牛,只用十分鐘就教會了我實現高層次的複用

 

接下來,我就按照複用度從低到高,對這些複用方式進行一一分析,幫助你更好地理解架構的可複用性。

技術複用

首先是程式碼級複用,這部分應該是你最熟悉的了。這裡包括你自己打包的類庫,第三方提供的 SDK,還有各種演算法封裝等。我們的程式碼可以直接呼叫它們,物理上也和我們的應用打包在一起,執行在同一個程式裡。程式碼級複用是最低層次的複用,你可以把它當作你自己原始碼的一部分。

再往上,是技術元件複用。這些元件有我們自己封裝的,更多的是大量開源的中介軟體,比如Redis、MQ、Dubbo 等;元件也包括各種開發框架,比如 Spring Cloud。這些基礎元件技術複雜度很高,它們的存在,極大地簡化了我們的開發工作。

值得注意的是,程式碼級複用和技術元件複用都屬於工具層面,它們的好處是在很多地方都可以用,但和業務場景隔得有點遠,不直接對應業務功能,因此複用的價值相對比較低。

業務複用

我們知道,系統最終是為業務而服務的,如果能夠實現直接的業務複用,那系統開發的效率就更高。在前面的課程中,我們討論架構的演進過程時,很多地方談到了業務能力的複用,比如說,微服務強調單個業務實體的封裝和複用,而中臺進一步實現了企業級業務能力的複用。

所以接下來,我們就從比較簡單的業務實體複用開始說起。

業務實體複用針對細分的業務領域,比如訂單、商品、使用者等領域。它對各個業務領域的資料和業務規則進行封裝,將它變成上層應用系統可以直接使用的業務元件。

業務流程的複用針對的是業務場景,它可以把多個業務實體串起來,完成一個端到端的任務。比如說,下單流程需要訪問會員、商品、訂單、庫存等多個業務,如果我們把這些呼叫邏輯封裝為一個下單流程服務,那下單頁面就可以呼叫這個流程服務來完成下單,而不需要去深入瞭解下單的具體過程。相比單個的業務實體複用,業務流程的複用程度更高,業務價值也更大。

最高層次的複用是對整個系統的複用,比如說一個 SaaS 系統(Software-as-a-Service),它在內部做了各種通用化設計,允許我們通過各種引數配置,得到我們想要的功能;或者說一個 PaaS(Platform-as-a-Service)平臺,它會提供可程式設計的外掛化支援,允許我們“嵌入”外部程式碼,實現想要的功能。

這種產品級的複用,它的複用程度無疑是最高的。這樣的系統,在落地的時候,它無需核心的開發團隊進行開發,只由外圍的實施團隊負責就可以了,這樣,一個專案的上線就能簡化為一次快速的實施,不但上線週期短,系統也更穩定。

當然,實現這樣的複用,難度也是很大的,你既要對所在行業的業務有很全面的理解,又要有很強的抽象設計能力。這類系統中,比較典型的有 Salesforce 的 CRM 系統和 SAP 的ERP 系統。

現在,我們先對複用做個總結。從技術複用到業務複用,越往上,複用程度越高,複用產生的價值也越大,但實現起來也越複雜,它能複用的場景就越有限。在實際工作中,技術層面上的複用相對比較簡單,我們對這部分的認知也最多,而且由於開源的普及,現在有豐富的中介軟體讓我們選擇,我們可以基於它們,逐步構建適合自己的技術體系。

但如果我們能進一步打造業務中介軟體,並在這個基礎上,形成業務平臺,這樣,我們就能實現更高的業務級複用,可以更高效地支援系統的快速落地

而在實現業務元件化和平臺化的過程中,首要的問題就是基礎服務邊界的劃分。邊界劃分決定了服務的粒度和職責,在實際工作中,也是非常困擾我們和有爭議的地方。

接下來,我就針對基礎服務邊界的劃分,和你分享我自己在專案開發的過程中,總結的一些實用的原則和做法。

基礎服務邊界劃分

服務邊界劃分要解決“我是誰”的問題,它實現了服務和周邊環境的清晰切割。

我們都知道,服務包含了業務資料和業務規則,並提供介面給外部訪問,其中,介面是服務的對外檢視,它封裝了服務的業務資料和規則。

所以從邊界劃分的角度來看,我們就是要確定哪些資料屬於這個服務,哪些介面功能由這個服務提供。這裡,我總結了 3 個基礎服務邊界劃分的原則,供你設計時做參考。

首先,是服務的完整性原則

你在劃分服務的邊界時,需要確保服務內部資料的完整性。

舉個例子,一個商品服務的資料模型,不僅要有商品基本資訊,比如商品名稱、價格、分類、圖片、描述等;還需要包含商品的擴充套件資訊,如商品的各種屬性、商品標籤等;最後還要包含各種複雜商品型別的定義,比如組合商品、套餐商品、多規格商品等。

另外,你還要保證服務功能的完整性。對於服務使用者來說,他們是以業務的角度看服務,而不是純粹的資料角度。比如一個套餐商品,在服務內部,它是多個單品的複雜組合,但從服務呼叫者的角度來看,它就是一個商品。

那現在問題來了,對於套餐的價格,商品服務是給出一個最終價格呢?還是給出各個單品的價格,然後讓呼叫方自己算最終價格呢?我們知道,套餐的價格不是各個單品價格累加的結果,它包含了一定的優惠,如果它的價格由服務呼叫方來算,這會導致商品的部分業務規則遊離於服務外面,破壞了商品服務的功能完整性。

在實踐中,有些服務只是儲存基礎資料,然後提供簡單的增刪改查功能,這樣一來,服務只是一個簡單的 DAO,變成了資料訪問通道。這樣的服務,它的價值就很有限,也容易被服務呼叫方質疑。因此,我們要儘可能在服務內部封裝完整的業務規則,對外提供完整的業務語義,最大程度地簡化服務的使用。

所以,當你在劃分服務邊界時,要保證服務資料完整、功能全面,這樣才能支撐一個完整的業務領域

其次,是服務的一致性原則

也就是說,服務的資料和職責要一致,誰擁有資訊,誰就負責提供相應的功能。

服務內部的業務邏輯要儘量依賴內部資料,而不是介面輸入的資料,否則會造成資料和業務規則的脫節(一個在外面,一個在裡面),如果服務對外部的依賴性很強,就無法提供穩定的能力了。

很多時候,我們對一個功能到底劃分到哪個服務,有很大的爭議。這時,我們可以結合這個功能所依賴的資料來判斷,如果功能所需要的大部分資料都儲存在 A 服務裡,那當然由 A服務來提供介面比較合適,這樣介面輸入的資料比較少,不但簡化了服務對外部的依賴,同時也降低了介面呼叫的成本。

給你舉個例子,在訂單小票上,我們經常能看到一些優惠資訊,比如說商品原價是多少,其中因為滿減優惠了多少,因為商品特價減免了多少。這個優惠計算的結果是訂單的一部分,毫無疑問,它需要儲存在訂單服務裡。

但這個訂單的優惠計算過程,卻不是由訂單服務來負責,而是由獨立的促銷服務負責的。因為優惠計算所需要的優惠規則是在促銷服務裡定義的,促銷服務可以在內部拿到所有的優惠規則,然後完成整個優惠計算。

否則,如果是由訂單服務負責優惠計算,訂單服務的呼叫者就需要在介面中提供完整的促銷規則,不但呼叫成本高,而且外部促銷規則的改變會影響訂單服務的內部實現。

所以在這裡,促銷服務負責促銷規則的維護,以及對應的優惠計算功能;訂單服務負責優惠結果資料落地,以及後續的查詢功能。這樣,每個服務儲存的資料和對外提供的功能是一致的。

最後一個,是正交原則

既然是基礎服務,它們就處於呼叫鏈的底層,服務之間不會有任何的呼叫關係,也就是說基礎服務相互之間是正交的。比如說會員服務和商品服務,它們代表不同維度的基礎業務域,彼此之間不會有呼叫關係。

正交還有另外一種情況:服務之間有資料的依賴關係,但沒有介面的呼叫關係。

比如說,訂單明細裡包含商品 ID 資訊,但訂單服務內部不會呼叫商品服務來獲取商品詳情。如果頁面需要展示訂單的商品詳情,針對這個具體的業務場景,我們可以在上層的聚合服務裡,通過聚合訂單服務和商品服務來實現。

總結

可複用是架構設計的一個重要目標,今天我們對複用進行了梳理,包括複用有哪些形式,以及它們有哪些價值,相信你現在對複用已經有了一個整體的認識。業務上的複用比純粹的技術複用有更高的價值,我們要儘量往這個方向上靠。

在實踐中,落地基礎服務是實現業務複用的有效方式,而基礎服務邊界的劃分,它有科學的成分,但更多的是一種藝術,這裡我提供了幾個實用的劃分原則,你可以在工作中結合實際情況,靈活地運用它們。

相關文章