領域驅動設計和服務自演進能力是內功。
《微服務的團隊應對之道》提到,微服務幫助企業提升其響應力,而企業需要從DevOps、服務構建、團隊和文化四點入手,應對微服務帶來的復雜度和各種挑戰,從而真正獲益。如果說運維能力是微服務的加油站,服務則是其核心。
企業想要實施微服務架構,經常問到的第一個問題是,怎么拆?如何從單體到服務化的結構?第二個問題是拆完后業務變了增加了怎么辦?另外,我們想要改變的系統往往已經成功上線,并有著活躍的用戶。那么對其拆分還需要考慮現有的系統運行,如何以安全最快低成本的方式拆分也是在這個過程中需要回答的問題。
本文會針對以上問題,介紹我們團隊在服務拆分和演進過程中的實踐和經驗總結。
我們項目架構的演化歷程該項目始于2009年,到現在已有7年的時間。在這7年中覆蓋的業務線不斷擴大,從工單、差旅、計費、文件、報表、增值業務等;業務流程從部分節點到用戶端的全線延伸;7年間打造多個產品,架構經歷了多次調整,從單體架構、RPC、服務化、規模化到微服務。
主要架構變遷如下圖所示:
在這7年架構演進路上,我們遇到的主要挑戰如下:
如何拆?即如何正確理解業務,將單體結構拆分為服務化架構? 拆完后業務變了增加了怎么辦?即在業務需求不斷發展變化的前提下,如何持續快速地演進? 如何安全地持續地拆?即如何在不影響當下系統運行狀態的前提下,持續安全地演進? 如何保證拆對了? 拆完了怎么保證不被破壞? 問題1:如何將單體結構拆分為服務化架構?就如庖丁解牛一樣,拆分需要摸清內部的構造脈絡,在筋骨縫隙處下刀。那么微服務架構中,我們認為服務是業務能力的代表,需要圍繞業務進行組織。拆分的關鍵在于正確理解業務,識別單體內部的業務領域及其邊界,并按邊界進行拆分。
1. 識別業務領域及邊界。首先需要將客戶、體驗設計師、業務分析師、技術人員集結在一起對業務需求進行溝通,隨后對其進行領域劃分,確定限界上下文(Boundary Context),也稱戰略建模。
以下我們經常使用的方法和參考的紅藍寶書:
Inception-> User Journey | Scenarios,用于梳理業務流程,由粗粒度到細粒度逐一場景分析。 四色建模,用于提取核心概念、關鍵數據項和業務約束。 領域驅動設計-戰略設計,用于劃分領域及邊界、進行技術驗證。 Eventstorming,用于提取領域中的業務事件,便于正確建模。 Inception與DDD戰略設計的對比:一個業務領域或子域是一個企業中的業務范圍以及在其中進行的活動,核心子域指業務成功的主要促成因素,是企業的核心競爭力;通用子域不是核心,但被整個業務系統所使用;支撐子域不是核心,不被整個系統使用,該能力可從外部購買。一個業務領域和子域可以包括多個業務能力,一個業務能力對應一個服務。領域的邊界即限界上下文,也是服務的邊界,它封裝了一系列的領域模型。
一個業務流程代表了企業的一個業務領域,業務流程所涉及的數據或角色或是通用子域,或是支撐子域,由其在企業的核心競爭力的角色所決定。比如企業有統一身份認證,決策不同部門負責不同的流程任務,那么身份認證子域并不產生業務價值,不是業務成功的促成因素,但是所有流程的入口,因而為通用子域,可為單獨服務;而部門負責的業務則為核心子域。
舉個例子
工單業務流程:
某企業為服務人員提供工單服務的業務流程簡化如下。首先搜索服務人員,選取服務人員購買的服務,基于目標國家的工單流程,向服務人員收取資料,對其進行審計,最后發送結果。
識別的領域:
其中服務為其核心競爭能力,包括該企業對全球各國的政策理解,即法律流程,服務資料(問卷),計算服務,資料審計服務,相比其他競爭對手的服務(價位/效率等),這些都為改企業提供核心的業務價值,自然也是核心子域。而其他用于統計改企業員工工作的工單,組織結構和員工為支撐子域,并不直接產生業務價值
領域劃分的原則在劃分的過程中,經常糾結的一個問題是:這個模型(概念或數據)看起來放這個領域合適,放另一個也合適,如何抉擇呢?
第一,依據該模型與邊界內其他模型或角色關系的緊密程度。比如,是否當該模型變化時,其他模型也需要進行變化;該數據是否通常由當前上下文中的角色在當前活動范圍內使用。 第二,服務邊界內的業務能力職責應單一,不是完成同一業務能力的模型不放在同一個上下文中。 第三,劃分的子域和服務需滿足正交原則。領域名字代表的自然語言上下文保持互相獨立。 第四,讀寫分離的原則。例如報表需有單獨報表子域。核心子域的劃分更多基于來自業務價值的產生方,而非不產生價值的報表系統。 第五,模型在很多業務操作中同時被修改和更新。 第六,組織中業務部分的劃分也是一種參考,一個業務部門的存在往往有其獨特的業務價值。簡單打個比方,同一個領域上下文中的模型要保持近親關系,五福以內,同一血統(業務)。
領域劃分的誤區和建議 業務能力還是計算能力?在劃分一些貌似通用的領域時,其實只是用到了通用的計算能力而不是業務能力,只需采用通用庫的方式進行封裝,而無需使用服務的方式。如我們系統的模板服務,是構建通用的模板服務,服務于整個平臺的服務;還是每個服務擁有獨立的模板模塊? 盡早識別剝離通用領域。如身份認證與鑒權領域,是企業系統中最復雜、有相對多變的領域,需要及早隔離它對核心業務的干擾。 時刻促成技術人員與客戶、業務人員的對話。業務領域的劃分離不開對業務意圖的真正理解。而需求人員和體驗設計師對于User Journey的使用更熟悉,而技術人員、架構師對領域驅動設計、Eventstorming更熟悉。不管哪種方法都要求跨角色的群體協同工作,即客戶人員、業務分析師、體驗設計師與技術人員、架構師。而現實的情況中,User Journey更多的在Inception,在需求階段進行,而領域驅動設計、Eventstorming更多的在開發設計階段被使用,故而需求階段經常缺失技術人員,而開發設計階段經常缺失客戶、業務人員的參與。 另一個常見的現象是,Inception的參與人員和真正的開發團隊有可能不是同一個群體,那么Inception中的業務溝通往往以UI的方式作為傳遞,因此在開發中經常只能通過UI設計來理解業務的真正意圖。 所以要想將正確的理解業務,做對軟件,需要時刻促成技術人員與客戶、業務人員的對話。識別了被拆對象的結構和邊界,下一步需要決定拆分的策略和拆分的步驟。
2.拆分方法與策略拆分方法需要根據遺留系統的狀態,通常分為絞殺者與修繕者兩種模式。
絞殺者模式 指在遺留系統外圍,將新功能用新的方式構建為新的服務。隨著時間的推移,新的服務逐漸“絞殺”老的一流系統。對于那些老舊龐大難以更改的遺留系統,推薦采用絞殺者模式。 修繕者模式 就如修房或修路一樣,將老舊待修繕的部分進行隔離,用新的方式對其進行單獨修復。修復的同時,需保證與其他部分仍能協同功能。我們過去所做的拆分中多為修繕者模式,其基本原理來自Martin Fowler的branch by abstraction的重構方法,如下圖所示:
就如我們團隊所總結的16字重構箴言,我覺得十分的貼切:
“舊的不變,新的創建,一步切換,舊的再見”。
通過識別內部的被拆模塊,對其增加接口層,將舊的引用改為新接口調用;隨后將接口封裝為API,并將對接口的引用改為本地API調用;最后將新服務部署為新進程,調用改為真正的服務API調用。
同時,拆分建議從業務相對獨立、耦合度最小的地方開始。待團隊獲取相應經驗和基礎設施平臺構建完善后,再進行核心應用遷移和大規模的改造。另外,核心通用服務盡量先行,如身份認證服務。
3. 拆分步驟對于模塊的拆分包括兩部分:數據庫與業務代碼,可以先數據庫后業務代碼,亦可先業務代碼后數據庫。然而我們的項目拆分中遇到的挑戰是數據層的拆分。在2015年的拆分中發現,數據庫層由于當時系統性能調優的驅動,在代碼中出現了跨模塊的數據庫連表查詢。這導致后期服務的拆分非常的困難。因此在拆分步驟上我們更多的推薦數據庫先行。
4.數據庫拆分我們借鑒了重構數據庫一書中提到的方法,通過重復schema同步數據,對數據庫的讀寫操作分別進行遷移。如下圖所示:
雖然技術上是可行的,然而這仍然占用了大量不必要的時間,包括大量的數據遷移。這也是導致當時的拆分無法在給定時間內完成的很大因素。
5. 我們的結果:系統架構圖:
問題2:拆分后業務變了增加了怎么辦?隨著客戶業務的變化,我們的服務也在持續的增加,而其中碰到了一個特大的服務。服務的大小如何衡量呢?該服務生產代碼7萬行+,測試代碼14萬行+,測試運行時間2個小時。團隊中7個stream每天50%工作需要對這個服務進行更改,使得團隊間的依賴非常嚴重,獨立功能無法單獨快速前行,交付速度及質量都受到了影響。
我們的總結:客戶的業務是在變化的,我們對業務的認知也是逐漸的過程,所以Martin Fowler在他的文章中提出,系統的初期建議以單體結構開始,隨業務發展決定其是否被拆分或合并。那么這也意味著這樣構建的服務在它的生命周期中必然會持續被拆分或合并。那么為了實現這樣一個目標,使系統擁有快速的響應力,也要求這樣的拆分必然是高效的低成本的。
因此,服務的設計需要滿足如下的原則:
服務要有明確的業務邊界,以單體開始并不意味著沒有邊界。 服務要有邊界,即使以單體開始也要定義單體時期的邊界。我們系統中有一個名為“Monkey”的服務,是在中國虎年啟動的,由此它并不是一個業務概念。當這個服務的名字為MonkeyAPI時,可以想象5年來它變成了什么?幾乎所有和這個產品相關的功能都放入了這個服務中。脫離平臺來看這一個產品的系統,其實它只是做了前后端分離而已。這個例子告訴我們,沒有邊界就會導致大雜燴,之后對其進行整理和重造的代價很大,可能需要花費“幾代人”的努力。 服務要有明確清晰的契約設計,即對外提供的業務能力。 服務內部要保持高度模塊化,才能夠容易的被拆分。 可測試。 問題3:如何安全地持續地拆?就如前言中提到的,系統已經上線大量的用戶正在使用,如何在不影響當下系統運行狀態的前提下,持續安全地演進?其實持續演進就是一場架構層次的重構,在這樣的路上同樣需要:
壞味道驅動,架構的壞味道是代碼壞味道在更高層次的展現,也就意味著架構的混亂程度同樣反映了該系統代碼層的質量問題。 安全小步的重構。 有足夠的測試進行保護——契約測試。 持續驗證演進的方向。 真正有挑戰的問題4:如何保證拆對了?拆分不能沒有目標,尤其在具有風險的架構層次拆分更需謹慎。那么我們如何驗證拆分的結果和收益?或許它可以提高開發效率,交付速度快,上線快,宕機時間也短,還能提高開發質量,可擴展性好,穩定,維護成本低,新人成長快,團隊容易掌握等等。然而軟件開發是一個復雜的事情,拆分可以引起多個維度的變化,度量的難度在于如何準確定位由拆分這一單一因素引起的價值的變化(增加或降低)。
其實要回答這個問題,還是要回到拆分之初:為什么而拆? 我所見過的案例中有因為政治原因拆的、業務發展需要的、系統集成驅動的等等;有因之而成功的,也有因之而失敗的。拆并不是一件容易的事,有諸多的因素。我認為不管表象是什么,拆之前需要弄清拆分的價值所在,這也是我們可以保證拆分結果的源頭。
總結系統可由單體結構開始,不斷的演進。而團隊需要對業務保持敏感,與客戶、業務人員進行業務對話,不斷修煉領域驅動設計和重構的能力。
在拆分的路上,我們的經驗顯示其的障礙來自意大利面一樣的系統。不管我們是什么樣的架構風格,高內聚低耦合的模塊化代碼內部質量仍然是我們架構演進的基石。具有夯實領域驅動設計和重構功底的團隊才可以應對這些挑戰,持續演進,保持其生命力。而架構變遷之前需要弄清背后的變遷動因與價值,探索性前進,及時反饋驗證,才是正解。那么我們如何保證架構不被破壞呢?這個問題會在后續的文章中持續探討。
最后,勿忘初心,且行且演進。
標題名稱:服務拆分與架構演進
本文鏈接:http://vcdvsql.cn/article42/chdphc.html
成都網站建設公司_創新互聯,為您提供微信公眾號、建站公司、服務器托管、標簽優化、搜索引擎優化、企業網站制作
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯