作者 Kid 螞蟻金服·資料體驗技術團隊
隨著我們解決的場景越來越專業化和複雜化,大型SPA應用的流行,前端承擔的職責越來越多。程式碼的質量和系統的完整性越來越難把握。很容易導致迭代著迭代著發現程式碼改不動了。最後只能新起爐灶,重新開發。歸根到底在於複雜度的失控
,本文會嘗試分析其中的問題以及從前端如何應用領域模型
開發的角度給出一些建議。
為什麼迭代越來越難
我們的系統架構精心設計過,按照標準的系統分層來管理複雜度。邏輯層
,展示層
,資料層
。每一層都精心設計。我們抽象出獨立的類來放通用邏輯。對著程式碼不斷地重構,將有複用能力的點進行抽象。為什麼需求的變動還是能經常摧毀我們的設計呢。
原因在於:
- 問題域本身錯綜複雜
- 技術模型與領域模型不匹配
- 知識的丟失
問題域本身錯綜複雜
軟體本身是為了管理複雜度,我們現在面對的問題域錯綜複雜。為了建立真正好用的軟體,開發者必須有一整套與之相關的知識體系。為此要知道的知識的廣度讓人生畏。一旦我們不能理解問題域,我們就沒法做到控制問題域的複雜性。
當複雜性失去控制的時候,開發人員就無法理解軟體。當領域的複雜度沒有得到解決時,基礎技術再好的構思也無濟於事。
技術模型與領域模型不匹配
上面我所描述的設計都是技術層面
的設計。我們很容易抽象出一個獨立的類來放通用邏輯,可是很難給它業務上的定義
!這個通用類只有技術維度上的通用。
問題在於技術維度上的通用很容易被業務摧毀
。需求上的變動或者膨脹,技術維度的通用很容易被摧毀。舉個例子,頁面變化了,某個檢視元件被複用了,他可能就要被提取到上層的common目錄。也就是技術模型立刻需要重新設計,然後就是重構,重構成工程師喜歡的簡潔的樣子。然後需求再變化,再重構....陷入了怪圈。
並且這個階段我們很難保證重構的高效進行,有個理論叫破窗戶
理論。一幢年老的大樓,一旦第一扇窗戶破了,就會立刻給人一種年久失修,腐敗的跡象。就像是一輛車,一旦第一個車窗壞了,裡面很快就會遭到破壞。
裡面的根本原因就是我們設計的技術模型
與領域模型
不匹配。於是每次需求的改動,對映到技術模型的改動可能就是極大的工作量。甚至根本改不動,在業務壓力很大的時候,我們只能告訴產品經理,這個可以做,但是我們需要2個月。結局很可能就是需求方的妥協,犧牲使用者的利益。導致產品越來越難用。
知識的丟失
任何專案都會丟失知識,外包出去的系統可能只交回了程式碼,知識沒有傳遞回來。離職了,轉崗了,一旦出於某種原因人們沒有口頭傳遞知識,知識就丟失了。
丟失的知識也會導致系統越來越難維護,新同學不知道對於通用邏輯的改動會發生什麼事情,程式碼最終變成了“石油坑”,越陷越深,最終無法自拔。
總結
以上這三個問題歸根到底,就是我們沒有在前端程式碼裡把我們業務描述清楚。我們很多情況下是檢視驅動
,而不是業務驅動
。很多時候只關心頁面長什麼樣子,發了什麼請求拿了什麼資料。於是在業務概念上每個人理解的深度都不同。解這個問題可能採用新的領域驅動設計的開發方式會比較合適。
領域驅動設計
領域模型是跨前端-後端-產品-設計的統一的語言。統一的語言既可以形成統一的理解,也可以促進領域模型的不斷精化。也能迫使開發人員學習重要的業務原理,而不是機械的功能開發。產品經理也會不斷提煉知識,昇華自身理解。如果沒有一個統一的,有共識的結構化的模型,一定會讓專案很快的僵化,最後變成維護代價極高的遺留系統。
前端應用領域模型
領域模型很多情況下都是由後端同學建立的,前端同學如何指導開發呢?我對於我們系統的演進過程進行了總結,希望能給大家一些靈感:
- 理解後端領域模型
- 建立前端領域模型
- 分離領域層
- 主導介面約定
- 開發中注意業務含義
- 實時同步
理解後端領域模型
我們在進行前端設計之前要搞懂我們要開發的業務含義。除了自己理解建立模型之外我們可以尋求後端同學的幫助。拿到他們的領域模型
,弄清他們的模組劃分。他們其實是業務邏輯的最終實現方,我們可以直接借鑑他們的模型,這樣也可以保證前後端對於業務模型的理解一致。
建立前端領域模型
我們要繪製出前端的領域模型圖,這個圖與後端的領域模型圖一致程度很高,但絕不是一樣
的。通常比後端模型簡單。比如頁面需要進行一項任務的配置,這個配置在後端模型裡可能會被解釋的相當複雜(會被拿去做一些同環比之類的複雜操作),但是在我們前端模型裡,他的業務功能就是簡單的任務配置而已~
分離出領域層
如圖,這一點是必須落到程式碼上的核心
!!一定要根據對應的前端領域模型在程式碼中分離出單獨的領域層。模型必須與實現緊密結合,一定要指導設計,並落到程式碼上成為最終產品的一部分
。
還需要強調的是領域層的建設一定不是兩個頁面同時發了個請求,於是把這個請求抽出來,給與一個領域的名字。他一定被提前建立好
的。在開始進行前端設計之前就被設計出來的一層。
我們要將所有頁面元件與模組內的業務行為都抽離出來,放在合適的領域模組中。只要是業務行為,一定有一個領域模組可以落
。如果不行就是領域模型設計的不合理。
要明白,驅動領域層分離的目的並不是頁面被複用,這一點在思想上
一定要轉化過來。領域層並不是因為被多個地方複用而被抽離。它被抽離的原因是:
- 領域層是
穩定
的(頁面以及與頁面繫結的模組都是不穩定的) - 領域層是
解耦
的(頁面是會耦合的,頁面的資料會來自多個介面,多個領域) - 領域層具有
極高複雜度
,值得單獨管理(view層處理頁面渲染以及頁面邏輯控制,複雜度已經夠高,領域層解耦可以輕view層。view層儘可能輕量
是我們架構師cnfi主推的思路) - 領域層
以層為單位
是可以被複用
的(你的程式碼可能會拋棄某個技術體系,從vue轉成react,或者可能會推出一個移動版,在這些情況下,領域層這一層都是可以直接複用) - 為了領域模型的持續
衍進
(模型存在的目的是讓人們聚焦,聚焦的好處是加強了前端團隊對於業務的理解,思考業務的過程才能讓業務前進)
這裡想引用下我們leader導演的話說,我們的競爭力絕不僅僅只是前端,我們的競爭力在於我們是資料部門的前端,在於我們對於資料業務的理解。只有對於業務有深層次的理解,才能將系統帶到正確的軌道上來。
主導介面約定階段
介面約定儘量由前端主導
,畢竟介面是給前端使用,前端來設計介面比較合理。而且在約定的過程中,前端同學又多了一次熟悉後端是如何分模組的機會。必須通過看後端同學的資料庫和整體的設計文件來約定介面路徑和變數名稱,也能夠讓前後端同學對於系統的各部分的命名一致。
在開發中注意業務含義
我們在類,方法,模組命名時要直指業務核心
,保持與領域模型的一致。比如一條員工資料記錄可能會被翻譯成 inputRec或者employeeData, inputRec其實就是一個計算機思維的術語,而employeeData才是直指問題領域。這個錯誤其實很容易犯,我們開發的程式設計師思維根深蒂固
。
實時同步
確保團隊內部所有同學都要熟悉系統的模型。尤其是對於要熟悉並修改程式碼的新同學,先向他們分享我們系統的領域模型之後再介紹技術架構。工作開展的重點的不同會導致程式設計世界觀的不同。這樣子會讓新同學養成習慣,在進行技術決斷之前先判斷是否符合現有的模型。不斷的思考模型,才能夠幫助我們業務成長。
總結
領域驅動設計對於降低專案的複雜度上是明顯效果的,而且將前端的程式碼業務邏輯和檢視邏輯解耦。可以做到業務邏輯層的複用。加深了前端同學對於業務的理解和思考,可以促進業務發展。這種分層思想並不侷限在某個框架下,建議大家嘗試下~
對我們團隊感興趣的可以關注專欄,關注github或者傳送簡歷至'tao.qit####alibaba-inc.com'.replace('####', '@'),歡迎有志之士加入~