業務瘦身42%+效率提升50% :去哪兒網業務重構DDD落地實踐

陶然陶然發表於2022-11-10

   一、案例簡述

  隨著集團戰略方向調整與業務重組,酒店供應鏈也面臨全新的調整。酒店基礎資料業務系統是從國際團隊接手,屬於10年前系統架構,涉及20多個微服務,架構老舊、系統耦合嚴重及業務邊界模糊,加上業務的快速發展,導致系統靈活性不足及無法快速承接產品需求,產研合作出現效率問題。因此,酒店供應鏈技術側結合酒店BU及公司相關成功案例,在2022年初主動發起基於DDD思想的技術架構調整,完整落地戰略、戰術設計及系統實現,本次重點介紹重構落地過程、設計原則及總結。

   二、問題分析

  1、業務需求複雜

  酒店基礎資訊業務系統最初是為去哪兒獨立業務設計,主要包括酒店聚合、房型聚合、圖片及城市等相關資訊(見下圖),最初業務劃分比較清晰。2015年攜程戰略收購去哪兒網後,業務戰略也隨之發生變化,但當時並未對系統進行隔離和重構,隨著需求迭代的不斷演化,業務邏輯變得越來越複雜,加上產品經理的流動性較大,酒店基礎資料PM、DEV、QA團隊缺少業務專家,大家對於原始需求缺少理解,部分業務不確定能否下線等等。  

  2、系統過度耦合

  酒店基礎資料涉及微服務20多個,系統之間耦合嚴重,模組彼此關聯,我們的系統越來越冗雜。有時修改一個很小的產品需求,光回溯該需求涉及需要修改的系統及功能點就需要達到“天”級別,更別提修改帶來的不可預知的影響面,無論還是產品、技術需求還是工單問題排查等,都給組內同學帶來較高學習成本和開發成本。

  下圖是我們酒店基礎資訊日常開發中的一個常見的系統耦合案例。  

  這個案列是要完成的業務功能是支援運營人員手工新增酒店圖片,當時預估工時時長8pd,實際工時11pd。上圖看出從運營開始上傳到我們最終將圖片外網展示,整體流程涉及到了4個系統。

  以下這段話是當時開發這個產品需求的同學的“真情告白”:

  需求及系統設計階段。接到TL的排期並閱讀產品PRD,發現涉及4個業務系統,深入系統內部發現業務邏輯複雜及系統耦合嚴重,熟悉系統及編寫設計方案花費3pd;

  系統開發階段。按照設計進行功能開發發現,除正常多業務系統呼叫外,業務系統內部存在兜底定時任務,需要進一步熟悉其作用、資料儲存及影響範圍,花費6pd;

  系統自測階段。系統自測涉及多個系統間的耦合及不同的資料庫版本,自測花費2pd。

  如果我們的系統經過精心設計,這個案例是不需要8pd,更別說最終上線花費11pd。我們可以看出系統過度耦合,不但降低我們的開發效率,而且對於開發人員不夠友好,這種情況對於人員的穩定性也造成很不好的影響,所以我們必須要做出改變。

   三、為什麼選擇DDD

  DDD有很多優勢,我們站在EA角度,可以繫結業務架構和系統架構,作為中間層,將問題域與應用架構相剝離;我們站在軟體複雜度角度,可以有效解決業務複雜度和軟體複雜度問題;還比如DDD可以有效的從業務視角對軟體系統進行拆解,是微服務劃分較好的實踐等等,作為基礎資料重構,結合我們上面提到的業務需求複雜和業務過度耦合問題,我們主要從解決軟體複雜度和微服務拆分角度考慮,最終選擇DDD作為我們專案落地的指導原則。

  1、軟體複雜度應對

  Eric Evans 認為“應用程式最主要的複雜性並不在技術上,而是來自領域本身、使用者的活動或業務”。因而,領域驅動設計關注的焦點在於領域和領域邏輯,因為軟體系統的本質其實是給客戶(使用者)提供具有業務價值的領域功能。那麼DDD是如何應對軟體複雜度的呢?我們可以粗略的歸為三類,分別是分而治之、關注點分離和統一語言。

  分而治之

  比起單體架構將所有功能都糅合在一起,DDD透過在其戰略設計層面對限界上下文、上下文地圖的劃分來做到這一點。各個業務領域內關注自身業務能力的內聚,明確分工,不被其他領域業務侵蝕,這不僅符合“高內聚、低耦合”的架構思路,更是與微服務拆分的思想不謀而合。

  關注點分離

  DDD使得領域模型與儲存模型分離,業務複雜度與技術複雜度分離。

  統一語言

  我們大部分需求是橫跨多個團隊,需求傳遞低效,需要反覆溝通,方案產出效率低,而統一語言使得產研在業務概念、理解等方面達成一致,降低溝通和理解成本。  

  2、微服務架構模式的優秀實踐

  微服務有9大特徵,我們這裡不贅述。由於DDD可以有效的從業務視角對軟體系統進行拆解,並且DDD特別契合微服務的一個特徵:圍繞業務能力構建。所以用DDD拆分出來的微服務是比較合理的而且能夠實現高內聚低耦合。我們之前會因為微服務不知如何拆分討論上好幾天,其實根本原因是不知道邊界在什麼地方,而使用DDD對業務分析的時候:

  使用聚合把關聯性強的業務概念劃分在同一個限界上下文,並限定聚合和聚合之間只能透過聚合根來訪問。

  聚合基礎之上根據業務相關性,業務變化頻率,組織結構等等約束條件來定義限界上下文。

  DDD的聚合和限界上下文,使得拆分微服務不再困難。

   四、演化式技術改造

  在我們確定使用DDD作為指導進行專案落地,一個難題是我們如何推進多業務系統重構上線,如果採用完全重做的方式,摒棄原有的系統重新開發,雖然可以快速有效擺脫原有系統的歷史技術債,但是也會丟失那些細小而繁雜的業務邏輯,為整個系統的改造帶來巨大的專案風險,特別是像酒店聚合、房型聚合等涉及多種演算法的業務,嚴重時會導致高額賠付。

  最終我們採用的是演化式技術改造,即利用重構在原有的系統上逐步改造。即在原有系統的基礎上,透過一步一步演進式的程式碼調整,逐步達到技術改造的目標。每一步重構,對於核心業務功能保持外部功能不變,重點調整程式內部結構,透過內部程式結構的最佳化,業務程式碼逐漸與各個層次、各種技術解耦。可以看出,演化式技術改造的目標是實現業務程式碼與技術框架的解耦,透過重構將長週期的改造過程,透過業務域、重要程度等劃分成一個短週期的重構,保障每次重構的正確性。

  酒店基礎資料重構包含酒店業務、房型業務及圖片業務,我們採用分階段上線,優先上線酒店業務。那麼對於酒店業務,其又包含了酒店聚合、酒店靜態資訊、酒店抓取等多個子業務,為保障重構業務輸出正確性及階段成果,我們將酒店業務又劃分兩階段上線和功能驗證,正是這樣的過程,保障了在改造過程中,雖然修改了既有程式碼,但沒有影響既有功能,使得改造更加平穩的進行下去。經過這一系列改造,系統的業務程式碼與技術框架解耦了,介面層建立起來,就可以從容地開展真正的技術改造。  

   五、DDD實踐及設計原則

  1、完整的DDD落地流程框架  

  上圖為整體DDD落地流程框架,也是thought works強力推薦的流程,那麼我們本篇文章並不打算去講解流程的每個細節,而是重點介紹我們在酒店基礎資訊重構過程中,所積累的落地時間經驗和原則,希望透過這些經過抽象和總結的經驗,能夠給各位帶來一些收穫和思考,具體內容請接著往下看。

  2、DDD落地實踐原則

  1)產研溝通,定位願景

  定位願景的主要目的是對產品的頂層價值設計,對產品目標使用者、核心價值、差異化競爭點、痛點等策略層資訊在團隊層面達成共識,這也是我們做這個事情的根本。而對於部分的DDD分享文章或者我們真正在做專案落地時,缺少關注我們的專案定位、願景是什麼,專案前期沒有關注,會導致我們在劃分子域時,不清楚如何確定是核心域、支撐域還是通用域等,以及資源安排沒有側重點,願景的定位非常重要,尤其在我們新專案落地。

  我們常用的定位願景所使用的方法叫“電梯演講”, 領域專家與專案團隊一起思考,我們做的專案業務範圍、目標使用者、核心價值和願景,我們與同類產品的差異和優勢在哪裡?整個過程是統一專案建設方向和團隊思想的過程,當然如果我們的專案是一個純粹的後端業務系統,並不是所有點都涉及,也可以跳過,比如下圖所示的“關鍵的優點,難以抗拒的使用理由”,對於酒店靜態資訊而言,我們的使用使用者更多是我們公司內部,那麼就沒有難以抗拒的使用理由了。  

  2)產研融合,提煉問題域

  理解一個複雜問題域以便創造簡單且有用的模型,需要深入詳盡的知識以及深刻的見解,這些需要產、運、研、測共同協作得到。只有透過協作及共享對問題域的理解,才能有效設計領域模型以應對業務的挑戰,這樣也能具備足夠的靈活性應對新出現的需求。初期我們與產運一起,透過線上畫板工具(初期推薦使用BeeArt,也可使用ProcessOn)及公司白板,開展事件及命令風暴,一起探討應用程式的應用場景。這一過程是所有參與者進行花火碰撞,獲得領域的深刻見解的催化劑,透過風暴我們提煉出領域知識,重新梳理業務流程,並形成通用語言(可以包括顯示的業務規則、領域名詞解釋等,形成統一的思維地圖),達成共識。

  下圖右半部分為“酒店靜態資訊業務流程”,我們本次重構重新對原有“蜘蛛網”式的業務流程進行梳理,明確業務流階段、價值及相應顯式規則,與產運溝通確認20次(微信、郵件、線下會議、騰訊會議等)。透過產研融合,提煉問題域後,確認線上低價值或無用業務佔比線上總業務用例的41.9%(原始業務用例:222個,重構後保留業務用例:129個,共下掉93個業務)。  

  3)集中精力,專注核心域

  酒店基礎資料涵蓋酒店、房型、圖片及位置區域等多個問題域,我們需要將問題域分而治之來降低複雜性,因為較小的模型可以在子域的上下文中更容易被理解。並透過劃分核心域、通用域及支撐域來決定研發策略(比如:影片、圖片及城市等通用域業務簡單且需求相對穩定,並未進行重構,保持現狀)及資源配備。相信大家在進行專案開發的時候,一定會覺得時間緊、任務重及資源不足等等,那麼這個時候我們更要關注要做的專案,他的核心價值是什麼(可以參考領域願景),因為他代表團隊或組織的價值所在,所以我們要專注核心領域,安排核心開發人員及做好跟蹤,確保核心領域按照預期推進。  

  4)使用限界上下文保護領域模型完整

  限界上下文擁有從展現層到領域邏輯層,再到持久化,甚至到資料儲存功能的垂直切片,那麼產品的概念可以存在每個限界上下文中,並且包含僅對該上下文普遍存在的特性和邏輯。任何限界上下文中的變化不再具有對其他有界上下文的影響,因為限界上下文或子域是隔離的。限界上下文具備概念上的獨立性,一個限界上下文內的子概念的解釋和目的都不應該超出上下文的邊界,若出現以下依賴關係,需要思考是否存在未澄清的問題:

  雙向依賴:上下文之間缺少一層未被澄清的上下文,或者兩個上下文其實可被合為一個;

  迴圈依賴:任何一個上下文發生變更,依賴鏈條上的上下文均需要改變;

  過長的依賴:自身依賴的資訊不能直接從依賴者獲取到,需要透過依賴者從其依賴的上下文獲取並傳遞,依賴鏈 路過長,依賴鏈條上的任何一個上下文發生變更,其鏈條後的任何一個上下文均可能需要改變。  

  另外我們要把握好領域職責,領域之外的事不要管,同時領域之間的資料互動,需要透過“防腐層”將領域外的物件轉換為領域內物件。舉個例子:下圖是我們酒店的抓取解析流程,重構前不僅做了抓取和解析,還做了特殊屬性處理、影片處理等非自己領域的事情,所以我們重構後,將這兩個事情讓給了“酒店靜態資訊上下文”去處理,並形成抓取解析上下文,只做一件事情:從外部抓取資料,並將資料轉換為Qunar內部物件。  

  5)應用程式架構落地COLA

  目前比較流行的應用架構,都會遵循一些共同模式,不管是六邊形架構、洋蔥圈架構、整潔架構、還是COLA架構,都提倡以業務為核心,解耦外部依賴,分離業務複雜度和技術複雜度。我們最終選擇了COLA架構,有幾點原因:

  ① 明確的分層架構

  所有的複雜系統都會呈現出層級結構,應用系統處理複雜業務邏輯也應該是分層的,下層對上層遮蔽處理細節,每一層各司其職,分離關注點。

  適配層:針對不同端、協議的適配,包括酒店基礎資訊命令執行、查詢及適配返回等;

  應用服務層:酒店基礎資訊業務用例識別,並負責呼叫領域層能力,對用例進行組裝、編排及返回結果;

  領域服務層:提供酒店基礎資訊相關業務能力,並針對複雜或跨聚合業務提供領域服務能力,供應用服務層呼叫;

  基礎設施層:主要處理技術細節問題的處理,包括領域外部服務訪問防腐層實現,資料庫DB、快取等持久化。  

  

  擴充套件:我們在整個戰術設計的過程中,利用奧卡姆剃刀原理(是指如無必要,勿增實體,即“簡單有效原理”)的思想,引入DP(Domain Primitive 是 Value Object 的進階版,在原始 VO 的基礎上要求每個DP擁有概念的整體,而不僅僅是值物件)並把實體屬性歸類,為 VO 的 Immutable 基礎上增加了 Validity 和無狀態行為,防止將過多的屬性拍平到實體。

  

  ② 符合演進式架構設計

  分層是屬於大粒度的職責劃分,我們有必要往下再down一層,細化到包結構的粒度,才能更好的指導我們的工作。COLA架構除了有分層規範以外,對每一層內部的包結構也有明確規範,即“聚合分包,功能分類”,這樣的設計可以更好的應對未來需求變化的不確定性,符合演進式架構(演進式架構就是以支援增量的、非破壞的變更作為第一原則,同時支援在應用程式結構層面的多維度變化。那如何判斷微服務設計是否合理呢?隨著業務的發展或需求的變更,在不斷重新拆分或者組合成新的微服務的過程中,不會大幅增加軟體開發和維護的成本,並且這個架構演進的過程是非常輕鬆、簡單的)。  

  

  COLA架構,本質上沒有什麼嚴格的約束,對於業務程式碼,還是有非常好的指導建議,不一定非要嚴格的按照框架的要求來執行,但一定要有規範的思想,這才是核心的。實際開發中,我們並沒有完全按照CLOA架構的指導要求來,比如擴充套件元件並沒有使用,適合自己的才最重要。相信只要我們做好分模組,分層次,做好命名規範+一定的充血模型,程式碼就能做到簡潔易懂。

   六、案例成果總結

  1、案例重構成果

  1)業務複雜度方面,我們共梳理188個業務用例,重構後保留109個,共下掉79個,業務平均下掉42%,對於降低產運研業務學習帶來較大幫助;

  2)瘦身服務及減鏈路方面,酒店基礎資訊業務涉及21個應用微服務,透過DDD領域劃分後下降到13個,微服務減少33%,對應程式碼下降情況統計,平均程式碼下掉58.3%,大大降低研發學習及硬體成本;

  3)業務專家方面,透過事件、命令風暴,對於專案成員對酒店基礎資訊業務更加有全域性觀,與產研形成統一語言和知識,減少產、運、測、研溝通成本,在業務專家方面增加2名研發業務專家,讓我們更多站在業務視角去思考和解決問題,而不是來了需求首先想到的是工時問題;

  4)效率提升方面,問題處理,工單處理下降50%。  

  2、專案重構常見問題

  1)沒有領域專家怎麼辦?

  我們都知道,業務領域專家和開發團隊之間的協作是DDD必不可少的部分,特別是戰略、戰術設計階段。不過,尋找到當前業務領域中是專家以及能夠為問題域提供深刻見解的人,可謂是少之又少。那麼如果沒有領域專家怎麼辦呢?作為替代,可以尋找對於我們當前工作領域具備多年經驗和理解的產品所有者、使用者、研發或其他任何人,不必在意他們是不是權威,是不是管理者等等。

  以我們當前的重構專案為例,酒店基礎資訊產品流動率高,沒有對應的產品業務專家,最終我們尋找到具有多年工作經驗的資深研發和業務QA作為我們的領域專家,最終取得的效果也是非常不錯的。我們推薦的業務領域專家順序為:業務產品團隊->技術負責人->業務開發負責人->業務QA。

  2)簡單問題複雜化

  DDD的價值在於幫助管理顯著優勢的複雜問題域,請不要輕視MVC模式,因為並非系統所有的部分都要被精心設計

  我們的酒店基礎資訊重構專案,有酒店、房型、圖片等多個問題子域,以房型子域為例,有房型抓取、落地及房型聚合等多個限界上下文,其中房型聚合業務邏輯簡單,清晰明瞭,所以我們並沒有採用DDD分層架構模式進行實現,而是使用MVC模式,不僅縮短工期,且可以快速上線驗證效果,所以我們要避免將領域模型應用到每個限界上下文。

  解決方案並不總是技術層面

  當遇到專案問題時,我們大部分人的思維是從實現上進行解決,比如技術實現考慮高併發、高可用及高效能,但是真的有必須要嗎?這讓我想起一句話:業務架構是靈魂,技術架構是容器,脫離靈魂得容器是沒有意義的!以我們的重構專案為例,我們在進行事件風暴前的業務用例梳理時,我們把業務進行歸類,對於非核心業務我們會找相關業務使用方進行溝通確認,是否可以下線,是否可以合併,是否可以簡化操作等等,最終我們在業務複雜度方面下降43%,節省大量的產研溝通和研發成本。

  3)非核心領域佔用大量精力

  我們可能因為沒有使用領域願景來描述我們專案的核心競爭力,導致我們缺乏對專案成敗核心是什麼的關注,而我們的資源是有限的,時間是有限的,我們只有把有限的資源投入到最重要的區域-核心領域,才能更好的服務於企業戰略,實現商業價值。以我們重構專案為例,我們專案開始則高亮我們的願景,明確核心領域,把資深人員安排在核心的位置,保障核心領域的研發和交付質量。在專案最重要的區域分攤太少的資源就是反模式。

  4)產品不關注是否使用DDD,只關注需求是否如期上線

  對於產品只關注自己的需求是否如期上線,這種情況非常常見,所以需要我們要付出一些努力來達成我們做DDD重構的目標,我認為可以從以下幾個方面入手:

  講清楚系統現狀,以及使用DDD重構後所帶來的價值,達成價值共識(與產品,必要可能需要跟產品的上級);

  資源緊張情況下,重要需求正常排期,非重要與產品溝通延遲上線;

  優先解決系統核心價值域問題,分階段成果釋出;

  研發資源閉環和共享模式共存。關於閉環和共享解釋如下(摘自“人人都是產品經理”):

  閉環:就是和業務需求方繫結,專門做此類變化快的需求開發,其他的都不做;而共享則相反,將研發資源共享成一個池,所有的業務需求也彙總在一個或多個優先順序佇列裡,排隊開發。

  共享:有利於充分利用研發資源,規模化、專業化,提升吞吐,但可能也降低了平均響應時間,更適合於進入成熟期,穩定漸進發展的業務。閉環,優先考慮專屬業務需要的響應,但也失去了規模與專業化效應,更適合快速發展期的創新業務,而過了業務高速期,專屬的研發就會形成資源浪費,對個體的成長也有不利因素。

   七、總結

  本文以酒店基礎資訊DDD重構實踐為案例,介紹了我們為什麼選擇DDD,完整的DDD落地流程框架,以及利用大量篇幅和例子介紹我們專案落地實踐的原則,最後介紹了案例成果和專案重構過程中常見的問題及解決方案,相信大家對DDD有了更加清晰的認識,同時在專案落地時也有了一些明確的指導原則,避免踩坑。但是DDD不是靈丹妙藥,更不是“銀彈”,最後有幾點建議送給大家:

  DDD是思想,是一種業務領域建模方法論、業務架構設計方法論,是指導開發過程的方法論。

  DDD是業務+技術的共同深度參與,開發人員需要有思考方式的轉變,所以實踐能否成功,不僅僅是技術的問題,更是貫徹實施的問題。

  戰略設計階段屬於整個DDD核心階段,領域邊界劃分對於團隊的抽象能力有一定挑戰,請不要為了“省事”,最後做成了“精簡版”的DDD。

來自 “ Qunar技術沙龍 ”, 原文作者:李全黨;原文連結:http://server.it168.com/a2022/1110/6774/000006774029.shtml,如有侵權,請聯絡管理員刪除。

相關文章