DDD 如何落地:去哪兒的 DDD 架構實操之路

作者:李全黨 / 朱浩曼

來源:技術自由圈

本文目錄

- 一、架構設計理念與技術

  - 1. 架構演變路徑

  - 2. 架構設計理念

- 二、業務系統重構背景

  - 1. 業務介紹:酒店基礎信息

  - 2. 基礎信息業務架構

  - 3. 落地技術中心戰略,償還技術債務

  - 4. 系統重構模式選擇

- 三、系統重構改造模式與架構選擇

  - 系統重構模式選擇

    - 1)服務業務戰略

    - 2)演進式架構

- 四、以業務驅動的微服務架構演進實踐

  - 1. 領域驅動設計過程

  - 2. 基於 DDD 落地實踐

    - 1)問題域分析

    - 2)識別限界及子域劃分

    - 3)領域建模

    - 4)模型實現

- 五、總結和思考

  - 1. 項目落地效果

    - 1)組織效率

    - 2)開發效率

    - 3)鞏固效率之本,分擔產品之憂

  - 2. 思維模型改變

    - 1)問題域分析領域建模

    - 2)模型實現

  - 3. DDD 帶來的優劣勢及建議

    - 1)優勢

    - 2)劣勢

    - 3)使用建議

- Q&A

  - Q1:DDD 重構時,如何協調產品上線需求的矛盾?

  - Q2:選擇 COLA 架構作爲 DDD 重構業務模型的原因是什麼?

- 說在最後

- 部分歷史案例

一、架構設計理念與技術

1. 架構演變路徑

這種業務架構的演變路徑,從側面反映了互聯網企業的演變路徑。每種架構的好壞並非絕對,選擇與否,僅取決於是否適應當前和可預見的未來。

本次分享主要介紹服務化到平臺化的過程,即從服務細粒度到領域能力積累的演進過程。

2. 架構設計理念

以業務爲中心、適應業務變化是架構設計成功的關鍵。指導業務架構設計的維度包括:

1)商業模式及成熟度

傳統行業的業務相對穩定和成熟,非必要情況下建議構建單一服務。如需拆分,建議將變化頻繁和不頻繁的業務拆分。

互聯網行業分爲初創公司和成熟穩定的公司:

目前旅遊行業已相對穩定,去哪兒網符合上述第二種情況,可以考慮將之前拆分過細的微服務進行合併。這也是去哪兒網架構演進的原因之一,原有業務拆分過細,達到人均 10 個應用,維護成本極高。

2)面向業務的變化

爲快速適應業務變化,需識別業務核心問題,明確業務邊界,實現業務組件的最大化複用;區分變化與不變的業務,將變化限制在一定範圍內,從而降低影響。

面向業務變化與不變的情況下,組件顆粒度要拆分到什麼程度?

組件拆分粒度過細時,可複用性強,但組裝複雜;拆分粒度過大時,使用方便,但應用場景有限。

3)技術延遲決策

《架構整潔之道》一書提到:“良好的架構設計應關注用例,並將它們與其他周邊因素隔離。”

在前期,應關注用例,後期再決策具體技術。

4)康威和逆康威定律

5)面向測試、運維

測試是確保系統質量的關鍵環節,採用測試驅動開發(TDD)來驗證架構的合理性、可隔離性和易測試性。

6)軟件質量屬性

在開發和運行階段,軟件質量特性表現爲可用性、可維護性、性能、安全性、易用性等。

以功能性爲核心進行架構設計,依據質量特性進行增量式的迭代重構和優化。

上圖是架構的一些關鍵技術,這張圖的粒度較粗。

從下往上觀察,公司底層主要由容器和自動化技術支持,上層則是監控和治理、前後端分離的系統。

根據這張圖,領域驅動設計(DDD)成爲整個架構的指導原則。

二、業務系統重構背景

1. 業務介紹:酒店基礎信息

上列四張圖簡單展示了去哪兒網本次重構的主題,也是酒店基礎信息部所負責的業務。

2. 基礎信息業務架構

注意:請點擊圖像以查看清晰的視圖!

上圖展示了酒店基礎信息業務對應的架構。

去哪兒網售賣的酒店,來源於各個代理商和集團分銷的信息,按照圖示自下到上,經過基礎層,然後到達基礎信息部門的主要業務層。

業務層最重要的內容是酒店聚合,包括代理商酒店 Tree 和 Q 物理酒店。

我們將各個代理商提供的酒店信息,按照一定業務邏輯規則,聚合到去哪兒網的 Q 側物理酒店,並將這部分信息對外銷售,以優化用戶體驗。

爲什麼能夠提升用戶體驗?

舉個例子,比如現在投放的是季楓酒店,A 代理商將其稱爲季楓酒店北京店,B 代理商將其稱爲季楓酒店北京中關村店,C 代理商稱之爲季楓酒店北京中關村蘇州街店,用戶容易混淆。

因此,去哪兒網將各代理商提供的酒店信息,按照一定的業務邏輯、規則整合爲外部可見的唯一物理酒店。

酒店 Tree 的含義是,每個代理商投放的酒店,對應到去哪兒網的酒店,以去哪兒網的酒店爲根,下方掛接不同代理商投放的酒店信息,形成對應關係。

目前,我們團隊的核心業務是將基於業務層的酒店信息,提供給應用層,如 APP 搜索或篩選時展示的信息。

3. 落地技術中心戰略,償還技術債務

旅遊業可能是受疫情影響最大的行業之一。在此背景下,技術中心在 2022 年提出了 “鞏固效率之本,分擔產品之憂” 的戰略,從而開啓了領域驅動設計(DDD)的重構之旅。

如上圖,重構前,業務和業務架構存在以下問題:

爲了解決這些問題,技術中心決定採用領域驅動設計(DDD)進行系統重構。通過這一方法,我們希望能夠提高產品需求交付效率,縮短數據寫入鏈路,以及降低核心業務受到的侵入程度。此外,DDD 還能夠幫助我們實現核心業務的模塊化,使得各個系統之間的耦合度降低,從而提高整個系統的靈活性和可擴展性。

4. 系統重構模式選擇

沒有最好的架構,只有最合適的架構。

以下是備選的系統重構模式:

三、系統重構改造模式與架構選擇

前文講解了架構的演變路徑、理念及改造模式的選擇,最終衍生出來的系統重構框架是什麼樣子?

系統重構模式選擇

以業務爲導向,適應業務變化是現代架構設計成功的核心要素,而領域驅動設計(DDD)的理念恰恰符合這一成功架構設計的原則。

1)服務業務戰略

站在 EA(企業架構)角度(包括業務架構 BA、應用架構 AA、數據架構 DA、技術架構 TA)的角度出發,DDD 能夠將業務架構與應用架構相結合,將問題領域與應用架構分離。通過分解業務架構中的 “價值流 + 業務能力”,實現能力的下移。同時,根據 DDD 劃分的限界上下文和聚合,構建應用架構,實現自下而上的 “高內聚、低耦合”。

2)演進式架構

DDD 的核心思想包括:

總之,自上而下地拆解業務,並以此爲指導,自下而上地構建模型,最終達到高內聚低耦合的狀態。

在系統重構過程中,我們選擇了絞殺模式和演進模式。鑑於系統複雜度高,瞭解業務細節的人員較少,爲了降低重構對現有業務的影響,我們將核心資源投入到核心業務中,快速上線以觀察效果。以下將詳細介紹演進實踐。

四、以業務驅動的微服務架構演進實踐

1. 領域驅動設計過程

上圖是以業務驅動的微服務架構演進的實戰過程,介紹 DDD 的完整流程和關鍵路徑。

進行領域驅動設計時,需要對組內成員進行定位。

最重要的是識別領域專家,即那些對特定領域有深入認知的人,他們能幫助團隊成員更好地理解業務,有利於後續的頭腦風暴和建模過程;其次是技術專家和開發團隊。

領域驅動設計的關鍵路徑如下:

**第一步,領域專家與開發團隊針對具體問題,明確業務願景,探討需求,從而確立統一的語言,積累領域知識。**統一語言意味着對問題領域內的概念達成共識,例如,團隊成員對某個詞語的定義有明確的認識,沒有歧義,從而降低溝通成本。

第二步,分析問題域並劃分子域(比如核心子域、支撐子域、通用子域),進而劃分限界上下文,構建上下文地圖。

**第三步,領域建模並實現模型。**將以上兩步分析,映射到代碼層面,進行模型實現。這一步驟可以概括爲 “兩關聯一循環”。“兩關聯” 指的是統一語言與模型之間的關聯,以及模型與軟件實踐之間的關聯。“一循環”意味着在實踐過程中,可能會遇到各種困難和不確定性,需要在不斷迭代的過程中提煉知識,最終趨近於完美的模型。

總之,領域驅動設計是一個動態迭代的過程,通過明確團隊成員的角色,發掘領域專家,建立統一語言,分析問題領域,進行建模和實踐,不斷優化和完善模型。在這個過程中,團隊成員需要密切合作,充分發揮各自專長,共同推動領域驅動設計取得成功。

2. 基於 DDD 落地實踐

上圖展示了基於 DDD 落地實踐的過程。

首先是定位願景,其重要性在於決定了後續的發展道路;

其次,分析問題域中的現有業務場景;

然後,根據劃分的子域,識別限界上下文;

最後,在限界內進行領域建模和實現模型。

1)問題域分析

① 定位願景

麥肯錫提出 “電梯演講” 概念是指,在乘坐電梯的 30 秒之內,向顧客清晰準確地解釋解決方案,即使用簡短的語言精準說明業務價值。

比如,去哪兒網的核心價值是 “總有你要的低價”。因此,所有核心工作都應圍繞低價展開。落實到基礎信息團隊,我們的願景就是提供豐富的信息聚合。

② 明確領域專家

由於產品迭代頻繁,系統演進缺乏領域專家。因此,按照產品、QA、技術人員的順序來確立領域專家。

爲了分析原有項目中,哪些用戶用例與我們的願景密切相關,我們整理了用例圖,並安排同學逐一分析。

分析結果顯示,經過多年的迭代,許多業務已經不再使用,舊業務無法適應現有的商業模式。

因此,我們將這些業務下線或重新分配資源,最終將 188 個用例精簡爲 79 個,大大簡化了工作內容。

③ 事件風暴

事件風暴主要關注三個方面:識別領域事件、識別決策命令、識別領域名詞。

事件風暴的輸出將作爲後續領域建模的輸入,需要遵循以下原則:

④ 統一語言

沒有 DDD 經驗的同學可能會問,什麼可以作爲統一語言?

答案是,什麼都可以作爲團隊的統一語言。

例如,如果一個老系統的內部邏輯複雜且難以理解,重構成本高,不易修改,可以將這部分代碼作爲團隊內部的統一語言。這樣一來,產品和技術的同學都瞭解老系統的目標、能力和應對策略。

由於技術同學在描述事物時偏向於使用代碼邏輯,這可能導致與產品同學的溝通出現偏差。

在這種情況下,統一語言可以拉近雙方的認知差距。技術同學只需拋出幾個領域名詞,產品同學就能理解。

需要注意的一點是,統一語言的術語表應包含中英文對照,以便在後期編碼和解碼過程中,在代碼層面實現認知統一。

2)識別限界及子域劃分

識別限界上下文的總體原則是先業務後技術,上圖展示了領域層面的劃分流程。

限界上下文的特徵

在繪製上下文依賴地圖時,要遵循三不原則:避免雙向依賴、循環依賴和過長依賴。

如上右圖所示,我們在製作上下文依賴地圖時,我們發現酒店解析依賴酒店抓取,酒店抓取依賴酒店聚合信息,酒店聚合信息依賴靜態信息,靜態信息依賴酒店解析出的數據,形成循環依賴。因此,劃分方式不合適。針對這種情況,我們創建了 “酒店上下文” 環節,打破循環依賴。

在限界上下文後,需要識別核心域、支撐域和通用域,劃分參考是與業務願景的相關性。

識別子域的好處是,對外可以明確告知自身核心競爭力;對內可以明確人員、設備資源分配,評估產品需求優先級以及是否處於核心領域。

3)領域建模

根據事件風暴和限界上下文的輸出,我們可以構建領域模型。

① 建模意義

② 建模過程

在建模過程中,最棘手的問題在於如何把握尺度,也就是判斷哪些方法應被納入模型,哪些屬性應置於哪個模型。我個人認爲,只要在團隊內達成共識,無論實際結果如何,都可以視爲正確。因爲建模是一個不斷優化的過程,隨着對業務理解的深入,要推翻之前的結論並一次性構建完美模型較爲困難。

在分層架構中,領域層和業務層都應存在。若盲目將功能和用例納入領域層,可能導致領域層過於龐大,進而影響其可重用性和業務表達力。因此,我們需要正視模型和領域能力的不確定性,逐步採取迭代方式,將能力下沉至領域模型中。

③ 建模原則

上圖展示了兩個原則:共性業務能力優先下沉到領域,共性技術問題抽象成業務。

沒有完美的模型,也沒有正確的模型,領域模型的共識即爲正確。因此,團隊的整體能力決定了模型完美程度的上限。提供一個參考的檢驗技巧:在建立完模型後,可以使用業務場景檢驗模型的完整度。如此循環往復,模型將逐漸完善。

④ 落地實踐時劃分微服務

如上所示,業務邊界、康威定律、業務變更頻率、彈性邊界、技術選型等,都可作爲劃分依據。

需要指出的是,一個微服務可以包含多個限界上下文,但只能包含一種子域類型(核心、通用、支撐),不能將核心域和支持域放在同一微服務中。如果支持域的可用性不佳,可能會影響核心邏輯,因此可能爲這個問題付出沉重代價。

4)模型實現

① 業務流程和領域模型映射

如上所示,業務流程或業務用例包含多個階段,每個階段又包含一系列活動。我們將這些業務活動與整個分層架構相結合,構建相應的映射關係。

建立映射關係的好處在於,在分層架構和領域模型高度凝聚、完善的情況下,便於後續需求的接入和擴展。從上到下分解業務流程,進行分層映射,技術負責度得以隔離。

② 模型映射代碼清單

如上圖所示,應用層、領域層、基礎設施層是領域對象的聚合,模型映射代碼清單明確了每個層次的能力、領域層的領域對象、是否存在前置依賴對象,以及包名、類名和方法名。這些內容與前面提到的中英文對照表相對應。

構建這份代碼清單有助於團隊內部協作,提高開發效率;爲新成員提供參考,快速瞭解項目核心邏輯和能力。

③ COLA 應用架構

在開發階段,我們採用了 COLA 開源框架,其分層架構包括適配層、領域層、應用層和基礎設施層。

無論是 COLA 還是 DDD 的分層架構,都圍繞業務核心,基於穩定的領域模型,對外提供領域能力。

選擇 COLA 的原因如下:

下圖是我們內部基於 COLA 架構落地微服務的實踐。

通過運用 COLA 框架,我們希望實現業務核心的模塊化,提高系統的可擴展性和靈活性。在實際開發過程中,團隊成員需遵循分層設計原則,緊密協作,不斷積累和共享領域知識,共同推動項目的成功。

上圖是對前一張圖的具體描述,我們採用了 CQRS 模式,即命令與查詢職責分離。

前文的一張圖描述了架構重構前的狀況:核心業務分散在各個服務中,沒有得到收斂和整合,業務耦合度較高。通過限界劃分和領域建模,我們實現了業務的分離。

在改造前,系統缺乏實時查詢功能,各團隊將數據拉回並本地緩存。經過重構,我們採用了異步調用機制,實現了數據持久化,並對外提供查詢功能。

在重構前,系統沒有提供實時查詢能力,各團隊將數據拉走並進行本地緩存。重構後,基於異步調用機制,實現了數據持久化,並對外提供查詢功能。

④ 領域模型與代碼模型映射

上圖是領域模型與代碼模型的映射。分層對應上一張圖展示的架構,在 Domain 層,根據領域劃分進行聚合分包。圖中標藍的 Hoteltree 就是前文提到的酒店聚合,在該領域內進行功能劃分。

Domain Primitive 是 Value Object 的進階版,它在原始 VO 的基礎上要求每個 DP 擁有概念的整體,而不僅僅是值對象。在 VO 的不可變基礎上增加了有效性(Validity)和行爲。

DP 特徵如下:

例如,聯繫信息對外顯示爲電話號碼,但背後隱藏了區號、國內外來源等隱式屬性。通過 DP,我們可以找出這些隱式屬性並將其轉化爲顯式,這是推薦使用 DP 的關鍵原因之一。

通過以上映射,我們希望實現領域模型的清晰劃分,降低業務耦合度,提高系統的可擴展性和靈活性。在實際開發過程中,團隊成員應緊密協作,不斷積累和共享領域知識,共同推動項目的成功。

在架構重構之前,系統核心業務分散在各個服務中,耦合度較高。通過限界劃分和領域建模,我們實現了業務的分離。重構後,系統具備了實時查詢功能,數據持久化,並對外提供查詢服務。在這個過程中,團隊遵循分層設計原則,緊密協作,不斷積累和共享領域知識,共同推動項目的成功。

五、總結和思考

1. 項目落地效果

1)組織效率

2)開發效率

3)鞏固效率之本,分擔產品之憂

2. 思維模型改變

技術團隊從被動了解業務轉變爲主動了解業務,解讀業務策略變化,爲其定義測量,提出數字化方案。產品經理的核心價值是成爲技術與業務之間的橋樑,但通過 DDD,技術同學也更加關注業務,實現產研融合。

1)問題域分析領域建模

2)模型實現

3. DDD 帶來的優劣勢及建議

1)優勢

2)劣勢

3)使用建議

業務架構是領域,技術架構是容器,脫離靈魂的容器沒有技術意義。

Q&A

Q1:DDD 重構時,如何協調產品上線需求的矛盾?

A1:首先,我們在進行 DDD 重構時,要依託公司技術中心的戰略,公司對此表示鼓勵和倡導;其次,重構模式包括修繕者、絞殺者、演進式。面臨與產品上線需求的矛盾時,我們可以選擇絞殺者,重新構建優化,在原有業務中也不會影響產品新需求的接入。

Q2:選擇 COLA 架構作爲 DDD 重構業務模型的原因是什麼?

A2:首先,COLA 是阿里開源的,具有大廠背書,可信度較高;其次,COLA 具備優秀的分層架構和規範,項目 Github 中提供了最佳實踐。如果初期不確定如何進行重構,可以直接參考官方 demo,將其映射到自己的業務中,後期再加入自身見解,進行系統優化。

說在最後

DDD 架構如何落地,是非常常見的面試題。

以上的內容,如果大家能對答如流,如數家珍,基本上 面試官會被你 震驚到、吸引到。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/V8S3MmOFKQLhVDE4SGU2rw