編寫高質量可維護的代碼:數據建模

這是第 67 篇不摻水的原創,想獲取更多原創好文,請搜索公衆號關注咱們吧~ 本文首發於政採雲前端博客:編寫高質量可維護的代碼:邏輯判斷javascript

什麼是數據建模

數據建模是一種用於定義和分析數據的要求和其須要的相應支持的信息系統的過程。前端

隨着前端頁面的交互變得更加細膩複雜,本來存放於服務端的狀態放置在了前端,相似 flux、redux、mobx、dva、rematch、vuex 的狀態管理庫也成了每一個項目的標配。vue

由於分層理念的普及,前端工程師們須要把更多精力放在數據管理上,數據建模也成了基本功。java

而建模的產物是數據模型,數據模型是定義數據如何輸入和輸出的一種模型,其主要做用是爲信息系統提供數據的定義和格式。vuex

數據模型包括數據結構、數據操做、數據完整性約束條件這三要素。redux

簡單理解就是數據模型提供了一個「模具」,數據按照預先的設計和約束進行放置。後端

三要素

數據完整性約束條件

好的數據結構必需要有約束,例如描述同一個狀態的字段有時候是字符串,有時候是數字,這樣的話就容易形成預期以外的狀況。添加約束能夠最大限度保障這份數據是乾淨整齊的;api

// status 是字符串的時候不經過
if (status === 1) {
  ...
}
複製代碼
// 按照必定約束
model.define(
  'user',
  {
    name: { 
      field: 'name',
      type: STRING(64),
      allowNull: false, 
      comment: '姓名',
	},
    sex: {
	  field: 'sex',
      type: INTEGER(1),
      allowNull: false,
      comment: '性別',
    }
  }
);
複製代碼

數據結構

描述模型自己的性質以外,還需經過某些字段表達模型(表)和模型之間的關聯;性能優化

數據操做

在數據結構上對數據或者數據之間的關聯關係的操做。markdown

領域驅動設計

在圍繞着數據模型進行應用開發的時候,咱們會思考如何進行建模呢?

實際上,軟件開發行業中已經積累了一些方法論,例如**領域驅動設計(DDD)**就被普遍採用。

在進行軟件開發前,一般須要先進行業務知識梳理,然後到達軟件設計的層面,最後纔是開發。而在業務知識梳理的過程當中,咱們必然會造成某個領域知識。根據領域知識來一步步驅動軟件設計,就是領域驅動設計的基本概念。簡單來講領域驅動設計就是關注精簡的業務模型及實現的匹配

分層架構

按照領域驅動設計的分層架構能夠將應用進行分層

  • UI 層:負責向用戶展示信息以及解釋用戶命令。
  • 應用層:用來協調應用的活動。它不包含業務邏輯;它不保留業務對象的狀態;但它保有應用任務的進度狀態。
  • 領域層:業務軟件的核心所在。在這裏保留業務對象的狀態,對業務對象和它們狀態的持久化被委託給了基礎設施層。
  • 基礎設施層:爲其餘層的支撐庫存在。它提供了層間的通訊,實現對業務對象的持久化,包含對用戶界面層的支撐庫等做用。

按照這個分層,越往左邊代碼變更越頻繁。隨着業務複雜,應用層和領域層的邊界變得模糊,領域之間也容易交錯在一塊兒。

良好的設計應該避免層與層之間產生過多依賴,若是代碼沒有被清晰地隔離到某層中,它會迅即變得混亂和難以維護。

經過分層架構和高內聚低耦合的設計思想,最終實現系統與需求有較好的一致性,在業務迭代中快速響應需求變動。

實體

實體在領域模型中是必需的對象,而且它們應該在建模過程開始時就被考慮。例如要實現一個「貓」的概念,咱們可能會去創造一個 Cat 的類,這個 Cat 可能包含名稱、性別、品種等屬性,可是這些屬性都不足以區分這隻貓,因此咱們須要建立一個惟一不重複的 ID 來區分他們,也就區分實體的標識符。

建立 ID 的方式有不少種,它能夠是主鍵、能夠來自外部、也能夠由系統本身產生,但它必須符合模型中的身份差異。

值對象

用來描述領域的特殊方面,且沒有標識符的一個對象,叫作值對象。例如畫布上的一個點 Customer 會跟姓名、省份、城市、區、街道相關。最好是將地址分離出來,保留對地址的引用,由於它們都是同一個址屬性。

服務

你能夠簡單地將行爲理解成一種服務。例如你去商店購買商品,你的朋友也能夠去購買商品。若是將購買這個能力做爲一個屬性放在 Person 這個實體裏顯然有點不對勁,由於「去購買」這個功能並不屬於你和你的朋友(實體或者值對象),同時去購買也可能涉及到商品對象。

保證服務的單一性和隔離很是重要,注意區分領域服務和應用服務。決定一個服務所應歸屬的層是很是困難的事情,咱們在設計階段創建模型時,須要確保領域層從其餘層中隔離開來。

模塊

模塊是一種被用來做爲組織相關概念和任務以便下降複雜性的方法,一般狀況下由功能或者邏輯上屬於一體的元素構成,以保證高內聚,同時經過接口的形式暴露給第三方以下降模塊之間的耦合。

聚合

聚合是針對數據變化能夠考慮成一個單元的一組相關對象。聚合基於(有且僅有)一個實體(根),聚合經過這個根被外部訪問,它能夠引用任意聚合或者被其餘聚合引用。如下是一個簡單的聚合例子:客戶做爲聚合的根,其餘信息都是客戶內部的,若是須要地址則將地址的拷貝傳遞出去( Javascript 中特別須要注意)。

工廠

工廠用來封裝對象建立所必需的知識,它們對建立聚合特別有用。工廠方法是一個對象的方法,包含並隱藏了建立其餘對象的必要知識。

資源庫

資源庫做爲一個全局可訪問對象的存儲點而存在。它是一個獨立的層,介於領域層與數據映射層(數據訪問層)之間。它的存在讓領域層感受不到數據訪問層的存在,它提供一個相似集合的接口,提供給領域層進行領域對象的訪問。

前端的數據建模

數據建模和後端的工做關聯較爲緊密,前端的數據模型更可能是依賴後端傳遞的數據傳輸對象(DTO)進行二次構建。不管二次構建是發生在服務端聚合階段仍是用戶端 AJAX 請求完成階段,前端都須要參與必定的數據清洗,並應用到前端的數據模型之上。

領域劃分

如今你能夠開始嘗試劃分你應用內的業務領域。以一個商城爲例子,它可能會包括用戶、商品、貨架、訂單、結算、帳戶等內容。

每個業務領域均可以致少拆分紅一個領域,按照業務領域來組織代碼,例如在交易領域中按照如下目錄結構劃分:

src
  modules 
    ...
    trading             # 交易領域
      components/         # 組件
      models/             # models
      pages/              # 頁面
      redux/              # redux
      services/           # 交易模塊相關api
      styles/             # 交易模塊樣式
      index.ts
  ...
複製代碼

概念模型

數據建模的前提是對業務的充分理解,充分理解業務至關於在更高的視角去看待業務之間的關係,有利於更好地完成模型建設。

嘗試回想一下你所維護的業務(應用)場景,你是否清晰業務場景和業務對象之間的關係以及具體交互?

使用思惟導圖梳理出概念模型,這個階段能夠不用嚴格遵照三要素,目標清晰表達現實世界就行。

定義模型

定義模型能夠依據概念模型,補充細節和關聯關係,例如簡單定義一個營銷商品:

以上展現了商場貨架上劃分的一塊活動區域,規則是滿 XX 減 XX ,再將參與該活動的商品在區域內進行上架。

下降複雜度

在大部分狀況下,特別是展現邏輯這塊,前端不該該是重邏輯的。

以商品爲例,不一樣商品的營銷類型背後隱藏着複雜的價格體系,儘管是同一種營銷類型,商品在不一樣的狀態展現的價格也不必定相同。你能夠想象這背後的字段,以及計算規則。

假如後端把這些字段、各類price和規則一股腦拋給你,先不談先後端對稱問題,光挑字段都能讓你目瞪狗呆。

遇到相似狀況更好的辦法是:儘可能避免在前端(用戶端)去處理複雜的業務判斷,在聚合層或者讓後端同窗給你處理好這些展現邏輯。

特別是在 C 端場景下,數據直出顯得更加劇要,同時前端同窗也有更多時間去作性能優化(早點下班不香麼?)。

另一個好處是假如出現展現問題,你只要肯定讀取的字段正確,剩下的僅需一我的排查就夠了;

// Bad
const switchPrice = product => {
  switch(product.status) {
    case 0:
    	return product.priceA;
    case 1:
    	return product.priceB;
    case 2:
    	return product.priceB;
    default:
    	return  product.priceBase;
  }
}
<Price value={switchPrice(product)}/>
     
// Good
<Price value={product.price}/>
複製代碼

邏輯分層

設計上須要區分應用邏輯(業務邏輯)和展現邏輯。應用層注重對領域層的調度,是業務邏輯的實現,展現層專一渲染和交互動做。

在一個大型項目中,同一個 Model 可能被多處引用,你很難肯定誰最終會對同一份數據進行怎樣的操做。

同時 Model 中僅保留數據源的抽象結構,而不修改數據源的內容。

// 在視圖層只作展現邏輯處理
// 組件A
...
<>
	<span>日期:{format(res.date, 'YYYY-MM-DD')}</span>
</>

// 組件B
...
<>
	<span>日期:{format(res.date, 'YYYY-MM')}</span>
</>
複製代碼

統一字段

在設計模型的時候,儘量與後端保持統一字段。好比某些表單場景在回顯和提交的時候要多一層轉換,後期維護會帶來多一層心智負擔。在先後端分離的開發模式下,不必定能保證後端會先給出字段,個人習慣是標記字段,等聯調的時候全局替換一下就好了。

簡化字段、明確語義、改變不合理的先後端交互是作好數據建模的基礎,不然你將花費大量時間去理解這些字段背後的含義和計算規則。

小結

沒有一個十全十美的數據模型能夠適用任何需求場景,模型的落地須要綜合考慮業務實際場景和技術選型。在構建模型的過程當中,鍛鍊系統性思考能力、從更高的視角看待業務,才能創造出一個生命週期更長的模型。

推薦閱讀

編寫高質量可維護的代碼:優化邏輯判斷

前端文檔站點搭建方案

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 50 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com

相關文章
相關標籤/搜索