領域驅動設計和服務自演進能力是內功。html
《微服務的團隊應對之道》提到,微服務幫助企業提高其響應力,而企業須要從DevOps、服務構建、團隊和文化四點入手,應對微服務帶來的複雜度和各類挑戰,從而真正獲益。若是說運維能力是微服務的加油站,服務則是其核心。ios
企業想要實施微服務架構,常常問到的第一個問題是,怎麼拆?如何從單體到服務化的結構?第二個問題是拆完後業務變了增長了怎麼辦?另外,咱們想要改變的系統每每已經成功上線,並有着活躍的用戶。那麼對其拆分還須要考慮現有的系統運行,如何以安全最快最低成本的方式拆分也是在這個過程當中須要回答的問題。數據庫
本文會針對以上問題,介紹咱們團隊在服務拆分和演進過程當中的實踐和經驗總結。後端
該項目始於2009年,到如今已有7年的時間。在這7年中覆蓋的業務線不斷擴大,從工單、差旅、計費、文件、報表、增值業務等;業務流程從部分節點到用戶端的全線延伸;7年間打造多個產品,架構經歷了屢次調整,從單體架構、RPC、服務化、規模化到微服務。安全
主要架構變遷以下圖所示:架構
在這7年架構演進路上,咱們遇到的主要挑戰以下:前後端分離
就如庖丁解牛同樣,拆分須要摸清內部的構造脈絡,在筋骨縫隙處下刀。那麼微服務架構中,咱們認爲服務是業務能力的表明,須要圍繞業務進行組織。拆分的關鍵在於正確理解業務,識別單體內部的業務領域及其邊界,並按邊界進行拆分。運維
首先須要將客戶、體驗設計師、業務分析師、技術人員集結在一塊兒對業務需求進行溝通,隨後對其進行領域劃分,肯定限界上下文(Boundary Context),也稱戰略建模。模塊化
如下咱們常用的方法和參考的紅藍寶書:微服務
一個業務領域或子域是一個企業中的業務範圍以及在其中進行的活動,核心子域指業務成功的主要促成因素,是企業的核心競爭力;通用子域不是核心,但被整個業務系統所使用;支撐子域不是核心,不被整個系統使用,該能力可從外部購買。一個業務領域和子域能夠包括多個業務能力,一個業務能力對應一個服務。領域的邊界即限界上下文,也是服務的邊界,它封裝了一系列的領域模型。
一個業務流程表明瞭企業的一個業務領域,業務流程所涉及的數據或角色或是通用子域,或是支撐子域,由其在企業的核心競爭力的角色所決定。好比企業有統一身份認證,決策不一樣部門負責不一樣的流程任務,那麼身份認證子域並不產生業務價值,不是業務成功的促成因素,可是全部流程的入口,於是爲通用子域,可爲單獨服務;而部門負責的業務則爲核心子域。
舉個例子
工單業務流程:
某企業爲服務人員提供工單服務的業務流程簡化以下。首先搜索服務人員,選取服務人員購買的服務,基於目標國家的工單流程,向服務人員收取資料,對其進行審計,最後發送結果。
其中服務爲其核心競爭能力,包括該企業對全球各國的政策理解,即法律流程,服務資料(問卷),計算服務,資料審計服務,相比其餘競爭對手的服務(價位/效率等),這些都爲改企業提供核心的業務價值,天然也是核心子域。而其餘用於統計改企業員工工做的工單,組織結構和員工爲支撐子域,並不直接產生業務價值
在劃分的過程當中,常常糾結的一個問題是:這個模型(概念或數據)看起來放這個領域合適,放另外一個也合適,如何抉擇呢?
簡單打個比方,同一個領域上下文中的模型要保持近親關係,五福之內,同一血統(業務)。
識別了被拆對象的結構和邊界,下一步須要決定拆分的策略和拆分的步驟。
拆分方法須要根據遺留系統的狀態,一般分爲絞殺者與修繕者兩種模式。
咱們過去所作的拆分中多爲修繕者模式,其基本原理來自Martin Fowler的branch by abstraction的重構方法,以下圖所示:
就如咱們團隊所總結的16字重構箴言,我以爲十分的貼切:
「舊的不變,新的建立,一步切換,舊的再見」。
經過識別內部的被拆模塊,對其增長接口層,將舊的引用改成新接口調用;隨後將接口封裝爲API,並將對接口的引用改成本地API調用;最後將新服務部署爲新進程,調用改成真正的服務API調用。
同時,拆分建議從業務相對獨立、耦合度最小的地方開始。待團隊獲取相應經驗和基礎設施平臺構建完善後,再進行核心應用遷移和大規模的改造。另外,核心通用服務儘可能先行,如身份認證服務。
對於模塊的拆分包括兩部分:數據庫與業務代碼,能夠先數據庫後業務代碼,亦可先業務代碼後數據庫。然而咱們的項目拆分中遇到的最大挑戰是數據層的拆分。在2015年的拆分中發現,數據庫層因爲當時系統性能調優的驅動,在代碼中出現了跨模塊的數據庫連表查詢。這致使後期服務的拆分很是的困難。所以在拆分步驟上咱們更多的推薦數據庫先行。
咱們借鑑了重構數據庫一書中提到的方法,經過重複schema同步數據,對數據庫的讀寫操做分別進行遷移。以下圖所示:
雖然技術上是可行的,然而這仍然佔用了大量沒必要要的時間,包括大量的數據遷移。這也是致使當時的拆分沒法在給定時間內完成的很大因素。
系統架構圖:
隨着客戶業務的變化,咱們的服務也在持續的增長,而其中碰到了一個特大的服務。服務的大小如何衡量呢?該服務生產代碼7萬行+,測試代碼14萬行+,測試運行時間2個小時。團隊中7個stream天天50%工做須要對這個服務進行更改,使得團隊間的依賴很是嚴重,獨立功能沒法單獨快速前行,交付速度及質量都受到了影響。
客戶的業務是在變化的,咱們對業務的認知也是逐漸的過程,因此Martin Fowler在他的文章中提出,系統的初期建議以單體結構開始,隨業務發展決定其是否被拆分或合併。那麼這也意味着這樣構建的服務在它的生命週期中必然會持續被拆分或合併。那麼爲了實現這樣一個目標,使系統擁有快速的響應力,也要求這樣的拆分必然是高效的低成本的。
所以,服務的設計須要知足以下的原則:
就如前言中提到的,系統已經上線大量的用戶正在使用,如何在不影響當下系統運行狀態的前提下,持續安全地演進?其實持續演進就是一場架構層次的重構,在這樣的路上一樣須要:
拆分不能沒有目標,尤爲在具備風險的架構層次拆分更需謹慎。那麼咱們如何驗證拆分的結果和收益?或許它能夠提升開發效率,交付速度快,上線快,宕機時間也短,還能提升開發質量,可擴展性好,穩定,維護成本低,新人成長快,團隊容易掌握等等。然而軟件開發是一個複雜的事情,拆分能夠引發多個維度的變化,度量的難度在於如何準肯定位由拆分這一單一因素引發的價值的變化(增長或下降)。
其實要回答這個問題,仍是要回到拆分之初:爲何而拆? 我所見過的案例中有由於政治緣由拆的、業務發展須要的、系統集成驅動的等等;有因之而成功的,也有因之而失敗的。拆並非一件容易的事,有諸多的因素。我認爲無論表象是什麼,拆以前須要弄清拆分的價值所在,這也是咱們能夠保證拆分結果的源頭。
系統可由單體結構開始,不斷的演進。而團隊須要對業務保持敏感,與客戶、業務人員進行業務對話,不斷修煉領域驅動設計和重構的能力。
在拆分的路上,咱們的經驗顯示其最大的障礙來自意大利麪同樣的系統。無論咱們是什麼樣的架構風格,高內聚低耦合的模塊化代碼內部質量仍然是咱們架構演進的基石。具備夯實領域驅動設計和重構功底的團隊才能夠應對這些挑戰,持續演進,保持其生命力。而架構變遷以前須要弄清背後的變遷動因與價值,探索性前進,及時反饋驗證,纔是正解。那麼咱們如何保證架構不被破壞呢?這個問題會在後續的文章中持續探討。
最後,勿忘初心,且行且演進。