一、架構設計理念與技術
1.架構演變路徑
圖片
- 單體(又稱巨石系統):所有業務融合于一體。在項目早期,公司一般會選擇單體以降低運營等各方面成本。
- 服務化:隨著業務飛速發展和流量增長,進入了服務化階段。在此階段,經歷服務拆分、治理和模型抽象。
- 平臺化:業務膨脹期過后,服務化維護成本高,服務粒度拆分過細、重復造輪子、系統交互混亂等問題暴露,因此邁向平臺化(服務能力沉淀、服務合并、領域自治等)。
- 中臺化:打造企業級能力復用平臺,是平臺化的下一站,其具備數據互通能力和業務變化高響應力。
業務架構的演變路徑,側面展現所在互聯網企業的演變路徑。每一種架構無關好壞,選擇與否,只取決于是否適合當下及可預見的未來。
本次分享主要介紹從服務化到平臺化的過程,即從服務細粒度到領域能力沉淀的演進過程。
2.架構設計理念
圖片
從業務出發、面向業務變化是架構設計成功的關鍵,指導業務架構設計的維度包括:
1)商業模式及成熟度
傳統行業的業務相對穩定和成熟,非必要情況下建議做成單一服務。如需拆分,建議將變化頻繁與不頻繁的業務拆分。
互聯網行業則分為初創公司與成熟穩定的公司:
- 初創、商業不穩定的公司:需要多種業務進行快速試錯,可使用微服務。以微小的單體制服務器,快速構造探索場景,以技術的確定性來應對未來發展的不確定性。去哪兒網的某些團隊產生簡單方案后,可使用微服務獲取市場反響,快速驗證效果。
- 商業穩定或固化的公司:不再需要技術端的靈活性,也不愿承擔靈活性帶來的架構維護成本,此時可以考慮合并微服務,以減少運營成本。
目前旅游行業已相對穩定,去哪兒網比較符合以上第二種情況,可以考慮將先前拆分粒度太細的微服務進行合并。這也是去哪兒網的架構演進原因之一,原有業務拆分太細,達到人均10個應用,維護成本極高。
2)面向業務的變化
- 組件設計圍繞業務變化
- 組件調用非強依賴
- 組件業務復用
- 組件顆粒度與成熟度
為快速適應業務變化,需識別業務核心問題,劃清業務邊界,達到業務組件的復用最大化;區別出變與不變的業務,將變化隔離在一定范圍內,進而減少變化。
面向業務變化與不變的情況下,組件顆粒度要拆分到什么程度?
組件拆分粒度過細時,可復用性強,但組裝麻煩;拆分粒度過大時,好用,但使用場景比較少。
3)技術延遲決策
《架構整潔之道》一書講到:“良好的架構設計應該只關注用例,并能將他們與其他的周邊因素隔離。”前期應該只關注用例,在后期決策使用的具體技術。
圖片
4)康威和逆康威定律
- 康威定律:產品必然是其組織溝通結構的縮影。系統設計本質上反映了企業的組織架構,系統各模塊之間的接口,反映了企業各部門之間信息流動和合作的方式。
- 逆康威定律:當前組織效率不夠高時,可優先進行系統設計,通過系統設計來演進后續的組織架構。去哪兒網在2021年實行對內DDD對外API的戰略,對團隊和職責進行合理劃分,這是逆康威定律的實例之一。
5)面向測試、運維
測試是保證系統質量的重要途徑,使用TDD驗證架構是否合理、是否可以隔離、測試性好不好。
6)軟件質量屬性
在運行期和開發期,軟件質量屬性體現為可用性、可修改性、性能、安全性、易用性等。以功能性為主進行架構設計,以質量屬性為依據進行增量式迭代重構和優化。
圖片
上圖是架構的一些關鍵技術,這張圖的粒度較粗。從下往上看,公司底層基本都由容器與自動化支撐,上層是監控和治理、前后端分離的系統。基于此圖,DDD是整個架構的指導思想。
二、業務系統重構背景
1.業務介紹:酒店基礎信息
圖片
上列四張圖簡單展示了去哪兒網本次重構的主題,也是酒店基礎信息部所負責的業務。
- 列表頁:用戶打開App,填寫地址與入住時間,點擊搜索,就會跳轉到列表頁,可以看到搜索地點的所有酒店列表信息。
- 詳情頁:點擊希望入住的酒店,即可進入詳情頁。
- 酒店Info:點擊酒店名字,則可進入酒店Info頁。
- 進訂頁:用戶決定預定酒店房間后,就跳轉到進訂頁。
2.基礎信息業務架構
圖片
上圖是酒店基礎信息業務對應的架構。去哪兒網售賣的酒店,來源于各個代理商和集團分銷的信息,按照圖示自下到上,經過基礎層,然后到達基礎信息部門的主要業務層。
業務層最重要的內容是酒店聚合,包括代理商酒店Tree和Q物理酒店。我們將各個代理商提供的酒店信息,按照一定業務邏輯規則,聚合到去哪兒網的Q側物理酒店,將此部分信息對外售賣,從而提升用戶體驗。
為什么能夠提升用戶體驗?
舉個例子,比如現在投放的是季楓酒店,A代理商將其稱為季楓酒店北京店,B代理商將其稱為季楓酒店北京中關村店,C代理商稱之為季楓酒店北京中關村蘇州街店,用戶容易混淆。所以去哪兒網將各個代理商提供的酒店信息,按照一定的業務邏輯、規則聚合為外網能夠看到的、唯一的物理酒店。
酒店Tree的含義是,每個代理商投放的酒店,映射到去哪兒網的酒店,以去哪兒網的酒店為樹根,下方掛靠不同代理商投放的酒店信息,形成對應關系。
當前我們團隊的核心業務是,將基于業務層的酒店信息,提供給應用層,比如提供APP搜索或篩選下展示的信息。
3.落地技術中心戰略,償還技術債務
圖片
旅游行業應該是受疫情影響最大的行業之一,在此背景下,技術中心在2022年提出“鞏固效率之本,分擔產品之憂”的戰略,自此開啟DDD重構之路。
如上圖,重構前,業務和業務架構存在以下問題:
- 核心業務分散:上圖的灰色條塊是我們的核心業務,被耦合于整條鏈路,分散在各個系統中,導致核心業務入侵。
- 核心業務入侵:產品需求交付效率低下,一個產品需求可能需要調整5個微服務。
- 數據寫入鏈路長:沒有對核心業務的收口能力,數據更新不及時。由于沒有實時查詢能力,別的團隊調用數據時,需拉取并自行緩存。
4.系統重構模式選擇
沒有最好的架構,只有最合適的架構。以下是備選的系統重構模式:
圖片
- 修繕者:在現有系統的基礎上,新增一個抽象層,保證對外提供能力不變,然后對系統內部進行改造。
- 絞殺者:修繕沒有辦法適應現狀的情況下,需要另起爐灶,在系統以外重新構建新功能,逐步剝離原有邏輯。對外提供新功能,逐步絞殺各個需要下線或重構的服務。重構時需要權衡絞殺者的優缺點。
- 優點:不影響原有業務,一旦條件成熟,新系統可以完全替換舊系統。
- 缺點:一段時間內需要維護兩套系統,付出額外的開發維護成本。
- 演進式:老項目的邏輯已經模糊,需要進行演進式迭代。識別老系統中的核心業務邏輯,從MVP版本開始小步快跑,先迭代核心業務,快速上線查看效果,然后將剩下的邊緣業務逐漸切換過來,及時調整。
- 優點:有效控制迭代風險,避免全部替換系統,造成難以估量的影響。
三、系統重構改造模式與架構選擇
前文講解了架構的演變路徑、理念及改造模式的選擇,最終衍生出來的系統重構框架是什么樣子?
1.系統重構模式選擇
圖片
從業務出發、面向業務變化是我們現代架構設計成功的關鍵,DDD的設計思想完全符合成功架構設計的理念。
1)服務業務戰略
站在EA(企業架構)角度(包括業務架構BA、應用架構AA、數據架構DA、技術架構TA),DDD可以綁定業務架構和應用架構,將問題域與應用架構相剝離;通過DDD將業務架構的“價值流+業務能力”進行解構化分解,能力下沉。同時依據DDD劃分的限界上下文、聚合,進行應用架構的搭建,實現自下而上的“高內聚、低耦合”的應用。
2)演進式架構
DDD的核心思想有哪些:
- 戰略層面:業務問題分析→分解子問題域,識別核心域→分而治之,降低業務復雜度;
- 戰術層面:識別問題域的不同業務上下文→領域建模,定義聚合,組件化業務需求→指導微服務的拆分;
- 實現層面:利用成熟的分層模式、依賴倒置屏蔽掉技術細節復雜度,通過DDD方法設計的微服務,不僅可以通過限界上下文和聚合實現微服務內外的解耦,同時也可以很容易地實現業務功能積木式模塊的重組和更新,從而實現架構演進。
總之,自上而下地拆解業務,并以此為指導,自下而上地構建模型,最終達到高內聚低耦合的狀態。
我們選擇絞殺模式和演進模式,進行系統重構。由于系統復雜程度高、了解業務細節的同學少,為了減少重構對現有業務的影響,我們將核心資源投入到核心業務中,快速上線以便查看效果,下文將具體介紹演進實踐。
四、以業務驅動的微服務架構演進實踐
1. 領域驅動設計過程
圖片
上圖是以業務驅動的微服務架構演進的實戰過程,介紹DDD的完整流程和關鍵路徑。進行領域驅動設計時,需要對組內成員進行定位。最重要的是識別領域專家,即哪些人對該領域認知深刻,能夠幫助成員深入理解業務,有利于后續腦暴和建模過程順利進行;其次是識別技術專家和開發團隊。
領域驅動設計的關鍵路徑如下:
第一步,領域專家和開發團隊就具體問題,明確業務愿景,討論需求,從而建立統一語言,形成領域知識。統一語言,即對問題域內的概念統一認知,比如大家明確某個詞語的定義且沒有歧義,大大降低交流成本。
第二步,分析問題域并劃分子域(比如核心子域、支撐子域、通用子域),進而劃分限界上下文,構建上下文地圖。
第三步,領域建模并實現模型。將以上兩步分析,投射到代碼層面進行模型實現,這一步驟可總結為“兩關聯一循環”。“兩關聯”是指,統一語言和模型相關聯、模型與軟件實踐相關聯。“一循環”是指,實踐過程可能經歷種種困難與不確定性,需要在不斷循環的過程中提煉知識,最終得到趨向完美的模型。
2.基于DDD落地實踐
圖片
上圖展示了基于DDD落地實踐的過程。首先是定位愿景,其重要性在于決定了后續的發展道路;其次,分析問題域中現有業務場景;然后基于劃分的子域,識別限界上下文;最后在限界內進行領域建模、實現模型。
1)問題域分析
① 定位愿景
圖片
麥肯錫提出“電梯演講”概念是指,在乘坐電梯的30秒之內,向顧客清晰準確地解釋解決方案,即使用簡短的語言精準說明業務價值。比如,去哪兒網的核心價值是“總有你要的低價”。因此,所有的核心工作都要圍繞低價展開,落實到基礎信息團隊,我們的愿景就是提供多樣的信息聚合。
② 明確領域專家
由于產品迭代頻繁,系統演進缺少領域專家,所以按照產品、QA、技術人員的順位確定領域專家。
圖片
為剖析原有項目中,哪些用戶用例與愿景強相關,我們整理用例圖并安排同學逐一分析。由此發現,經過多年迭代,很多業務已經不再使用,舊業務無法適應現有商業模式。所以我們將此類業務下線,或轉投資源,最終將188個用例減少為79個用例,極大簡化工作內容。
圖片
③ 事件風暴
事件風暴主要關注三件事:識別領域事件、識別決策命令、識別領域名詞。
事件風暴的輸出,將作為后續領域建模的輸入,需要遵循以下原則:
- 業務視角事件:從業務視角分析領域事件,比如,某個業務動作對內產生某種數據,觸發業務流程狀態變化,對外發送消息。技術人員進行頭腦風暴時,很容易陷入代碼細節,忘記最初目的。
- 先發散,再收斂:先將所有想法羅列出來,在此基礎上再進行收斂。
- 決策命令:哪些人做了哪些動作導致了事件產生。
④ 統一語言
圖片
沒有DDD經驗的同學可能會問,什么可以作為統一語言?
答案是,什么都可以作為團隊的統一語言。比如,一個老系統的內部邏輯特別復雜難懂,這部分代碼的重構代價大,難以改動,就可以作為團隊內部的統一語言。由此,產品和技術同學都知道老系統的目標、能力及應對態度。
技術同學表達事情的思維,偏向代碼邏輯,導致和產品同學溝通存在偏差。此時,統一語言可以拉齊雙方認知,技術同學只要拋出幾個領域名詞,產品同學即可理解。
需要注意的是,統一語言的術語表應具備中英文對照,便于后期編解碼時,在代碼層面達成認知統一。
2)識別限界及子域劃分
圖片
識別限界上下文的總體原則是先業務后技術,上圖展示了領域層面的劃分流程。
- 降低業務復雜度:業務層面,需要杜絕語言的二義性。比如,在去哪兒網內部,商務同學、技術同學可能對“酒店”一詞的認知不同,限界上下文時就要避免這種情況。
- 降低管理復雜度:基于領域層劃分的業務邊界,影響工作邊界的劃分。上文提到的康威定律以及著名的兩個披薩原則(一個高效的技術研發團隊,最佳的團隊規模應該控制在2個披薩就可以吃飽的人數),都對工作邊界具有指導意義。
- 降低技術復雜度:限界上下文承接的流量不同,我們通過制作彈性邊界、部署及可用性測試,使用不同方式對待不同的限界上下文。
圖片
限界上下文的特征:
- 最小完備原則:自治單位履行的職責是完整的,無需求助其他自治單位獲取自己的信息。
- 自我履約原則:自治單元自身決定職責。比如,進行代理商酒店的抓取落地時,無需進行后續解析。
- 穩定空間原則:外部變化不會影響自治單元。
- 獨立進化原則:對外提供穩定接口,內部變化不影響外部。
繪制上下文依賴地圖時,謹記三不原則:不要雙向依賴、不要循環依賴、不要過長依賴。
如上右圖所示,我們在制作上下文依賴地圖時發現,酒店解析依賴酒店抓取,酒店抓取依賴酒店聚合信息,酒店聚合信息依賴靜態信息,靜態信息依賴酒店解析出來的數據,形成循環,所以劃分方式不合適。基于這種情況,創造出“酒店上下文”環節,打破循環依賴。
圖片
在限界上下文后,需要識別核心域、支撐域和通用域,劃分參考是與業務愿景的相關性。識別子域的好處是,對外可以明確告知自身核心競爭力;對內明晰人員的資源分配、機器資源分配,評估產品需求的優先級、是否位于核心領域內。
3)領域建模
圖片
依據事件風暴和限界上下文的輸出,可以構建領域模型。
① 建模意義
- 聚合來表達業務“高內聚,低耦合”
- 降低業務復雜度,更好地適應業務變化
② 建模過程
- 識別實體、值對象、豐富領域邏輯
- 定義聚合、識別聚合根
建模過程中最難把握的是尺度,哪些方法應該加入模型,屬性應該放在哪個模型。個人建議是共識即正確,無論是否正確,組內達成共識,這個決策在當下就是正確的。因為建模是循環提煉的過程,隨著后續深化業務理解,推翻之前結論、一次性創建完美模型的難度較大。
分層架構中具有領域層和業務層,如果將功能和用例盲目加入領域層,領域層膨脹會影響復用性和業務表達力。所以,要承認模型和領域能力的不確定性,循序漸進地使用迭代方式,將能力下沉到領域模型中。
③ 建模原則
- 重點關注核心域建模:投入核心資源
- 聚合盡量小,適應業務變化
- 聚合邊界內強一致性
- 抽象模型,防止過多屬性拍平(DP):屬性被拍平的弊端是,模型內字段太多,無法識別識別模型。
圖片
上圖展示了兩個原則:共性的業務能力優先下沉到領域,共性的技術問題抽象成業務。
沒有完美的模型,也沒有正確的模型,領域模型共識即正確,所以團隊的綜合能力決定了模型完美程度的上限。提供一個可參考的檢驗技巧,建完模型后,可以用業務場景檢驗模型的完整度。隨之循環往復,模型也會越發完善。
圖片
④ 落地實踐時劃分微服務
如上所示,業務邊界、康威定律、業務變更頻率、彈性邊界、技術選型等,都可作為劃分依據。
需要注意的是,一個微服務可包含多個限界上下文,但只能包含一種子域類型(核心、通用、支撐),不能將一個核心域和一個支撐域放在同一微服務中。如果支撐域可用性不好,影響核心邏輯,就可能為這個不太重要的問題付出沉重代價。
4)模型實現
① 業務流程和領域模型映射
圖片
如上所示,業務流程或業務用例被分為不同階段,每個階段又被分為不同活動,我們將這些業務活動與整個分層架構聯系起來,構建映射關系。
構建映射關系的好處是,在分層架構和領域模型高度內聚、完善的情況下,方便后續需求接入和擴展。自上而下分解業務流程,分層映射,隔離技術負責度。
② 模型映射代碼清單
圖片
如上圖,應用層、領域層、基礎設施層是聚合領域對象,模型映射代碼清單劃分了每一層具備能力、領域層的領域對象、是否具有前置依賴對象,包名、類名及方法名,這些內容與上文提到的中英文對照表一一對應。
構建這份代碼清單,便于達成組內多人合作,提高開發效率;為新人熟悉項目時提供參考,快速說明項目的核心邏輯與能力。
③ COLA應用架構
圖片
在代碼開發階段,我們選擇了開源框架COLA,其分層架構分為適配層、領域層、應用層、基礎設施層。
無論是COLA還是DDD的分層架構,都以業務為核心,基于穩定的領域模型,對外提供領域能力。
選擇COLA的原因如下:
- 定義良好的分層結構、規范;
- 層內部結構“聚合分包,功能分類”;
- 提供最佳應用架構的最佳實踐:《領域驅動設計》一書只提供思想指導,而COLA給出了可參考的模板。
下圖是我們內部基于COLA架構落地微服務的實踐。
圖片
- Adaptor:多端適配
- Client:業務提供的接口,比如Dubbo
- APP:業務用例Case的編排,含executor、publish、qschedule等
- DomAIn(聚合、實體、值對象的定義):
- 業務規則顯示化,包括邏輯判斷,和對象設值代表的含義,純內存操作
- Entity解決單個對象的邏輯變更,領域服務解決多對象的業務邏輯變更
- 不允許跨聚合調用
- 充血模型
- Insfrastructure:Repisitory實現;ACL的定義和實現
- Common:公共屬性、工具類,由domain調用
圖片
上圖是對前一張圖的具體描述,我們使用的是CQRS模式,即命令查詢職責分離。
前文的一張圖描述了架構重構前的狀況:核心業務滲透到各個服務中,沒有做收攏聚合,業務耦合。而通過限界劃分、領域建模,即可實行分離。
基礎設施層實現了依賴倒置,即基礎設施層依賴領域層,由此無需關心領域層使用ES還是redis進行存儲,可專注于領域能力。
重構前,沒有提供實時查詢的能力,各個團隊將數據拉走,進行本地緩存。重構后,基于異步調用機制,實現數據持久化,對外提供查詢。
④ 領域模型與代碼模型映射
圖片
上圖是領域模型與代碼模型的映射。分層對應上一張圖展現的架構,在Domain層,按照領域劃分進行聚合分包。上圖標藍的Hoteltree就是前文提到的酒店聚合,在這一領域內進行功能劃分。
圖片
Domain Primitive 是 Value Object 的進階版,在原始 VO 的基礎上要求每個DP擁有概念的整體,而不僅僅是值對象。在 VO 的 Immutable 基礎上增加了 Validity 和行為。
DP特征:
- 擁有完整的概念整體,精準定義
- 使用業務域中的原生語言
- 業務域的最小組成部分,可構建復雜合
- 隱式轉顯式
例如,聯系信息對外顯示為電話號碼,背后隱藏了區號、國內外來源等隱式屬性。通過DP可以找出隱式屬性,并將其轉為顯式,這是我們推薦DP的重要原因之一。
五、總結和思考
1.項目落地效果
1)組織效率
- 組織資源是否集中在了核心業務領域;
- 是否能用統一語言溝通描述業務,表現在需求評審、站會等有關會議的效率上;
- 領域知識是否得到沉淀,是否有人能承擔“領域專家”;
- 團隊間職責模糊地帶少,相互扯皮的機會少。
2)開發效率
- 模塊粒度是否合適、模塊間依賴是否健康;
- 接口數量是否穩定,不膨脹;
- 因為功能理解不足引起的bug數量是否低;
- 模塊和接口的自測性程度高不高;
- 代碼可讀性,人員交接和新人上手是否足夠快。
3)鞏固效率之本,分擔產品之憂
- 清晰領域,核心子域重點投入;
- 統一語言,減少產運研測溝通成本,增加2名研發業務專家;
- 承接產品需求75%,助力0.5PM;
- 21個應用微服務,通過DDD領域劃分后下降到13個,微服務減少33%;
- 開發工時3pd以下產品響應效率提升52.3%,3pd以上32.5%,QA工時下降62.3%;
- 架構、聚合分層,功能分類,新人上手快。
2.思維模型改變
圖片
技術人員從被動了解業務,到主動了解業務,解讀業務策略變化,為其定義測量,提議數字化方案。產品經歷的核心價值是成為技術與業務連接的橋梁,但通過DDD,技術同學也更關注業務,真正做到產研融合。
1)問題域分析領域建模
- 分治思維
- 模型思維
- 抽象思維
- 結構化思維
2)模型實現
- 簡單思維
- 契約思維
- 解耦思維
3.DDD帶來的優劣勢及建議
1)優勢
- 隱性知識顯性化,統一團隊語言
- 圍繞業務變化,隔離“變化”
- 積木式組合業務演進
- 關注點分離,隔離技術細節
- 面向測試、運維
- 業務思維(主動向前看業務,主動提想法,0.5PM)
2)劣勢
- 團隊上手有門檻(概念-理解-困惑-深入理解)
3)使用建議
- 業務場景復雜
- 業務變化頻繁
- 重點核心業務領域
- 可部分取用(分層思想、聚合、限界、架構設計、解耦思維等)
- 團隊共識即正確
業務架構是領域,技術架構是容器,脫離靈魂的容器是沒有技術意義的。
Q&A
Q1:DDD重構時,如何協調產品上線需求的矛盾?
A1:首先,我們進行DDD重構的時候,背靠公司技術中心的戰略,公司是鼓勵和倡導的;其次,重構模式包括修繕者、絞殺者、演進式。面臨與產品上線需求的矛盾時,我們可以選擇絞殺者,另起爐灶來優化重構,在原有業務中也不影響產品新需求接入。
Q2:選擇COLA架構作為DDD重構業務模型的原因是什么?
A2:首先,COLA是阿里開源的,大廠背書,信任度較高;其次,COLA具備很好的分層架構和規范,項目Github中提供了最佳實踐。如果初期不清楚如何進行重構,可以直接參考官方demo,將其映射到自己的業務中,后期再加入自身見解,進行系統優化。
作者介紹
李全黨,2021 年加入去哪兒網,擔任酒店供應鏈代理商和基礎信息業務負責人、業務架構SIG成員,擁有 10 年以上系統研發和軟件架構設計經驗主導搭建多個 DDD 項目,有高并發、分布式服務、高可用的建設優化經驗。
朱浩曼,2021年9月加入去哪兒網,擔任標準代理商業務負責人、業務架構SIG成員。