本文書接上回《圖窮匕見-所有反DDD模式都是垃圾》,關注公眾號(老肖想當外語大佬)獲取資訊:
最新文章更新;
DDD框架原始碼(.NET、Java雙平臺);
加群暢聊,建模分析、技術實現交流;
影片和直播在B站。
背景
我在與開發者交流關於DDD的建模思路時,往往會遇到一個難題,就是不少經驗豐富的開發者,總是帶著技術的思維來理解業務,ta的大腦裡無法純粹地勾勒出一個邊界明確的代表業務實體的形象。其中最明顯的一個現象,就是習慣性地用關聯式資料庫中的“關係”,來對映業務模型之間的關係,一旦帶著“關係”來思考,那麼“邊界”就很難再有一席之地。而對於沒有太多“關聯式資料庫”經驗的開發者,反倒很容易理解什麼叫“邊界明確”。
關係型資料庫的三正規化
這裡我們先列出來關係型資料庫的三正規化定義:
- 第一正規化(1NF):確保每列的值都是原子性的,即每列的值不可再分。
- 第二正規化(2NF):在滿足第一正規化的基礎上,確保表中的每一列都完全依賴於主鍵,而不是部分依賴。
- 第三正規化(3NF):在滿足第二正規化的基礎上,確保表中的每一列都不依賴於非主鍵列,即消除傳遞依賴。
更通俗地說:
- 第一正規化(1NF):每個欄位只能儲存一個值,不能有重複的列。
- 第二正規化(2NF):在滿足第一正規化的基礎上,所有非主鍵欄位必須完全依賴於主鍵,而不能只依賴於主鍵的一部分。
- 第三正規化(3NF):在滿足第二正規化的基礎上,非主鍵欄位之間不能有依賴關係,必須直接依賴於主鍵。
而滿足三正規化的幾個關鍵目的:
- 消除資料冗餘:透過確保每個資料項在資料庫中只儲存一次,減少資料重複,節省儲存空間。
- 提高資料一致性:減少資料冗餘後,資料更新時只需修改一個地方,避免資料不一致的問題。
- 簡化資料維護:透過規範化設計,減少資料異常和複雜的維護操作,使資料庫結構更清晰,易於管理和擴充套件。
DDD思維 vs 三正規化思維
假設我們在設計一個包含使用者、角色、部門要素的系統,如果我們基於關聯式資料庫的思維,那麼設計出來的效果大機率類似下圖:
假如我們設計一個系統,完全滿足關係型資料庫的三正規化,那麼可以推論出來,基本上整個系統的所有實體之間都會直接或者間接地產生“關係”,畫出來的圖也會像一張蜘蛛網一樣錯綜複雜,這與DDD的理念正好相反。
因此,從這個角度來說“關係型資料庫三正規化的模式是一種反DDD模式”。
而如果我們使用DDD的思維,那麼“連線意味著它們就是一個整體”,要把邊界給明確出來,就需要消除連線,那麼設計出來的圖,大機率是下面的樣子:
當然你一定會有疑問,使用者和角色之間客觀上是存在關係的呀,你把連線移除,那麼這個關係就無法表達了,上面的圖並不能反映完整的設計,實際上,如果我們把“使用者聚合”的細節圖展開,你就會發現,我們把“關係”放置到了“使用者聚合內部”,需要注意的是這裡多出來了一個叫“使用者角色”的實體,它與“角色聚合”不是同一個概念。
到這裡,我想你也能發現DDD的思維有一些特徵:
-
聚合之間不連線(join),保持邊界明確
-
聚合內部滿足關係型資料庫三正規化
-
為了實現聚合之間不連線(join),會產生一些“冗餘”實體
代價與收益
假如我們使用DDD的模式來設計系統,代價是資料會一定程度上的“冗餘”,這些冗餘的實體資料,需要透過“事件驅動”的方式保持一致性,而收益是系統被明確地劃分成了多個邊界明確的“領域”,複雜度就像“雜物”一樣被收納在了一個個“收納箱”。
此外,關於“冗餘資料”,我們是從“關係型資料庫”的視角來看待的,如果用DDD的視角來看,前面模型圖中的“使用者角色”是“使用者聚合”客觀上存在的屬性,因此它不是冗餘。你看,我們一旦切換到DDD的視角,會發現事情變得很自然了。
現實世界到處是冗餘
如果上面的例子,你仍然覺得沒有說服力,那麼可以觀察一下現實世界的狀況,你會發現資訊冗餘的現象是普遍存在的,比如我的手機通訊錄,冗餘了你的名字和手機號,因為“我的通訊錄”就是客觀上“我”的屬性,現實世界到處是類似的例子。
結論
如果你期望儘快地走進DDD的世界,那麼在分析需求和設計模型時,一定要儘可能先忘記關聯式資料庫和三正規化的存在,哪怕你要應用三正規化,也應該僅僅將它應用在你的模型的內部,千萬不要用它來表達“聚合”之間的關係,因為,“聚合”是獨立的有明確邊界的。