1. 前言
在自己獨立開發一個項目的過程中,我發現了一些以往寫小 Demo 從來沒有遇到過的問題。
最近在獨立制作一個全棧的通知管理平臺。一開始我沒有考慮太多,直接根據頭腦中零星的想法就開擼后端數據庫 model 和 API,用的是學了半成品的 MongoDb。
結果就是寫到后面在遇到復雜的數據庫依賴關系時,我感到崩潰。這才想起指導老師給我發了一篇計算機的論文,我便開始虛心研究。
做一個項目要經過這些過程:
- 系統分析
- 可行性分析
- 用戶需求分析
- 整體功能模塊分析
- 技術分析
- 系統流程分析
- 系統設計
- 系統功能模塊設計
- 系統結構設計
- 數據庫概念設計
- 數據庫設計
- 數據庫表設計
- 系統實現
- 功能模塊的實現
- API 接口功能的實現
- 系統測試
- 黑盒和白盒測試
- 測試環境與條件
- 功能測試
敲代碼的時候思維很局限,總覺得完成了某一個單個功能就算成功。真到讓我獨立設計一個項目,我還真就難住了。這里就來講講我第一個遇到的問題,數據庫怎么設計?
本文用到的工具:
eraser.io
2. 構建實體
打開一額eraser.io
文件,在左側寫入所有的實體Entity
,例如:
- 用戶
- 班級
- 通知
然后在canvas
中添加一個Diagram as Code > Entity Relationship
也就是E-R
圖。
? 一個最佳實踐:總是從用戶表
User-Table
開始著手你的 E-R 圖設計。
這是因為,一切都是為了用戶用戶就是上帝。
從用戶表開始,并從用戶的注冊開始。
我們的用戶表可以是這樣:
User {id string pkusername string uniqueemail stringbio string
}
強調一點:業務邏輯永遠不要成為主鍵,例如這里除了
id
外所有的屬性皆是如此。
也許你不需要一個createdAt
鍵,但一個很中肯的建議是添加它,總有一天你會需要它的,當你需要它的時候可不能后悔。
User {id string pkusername string uniqueemail stringbio stringcreatedAt timestamp
}
同樣的方法,添加班級、通知,完成后如下圖所示:
3. 構建關系
關系分為多種:
- 一對一
- 一對多
- 多對多
這里用戶和班級之間存在多對多的關系,構建關系時我們也總遵循從User
表開始的原則,正如之前提到的,用戶是整個產品的核心。
為了加深對關系的了解,這里舉個用戶發推文的例子:一個用戶能發多個推文,每一條推文只有一個用戶作為作者。這是一對多的關系,一個用戶對應多個推文,但每一條推文只能對應一個用戶。
在這里,假如我希望一個班級對應多條通知,在eraser.io
中可以使用這樣的語法來表示:
# 一對多
Classes.id < Notifies.classId
這里用到的關系符號是<
,同樣的還有一對一和多對多,分別用-
和<>
符號表示數量關系。
觀察上面的代碼你會發現一個問題:通知實體并沒有classId
這個鍵。
這就是我們需要創建的,這里classId
是一個外鍵,表示引用了一個其他表的主鍵。
我們修改通知Entity
的結構:
Notify {id string pktitle stringcontent stringcreatedAt timestampclassId string pk
}Class.id < Notify.classId
修改后大概是這樣:
這里我們再添加一個Media
實體:
Media {id string pkfileUrl stringtype enumcreatedAt timestamp
}
很顯然,一個班級對應多條通知,一條通知可能對應了多個媒體,所以媒體也需要一個類似的外鍵來唯一的引用一個它所對應的通知。
你有沒有想過為什么反過來不行,為什么不是通知的外鍵引用到媒體呢?
很顯然,通知對應多個媒體,一條外鍵是不夠的,而媒體只對應一個通知,一個外鍵就剛好。
添加完成后我們再來加上顏色和圖標就是這個效果:
關鍵其實還有語義化的功能,在看到這個外鍵后就知道通知與某個班級有關,媒體與某條通知相關。
在這種情況下外鍵是很有意義的。
如果我們的用戶能夠在每一條通知下進行評論,就需要一個Comments
實體。很明顯他用外鍵和唯一的用戶關聯表示該用戶的評論。
在這里,用戶和評論是一對多的關系,通知和評論也是一對多的關系,所以你能看到在評論的身上有兩條外鍵分別拉到了用戶和通知身上。
根據同樣的一對多的原理,我們來制造一個like
,也就是用戶對評論的點贊:
4. 多對多
根據上面的例子我們不難發現,要處理一對一、一對多的關系都能直接使用外鍵來處理。
但是多對多呢?
用戶的好友是一個多對多的關系,用戶可以有多個好友,很多人也可以加這個用戶作為好友。
我們的班級和用戶之間也是這樣的關系,班級可以有很多成員而成員也能加入很多班級。
對于多對多的關系我們一般新建一個表,例如,用戶好友的關系。
這里比較令人困惑,要仔細看看。
這張表實際上就是單獨跟蹤了誰關注了誰,有兩個字段:關注follow
,粉絲follower
。
如果要查詢用戶的粉絲可以用select * from Friends where follow = user_id
就能查詢到用戶的所有粉絲。
如果要查詢用戶的關注列表就是:select * from Friends where follower = user_id
。
5. 總結
關于數據庫的設計關鍵是將所有實體抽象出來,并理清楚實體之間的關系。
本次實驗🧪的鏈接:https://app.eraser.io/workspace/1GT4Nb82OR4LTYIuOmkT