天天看點

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

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

傳統意義上的網站架構,通過超文本傳輸協定,浏覽器可以快捷友善地将我們想要通路的頁面呈現在眼前。每個網站由多個頁面組成,這些頁面也都屬于 Web Server 的一部分,如圖 1 所示。

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

圖 1  Website 與 Server 抽象模型

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

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

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

圖 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 等。

2網易新聞用戶端的架構演進曆程

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

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

第一階段:Static Method

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

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

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

圖 3  Static Method 蛋糕加工模型

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

第二階段:MVP

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

由于 Presenter 與 View 通過接口協定綁定,通常一個 Presenter 裡的業務邏輯隻能為一個 View 服務,代碼複用性的喪失會導緻大量備援代碼的産生。

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

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

圖 4  MVP 蛋糕加工模型

第三階段:MVPs

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

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

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

圖 5  MVPs 蛋糕加工模型

第四階段:符合 DDD 的 VIPER

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

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

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

圖 6  VIPER 蛋糕加工模型

3基于 DDD 的短視訊架構優化

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

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

圖 7  視訊詳情頁抽象結構

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

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

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

4DDD 的選型與實踐選型背景

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

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

落地難題

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

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

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

重構效果

在基于 DDD 的架構重構初期,新聞用戶端選取了視訊詳情、視訊清單和圖集三個業務子產品使用 VIPER 重構,對 DDD 進行嘗試性探索。

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

新聞用戶端以半年為一個次元,統計出子產品重構前半年和重構後半年的系統故障率(主要指程式開發期間産生的問題),得出了如圖 9 所示的資訊。資料發現,越是業務變化很頻繁的子產品(如視訊),通過 DDD 獲得的子產品穩定性收益就越大。

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

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

5DDD 落地面面觀

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

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

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

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

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

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