博客 / 詳情

返回

如何確定微服務範圍

《微服務架構設計模式》有專門一個章節介紹如何界定微服務系統中服務的範圍。我參考書中內容結合自身多年微服務系統架構設計經驗,將定義微服務範圍的方法整理成本文。一來為了對自己的經驗和思路進行梳理,以查缺補漏並加深理解。二來方便和對此處內容感興趣的小夥伴交流學習。微服務拆分是一門藝術,沒有統一的評判標準,文章的內容也絕非圭臬必須嚴格遵循。

文章涉及到領域驅動設計相關內容,你不需要對DDD有多麼深入的理解,但是至少了解領域、子域、限定上下文、統一語言和領域模型等基礎概念。如果不熟悉請閲讀相關書籍,當然也可以直接閲讀本文,我會在遇到相關概念的時候加以解釋説明。確定微服務範圍簡單説可以分為三步:第一步明確領域範圍,第二步明確服務範圍,第三步明確服務依賴。下面我來逐一介紹:

一、明確領域範圍

當我們説一個組織的領域範圍的時候,其實就是在説一個組織所從事的所有業務,也就是系統需要實現的全部功能。設計任何一個系統之前首要任務就是明確系統領域範圍,也就是搞清楚系統提供哪些功能服務、包含哪些領域模型。例如電商系統從事電子商務業務,下單、付款、發貨、確認收貨等行為就是系統提供的功能,而這些功能是由用户、商鋪、快遞員、訂單、賬單、錢包、商品、收貨地址等領域模型支撐並實現的,系統功能和領域模型共同構成了系統的領域範圍。

確定一個系統的領域範圍的主要方式就是需求調研及需求分析。系統設計人員和組織內領域專家相互溝通協作,先輸出用例和用户故事等需求文檔,再基於這些文檔進一步分析以明確領域模型和功能範圍。需求調研階段形成用例或者用户故事已經站在用户的角度詳細描述了系統功能,我們可以在其中挑選動詞作為功能範圍的候選。當然需求文檔中不僅僅有動詞,還有充當主語或者賓語的名詞,而這些名詞就是領域模型的候選。詞性篩選只能作為初步分析手段,而進一步的分析就需要一定的經驗和技巧了。下面我來介紹一下自己的經驗之談:

  • 首先,要區分不同詞語但概念相同或者相同詞語但概念不同這兩個場景。還是舉例説明:錢包、賬户都是名詞,來自於不同需求描述,但是顯然他們含義相同,所以將他們統一稱為賬户領域模型。再舉一個例子,同樣叫做訂單,用户下單後生成的訂單和快遞員送貨的訂單顯然包含了不同的屬性,後者叫配送單可能更貼切。所以我們應該將這兩個概念視為兩個不同的領域模型。
  • 其次,要區分整體和部分的概念。餘額雖然也是名詞,但是他沒有專屬自己的方法,它的功能都是通過賬户這個概念整體對外體現,所以它不是一個獨立的領域模型而是賬户領域模型中的一個屬性。驗證產品是否下架這個功能也不是系統對外提供的服務,而是創建訂單操作中眾多步驟中的一個前置驗證條件,後者才是系統功能。當然,如果需求中就包含驗證產品是否下架這個功能,我們也可以將它定義為系統的功能範圍。
  • 再次,領域模型分析是一個循序漸進、逐步展開的過程,對於拿不準的概念可以稍後處理,在掌握更多信息後更容易做出判斷。
  • 最後,別忘了你的領域專家,遇到任何問題都應該和領域專家溝通,最終的領域模型也需要和領域專家確認。

分析過程我們已經介紹完了,下面介紹如何記錄分析的結果。可以使用UML中類圖表示系統中的領域模型,UML中類圖用於面向對象設計中描述類本身及類之間的關係。面向對象設計中的類和領域驅動設計中的領域概念一致,所以使用UML類圖來描述領域模型也是相當合適的。

圖1 電商系統領域模型示例
image.png

領域模型可以使用統一建模語言(UML)表示,系統的業務範圍就只能靠我們自己定義的格式記錄了。我們可以使用一張表記錄系統所有的功能,另一張表記錄某一個功能的詳細描述。前者叫系統功能表,後者叫功能描述表,分別對應表1和表2的格式示例。這兩個表的字段沒有嚴格的要求,你也可以自己定義格式及內容。

表1 系統功能清單示例
image.png

説明:

  • 操作人、操作對象都是領域模型。一個是操作的執行者,一個是操作的被執行者(物或人)

表2 功能描述清單示例

image.png
説明:

  • 前置條件是操作執行前需要滿足的先決條件,通常涉及到各種條件判斷。
  • 後置條件就是操作執行後的結果。
  • 前置、後置條件就是我們常説的業務規則。

二、明確服務範圍

經過上面步驟,相信你已經對系統的業務和功能有了一定的瞭解。在進一步設計前,我們先要搞清楚一個問題,之所以要將系統設計為微服務架構,一定是系統具有較高的業務複雜度,否則單體應用架構就可以勝任。那麼面對一個複雜的業務系統,降低複雜度最有效的方式就是將它拆分成一個個既相互獨立又互相關聯的功能模塊,這些功能模塊在DDD中稱為子領域或者子域。微服務架構就是應用了這個原理將整個業務系統拆分成一個個獨立的微服務架構模塊以降低系統複雜度,所以我們可以很自然的將DDD中子域的概念和微服務架構中服務的職責範圍聯繫起來,明確了系統的子域定義也就確定了微服務模塊的業務範圍。至此我們為如何劃分微服務業務範圍找到了指導原則和方法。

接下來我介紹如何定義子域。我們首先對上面步驟的產出系統功能表進行分析。通過觀察一定可以發現一系列行為有共同的操作對象(領域模型),還是用電商場景舉例,關於訂單的操作就有創建訂單、取消訂單、確認訂單、查看訂單詳情等,關於商品的操作也有上/下架商品、查看商品詳情、調整商品等。如果一個領域模型涉及到大量的操作,這就説明這是一個關鍵領域模型且包含複雜的業務邏輯,應當被劃分到獨立的子域中以降低整個領域的複雜程度。通常子域不會只包含一個關鍵領域模型,還包括與它關聯的領域模型。關聯模型可以在上一步驟中輸出的系統領域模型圖中查找。至此我介紹了一個完整分析流程,首先通過查找擁有複雜功能的關鍵領域模型作為初步劃分子域的基礎,然後通過關聯模型進一步補齊子域的能力和範圍。這種由下向上逐層彙總的方式很適合不熟悉的領域場景,而對於有一定了解的場景或者廣為人知的場景,更適合使用由上向下的方式定義子域範圍。這些場景一般都有比較明確的業務模塊劃分,例如電商業務就是由訂單、物流、庫存、支付等功能模塊構成的,我們可以直接將業務模塊定義為子域的範圍,先構建一個子域雛形,然後根據業務模塊的功能定義將相應的領域模型劃分到不同的子域中去。這兩種方式一個是先定義子域在補充領域模型,另一個先找到關鍵領域模型再定義子域,兩種方法沒有好壞之分,可以根據業務場景複雜度和領域知識熟悉度結合使用。

除了具體方法,還有一些抽象的原則可以作為定義子域範圍的依據,因為子域範圍就是微服務的範圍,下文中子域、服務、微服務就是相同意義的不同表達了。

  • 單一職責原則:一個服務應該僅有一個修改的理由。這個原則本來是用於面向對象設計如何定義類,但是用在這裏也毫無違和感。這個原則可以保證服務內的高耦合,想象一下如果我們需要增加一個禁止用户購買某種商品的需求,而這個需求需要修改訂單微服務,顯然訂單微服務的劃分就不夠不合理的,擴大了與訂單相關的業務範圍,需要進行裁剪。
  • 閉包原則:這個原則強調一個業務變更所修改的範圍要應該被限制在一個服務中,而不應該擴展到其他的服務,否則要考慮將兩個服務合併。這個原則可以保證服務間低耦合,試想一下,如果我們把訂單項服務從訂單服務中拆分出來形成獨立的微服務,那麼每次訂單服務的需求變更都可能要同步修改訂單項微服務,反之依然。顯然這種拆分就是不合理的,兩個服務還是耦合在一起的。
  • 2 Pizza原則:這個原則限制了一個微服務開發團隊的規模,8-12人也就是兩個披薩可以餵飽的團隊人數最佳。為什麼要限制團隊規模呢?微服務中拆分的目的除了降低單個服務的複雜程度外,還要降低了團隊內溝通的成本,而團隊內溝通成本是與團隊成員規模成正比的,如果團隊人員過多,一定説明服務功能複雜且團隊溝通成本巨大,這就違背了微服務架構設計的初衷。
    最後我們將分析結果記錄下來,生成微服務清單(詳情見表3),作為後面分析步驟的基礎。

表3 電商場景微服務清單示例

image.png

三、明確服務依賴

分析工作已經到了最後一步了,先回看一下前兩步我們得到的輸出內容,第一步輸出了系統功能清單,第二步輸出了微服務清單。接下來我們需要將功能和服務相對應,為每一個系統功能找到承載其能力的微服務。我介紹一個我自己經驗方法,通常功能都會有操作的領域模型,例如創建訂單中訂單就是操作的模型,在服務列表中也已經列出了服務所包含的領域模型,訂單服務包含訂單領域模型,既然功能操作的領域模型屬於訂單服務,那麼創建訂單操作理應屬於訂單服務。創建訂單這個例子可能顯而易見不用非要繞一圈判斷,但是遇到一些不好判斷的功能時,上面的方法就可以作為一種判斷依據。

有些系統功能只需要一個微服務就夠了,但是大部分功能都需要多個微服務相互配合共同支持。例如創建訂單操作的前置條件中就有很多判斷行為不屬於訂單服務的業務範圍需要其他服務支撐。遇到這種情況就需要先確定一個主要服務,然後再由這個服務調用其他支撐服務的功能,顯然創建訂單操作的主要服務就是訂單服務,而支撐服務就是用户服務和商品服務。經過以上分析最終可以得到微服務關係列表(表四),通過這張表我們就可以明確微服務的功能範圍和服務間對應關係,在配合前面兩個步驟輸出內容,可以有效的指導微服務的開發工作。

表4 電商場景微服務關係列表示例

image.png

四、單體工程改造場景

讀到這裏你可能也發現了一個問題,上面介紹的內容似乎都是從零開始設計並搭建的新系統,如果我們的任務是對一個已有的單體應用系統進行微服務改造,我們又該怎麼辦呢?其實上面的方法同樣適用,但是略有不同。因為系統已經存在所以通常情況下就不需要需求調研階段了,可以直接進入後續分析階段。在明確領域範圍階段,我們也不能使用需求文檔為分析依據,而是通過閲讀代碼進行分析工作並輸出相應的文檔,後面的分析階段就相同了。閲讀代碼開展分析工作有好的方面也有不利的方面,不利的方面代碼的畢竟沒有需求文檔那麼直接,尤其是遇到代碼可讀性比較差的時候。但是好的方面優勢明顯,如果代碼結構清晰、定義明確可以給我拆分工作提供很多便利,我們甚至可以將組織良好的單體工程按照模塊、包、文件、類等語言級別命名空間方式直接拆分為獨立微服務工程。即使通過是直接拆分工程的方式完成微服務化改造,也需要輸出微服務關係列表、微服務清單等文檔,一來在開發工作前對微服務架構的設計進行評審,二來作為後續開發工作的依據。從我過往經驗看對一個單體應用做微服務改造時,業務拆分上耗時並不會太多,更多需要關注的是技術方面問題,例如選擇合適的微服務中間件以解決服務進程獨立後所帶來的服務發現、負載均衡、服務治理等服務調用問題,還需要關注數據拆分後出現跨服務查詢、查詢數據一致性和分佈式事務等數據問題。這些問題超出了本文的範圍,我會單獨寫一篇文章介紹。

五、寫在最後

本文介紹了微服架構設計中一個重要的問題就是如何確定系統中每一個微服務的功能範圍和職責。縱觀全文三個步驟都是圍繞系統功能展開的,最終也是用系統功能串聯起一個個微服務。這就説明無論系統架構風格如何,最關鍵永遠都是實現業務功能。最後,我要明確一下微服務架構的定義:微服務架構系統由可獨立開發、測試、部署、擴展並且滿足高內聚和低耦合原則的獨立服務組成的系統,良好微服務系統具有高可靠性和高擴展性。希望你在今後的架構設計工作中也能根據定義設計出符合初衷的微服務系統。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.