網易新聞App架構重構實踐:DDD正走向流行

當前,大多數移動開發團隊選擇以 MVP 作爲業務層的核心架構模型,在此基礎上實現了客戶端的組件化、插件化、容器化等,但作爲業務層核心的 MVP 架構模式至今仍有諸多弊端。網易新聞 App 在領域驅動設計(DDD)思想指導下,對其架構做了整體重構,得到了不錯的重構質量與項目收益。移動端架構與網站架構的區別是什麼?網易新聞客戶端的架構演進歷程是怎樣的?爲什麼要選擇 DDD 思想來指導重構?DDD 落地中應當關注哪些方面?帶着這些問題,InfoQ 記者採訪了網易高級客戶端工程師李雲鵬,他也將在 QCon 北京 2020 上帶來《在領域驅動設計(DDD)下重構網易新聞 APP 架構》的演講。

移動端架構與網站架構的區別

傳統意義上的網站架構,通過超文本傳輸協議,瀏覽器可以快捷方便地將我們想要訪問的頁面呈現在眼前。每個網站由多個頁面組成,這些頁面也都屬於 Web Server 的一部分,如圖 1 所示。

圖 1 Website 與 Server 抽象模型

網站大多數時候不需要關注離線化,而主要關注負載均衡、高併發和多級緩存等場景,以實時響應大規模流量。

而移動端 App 需要用戶先進行安裝操作,然後再進行頁面訪問,其中的高併發網絡請求等場景通常交由 Server 端處理,移動端則更關注處理用戶交互與界面變化,如圖 2 所示。

圖 2 Application 與 Server 抽象模型

所以移動端和網站端的核心關注點不同,也就造成了二者在架構上的歷史演進的不同。舉一個簡單的例子,最初起源於 .NET Framework 3.0 的模型 / 視圖 / 視圖模型(MVVM)思想,從 WPF 應用過渡到 ASP.NET,用於網頁的開發,後來卻在移動端成爲最流行的架構模式之一。

隨着網站端的歷史演進,網頁的工作量和跨平臺的需求增長迅速,使得網站前端開發的重要性日趨明顯,網站端已經抽象爲了前端和後端,網站前端通過瀏覽器實現跨平臺,與移動端共同組成了大前端。

目前常見的移動端架構設計模式主要包括關注面向接口編程的 MVP(Model-View-Presenter)、關注數據驅動與雙向綁定的 MVVM(Model-View-ViewModel)、關注表現層分離的 MVC(Mode-View-Controller)和符合領域驅動設計思想(DDD)的 The Clean Architecture 等。

網易新聞客戶端的架構演進歷程

網易新聞客戶端的架構演進主要經歷了四個階段:Static Method、MVP、MVPs 和 VIPER。

爲擺脫沉重晦澀的架構模型束縛,網易新聞客戶端團隊將一些軟件設計中的元素抽象爲輕鬆的食品加工中的元素,用蛋糕模型來做示例。

第一階段:Static Method

從最初的設計階段開始,爲了簡單地達到代碼的可複用性,新聞客戶端採用了一種 Static Method 的設計方式,將業務邏輯按照業務模塊轉移到一些工具類中,使得開發人員可以用最小成本複用這些業務邏輯。

在 Static Method 的模式中,技術團隊將 View 抽象爲一塊蛋糕,蛋糕上面需要奶油,檸檬,櫻桃(Model)等食材,所有負責輸送材料的加工廠(Static Method)派工人(而不是廚師)將材料直接運送並擺放在蛋糕上面,如圖 3 所示。這使得蛋糕最後擁有了所有他該有的食材成分。但這些食材存在隨意擺放,使蛋糕變得混亂的情況,如果再繼續這樣堆砌食材,它就不再像是一塊蛋糕了。

圖 3 Static Method 蛋糕加工模型

所以,隨着時間的推進和業務迭代的不斷加速,這種喪失了封裝、多態和繼承的面向對象特性的工具類設計,難以應對業務的變化,過渡到流行的 MVP 模式已成必然趨勢。

第二階段:MVP

傳統的 MVP 模式中,一個 View 由一個 Presenter 管理,在這種模式下產生了代碼複用的難題。

由於 Presenter 與 View 通過接口協議綁定,通常一個 Presenter 裏的業務邏輯只能爲一個 View 服務,代碼複用性的喪失會導致大量冗餘代碼的產生。

Presenter 與 View 爲一對一的方式,就像一塊蛋糕(View),指派給一個廚師(Presenter)去製作,但是廚師一個人需要做的事情太多,他需要親自加工食材(Model),再將這些材料一一裝飾在蛋糕上面,如圖 4 所示。如果這時候再告訴他我們的蛋糕還需要添加一些突然增加的裝飾和點綴,他可能會面臨崩潰。

圖 4 MVP 蛋糕加工模型

第三階段:MVPs

爲了解決 MVP 代碼複用的問題,大多數的設計方式都是將 View 與 Presenter 改爲多對一的模式,使得一個 Presenter 可以爲多個 View 服務,而一個 View 也被多個 Presenter 控制。更多的 Presenter 介入也意味着這些 Presenter 爲了適應不同的 View 將產生更多的接口。

Presenter 與 View 爲多對一的方式,就像一塊蛋糕(View),指派給多個廚師(Presenters)在共同加工。而每個廚師可能會處理多塊蛋糕,他們同時還要做好手上的裝飾品(Model),再親自將其放在每個蛋糕上。在這期間,廚師們直接接觸每塊蛋糕時,還加入了很多他們擅長的但是蛋糕不需要的手藝。多個廚師圍着一塊蛋糕轉,蛋糕有了很多他原本不想擁有的東西,而這些廚師們也難以管理,他們直接操控蛋糕,沒人能夠合理控制他們,如圖 5 所示。所以,新聞客戶端過渡到了符合 DDD 的 VIPER 模式下。

圖 5 MVPs 蛋糕加工模型

第四階段:符合 DDD 的 VIPER

在符合領域驅動設計的 VIPER 架構設計模式下,一塊蛋糕(View)只由一個主廚(Presenter)進行裝飾擺放,但是蛋糕上所有的飾品食材,都由這位主廚指派給多個不同的廚師(Interactor)進行加工(Entity)。當這些廚師加工完畢後,再把材料送過來,通知主廚,由主廚親自進行擺放。

那些負責加工的廚師沒有機會再直接接觸到蛋糕,蛋糕只有它原本應有的樣子,變得更加利於加工製作。更美好的是,以往蛋糕需要每個廚師親自配送到它需要被送達的地方,現在廚師只需要打個電話,一切配送工作都將由快遞員(Router)去完成,如圖 6 所示。

圖 6 VIPER 蛋糕加工模型

基於 DDD 的短視頻架構優化

以網易新聞客戶端的視頻詳情頁爲例,新聞客戶端的視頻詳情頁結構可以抽象爲圖 7。初期設計的視頻詳情頁,所承載的業務並不多,界面也較爲簡單,Holder、子頁面和適配器等都在 Fragment 類中定義實現。但是隨着短視頻風口的到來,業務加速迭代,視頻的業務需求急劇增加,視頻詳情頁所需要承載的業務也越來越多,Fragment 類從最初的幾百行,急速擴張到兩千多行。基於舊有的視頻詳情頁設計,加入新的業務,使得維護成本變得越來越高,類也變得越來越大,臃腫膨脹的類已經變爲了“麪條代碼”。

圖 7 視頻詳情頁抽象結構

基於 DDD 的思想,新聞客戶端實現了一套包含 UseCase 的基礎框架,劃分出了領域模型,由於視頻詳情頁由多 Fragment 組成,技術團隊還加入了共享變量層,使擁有統一生命週期的組件之間能解耦傳遞數據,重構後的視頻詳情頁整體架構如圖 8 所示。

圖 8 視頻詳情頁重構後的架構設計圖

DDD 的選型與實踐

選型背景

新聞客戶端的多數業務模塊在設計初期的時候,承載的業務都不是很多。當某些業務模塊的需求逐漸增加,開發人員爲了快速完成迭代工作,代碼經常會衝刺堆積在一起,久而久之,業務模塊之間的邊界變得越來越不清晰,耦合也變得越來越嚴重。

DDD 的限界上下文可以幫助技術團隊定義出清晰的領域模型邊界,以達到解耦的目的。VIPER 是符合 DDD 理念的架構模型,VIPER 曾一直流行於 iOS 端,在 Android 端的應用案例非常少,但其實 VIPER 的核心思想是 The Clean Architecture,所以它同樣適用於 Android 端,是一個通用的解決方案。

落地難題

在將 DDD 落地的過程中,團隊遇到的比較困難的問題大致是兩方面,一方面在於優化工作流程,另一方面在於轉變編碼思想。

在工作流程方面,大多數互聯網公司都會有需求評審會,其中會有產品經理、項目經理、軟件工程師、UI 交互設計師等參與。但傳統形式的需求評審主要關注點還是在整體的業務本身,在事件劃分上是非常模糊的,這與確定限界上下文的事件風暴相差一段距離,推動多個團隊變更評審方式的難度比較大。所以,開發團隊內部在編碼階段開始前,還會進行一次技術評審,由準領域專家們參與到其中,與開發人員共同分析討論界限上下文。

在編碼思想方面,需要團隊成員接受 DDD 的思想,轉變爲領域驅動思維,技術團隊在內部進行了一系列相關的分享,通過編程中的“隱喻”,讓大家循序漸進地建立對 DDD 的認知,逐步加深瞭解。

重構效果

在基於 DDD 的架構重構初期,新聞客戶端選取了視頻詳情、視頻列表和圖集三個業務模塊使用 VIPER 重構,對 DDD 進行嘗試性探索。

  • 在重構質量上,由於先確定了領域模型,代碼整體解耦符合預期,三個模塊重構後上線均未產生嚴重線上問題。
  • 在項目收益上,一方面,DDD 幫助團隊加速了迭代的開發效率,另一方面,代碼的可維護性也有了比較大的提高,同時,模塊錯誤率也約降低了約 50%。

新聞客戶端以半年爲一個維度,統計出模塊重構前半年和重構後半年的系統故障率(主要指程序開發期間產生的問題),得出瞭如圖 9 所示的信息。數據發現,越是業務變化很頻繁的模塊(如視頻),通過 DDD 獲得的模塊穩定性收益就越大。

圖 9 DDD 重構前後系統故障率統計圖

DDD 落地面面觀

DDD 從 2003 年被提出來以後,得到了軟件學術界的高度認可,但由於國內外開發環境和開發人員思想理念不同等多種影響,導致 DDD 在國內的實踐並不是很理想。從 2013 年開始,微服務架構和中臺化在國內逐漸盛行,而 DDD 可以作爲其指導思想,幫助微服務架構進行清晰的領域和子域劃分,DDD 逐漸在企業應用實踐上獲得理想的成果,這正是 DDD 在國內突然變得流行的最主要原因之一。

對於開發團隊來說,實踐 DDD 最重要的一步就是通過事件風暴來進行領域分析建模,但這對於帶頭發起人的領域素養要求較高,需要團隊內有一位佈道師開路,擔任領域專家的職責。

針對移動端來說,目前最合適的、符合 DDD 的架構模型就是 The Clean Architecture,Google 官方推出的安卓藍圖項目也針對 MVP 提供了一套符合 The Clean Architecture 的 MVP-Clean 項目,開發者可以由此爲起點進行逐步探索和嘗試。

由於國內外軟件工程師職業環境和所承受的壓力有所不同,在很多突發性的業務需求的衝擊下,使得開發者只能對代碼做瘋狂堆疊,導致開發者不得已放棄 DDD 的設計,而期間發生需求變更就很容易導致風險。

當風險變爲危險後,發生各種互相推諉的現象,其問題本質歸結起來無非是兩方面,一方面是組織結構和環境所影響,另一方面是邊界劃分不明確。

對於組織和團隊方面,前期無需變動,也可以滿足向 DDD 轉型的條件,也爲後期再建設微服務和中臺化提供了便利。但需要注意的是,改變團隊成員的固有開發思維也是十分重要的一個環節,團隊內應該定期組織關於 DDD 的分享,有利於使大家對 DDD 的觀念潛移默化。

嘉賓介紹

李雲鵬,網易新聞架構技術組工程師,國內首個移動架構模型書籍《移動開發架構設計實戰》作者。10 餘年互聯網行業經驗,擅長移動端架構選型、項目重構與插件開發相關工作。曾就職於世界 500 強核心技術實驗室,作爲第一發明人,申請了 14 項專利和著作權。


QCon北京2020的演講中,李雲鵬老師將從近五年的網易新聞客戶端架構模型演進展開討論,着重介紹新聞團隊從舊有架構模型,遷移到以 MVP 爲基礎的、符合領域驅動設計思想(DDD)的 VIPER 架構時,經歷的自我創新與踩坑實踐,以幫助開發者瞭解“重構如何保障模塊穩定性”,“如何快速重構業務模塊”等痛點問題的解決方案。同時,針對不同類型移動端產品舉例介紹架構模型選型策略,進而發散開發者架構設計思維,明確架構問題分析方式,掌握架構選型要點。點擊瞭解詳情

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章