閒談軟體設計:Database Driven Design?

閱讀時間約 8 分鐘


今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,試著回想一下,平常在開發一個新功能時,是不是有以下症狀:

  • 總是先從 database schema 開始設計,連 use case 都還沒討論清楚,就已經先有 schema 了
  • 討論 use case 時,都是針對 database schema 的 CRUD,連 API 看起來都像是某個 table 的 CRUD
  • 除了 service 物件,物件都只有資料,沒有 getter/setter 以外的邏輯 (貧血模型)
  • 不太考慮 boundary context,把不同服務所需要的資訊都放在一個大表中,於是形成一個超大物件。


如果有以上症狀,那有很高的機率是 Database Driven Design,這邊要先聲明,這種設計方式不見得是不好的,如果是簡單的應用程式,只需 CRUD 卻硬要套複雜的 Clean Architecture 或是 Domain Driven Design 有時反而沒有必要,使用某些現成的框架,快速地完成 CRUD 的開發是可以接受的。

在討論 Database Driven Design 可能遇到的問題前,先閒聊一下個人過去的學習經驗吧!大二算是正式接觸物件導向設計,在那之前都是自學,從各種不同的管道東拼西湊關於物件的概念,可能是學校課程太過偏於語言怎麼實作物件導向的特性,於是那門課上完後,除封裝、繼承與多型,大多記得的是語言特性 (C++),像是 class、pointer、overloading、pure virtual function,實際上怎麼用這些東西設計出真正的物件導向系統,倒是沒什麼概念。

大三學資料庫系統,從正規化開始,什麼第一正規化、第二正規化到第三正規化,ER 模型,一對多、多對多、reference key 等等,最後專題用 PHP 寫個組裝電腦用的購物車 (跟原價屋有點像,但會幫忙過濾掉不相容的規格,例如選 Intel 的 CPU 會過濾掉 AMD 晶片組的主機板之類的),那時就真的是 Database Driven Design 了,物件導向完全沒用上,但還是弄出個可以動的系統。

碩一的物件導向設計課,學 design pattern,整個學期分七次作業慢慢寫出一個 class diagram 編輯器,加上作業要求用上特定的 design pattern,慢慢開始對物件導向有感覺了。接著修 OOAD,從 use case 開始分析模型,用在自己的論文上,開發 visual language 的編輯器,算是比較熟悉物件導向設計了。這兩個 editor 用的是自定義的檔案結構,沒有用到資料庫,因此完全不會有被 schema 綁架的問題。

好,回到正題,會寫這篇主要是進到業界後,開發系統時一定會碰上資料庫,只要碰上資料庫,總是覺得哪裡怪怪的,物件之間的關聯是為了 ORM 寫的、核心的模型卻依賴 ORM 框架,DAO 或是 repository 充斥著各種邏輯,又或者出現上述的幾個症狀。

首先,OO 模型和 ER 模型是不一樣的,OO 有繼承、實作、composite、aggregate 等不同關係,不是每一個都能一對一的對應到 ER 模型,ER 模型為了處理多對多的關係,會有 relation table,但 OO 不需要,ER 需要 reference key,或是因應 multi-tenancy 設計所加的 columns,但這些資料作為物件的 field 卻不一定有用。例如,一個多商家使用的電商系統,在訂單資料表上一定會有像是 merchant_id 的 column,但訂單物件卻不一定要有 merchantId 的 filed。


Figure 1 - ER Model

Figure 1 - ER Model


Figure 2 - Class Diagram

Figure 2 - Class Diagram


從 Figure 1 和 Figure 2 可以看到兩者的差異,事實上,Merchant 物件需不需要有一個 orders 的陣列,代表某個商家的所有訂單,或是 Order 有個 merchant 指向所屬的商家?若是使用 ORM 框架,框架會建議你要加,但這是因為 ORM 框架需要而不是你的 Domain Model 需要。

因此,從 database schema 出發,往往會影響到 OO 的模型設計,但有趣的是,自己的經驗是,先從 OO 模型出發,反而不太會影響到 database schema 的設計。

再來,個人覺得最大的問題是,都沒有再討論行為,都只討論狀態,OO 的重點是把狀態封裝,透過有限的行為操作物件,OO 的模型怎麼可能會沒有行為?在閒談軟體設計:State 與語言中,在建立 SlotMachine 介面時,都是以自動販賣機能提供什麼行為開始,內部的狀態反倒是其次。這邊提供兩種不同的實作範例,第一種用狀態的角度出發進行設計 (解說用,某些內容省略,不能編譯):


第二種則是從可以有什麼操作的角度出發進行設計:


不知道大家比較喜歡那個版本?我覺得寫程式沒有標準答案,所以僅提供幾個觀點讓大家思考:

  • 當要呼叫 API 接受訂單時,是 PUT /orders/{merchantId}/{orderId}/state 比較直覺?還是 PUT /orders/{merchantId}/{orderId}/accepted 比較直覺?
  • UpdateOrderStateRequest 也把取消訂單需要的內容也放進去時,說明文件好寫嗎?
  • 能不能接單的邏輯,到底該歸 OrderManager 還是 Order
  • 比較喜歡 switch...case 的直接了當,還是清楚語意的 method name?
  • 當要修改接受訂單的邏輯時,哪個版本比較容易知道修改何處?
  • 修改完後,那個版本需要比較多的回歸測試?取消訂單要測試嗎?
  • Order 還是沒有邏輯的貧血模型嗎?
  • 物件一定需要提供 setter 嗎?

基於上述的觀點,我個人喜歡第二個版本。當然,這是隱含著使用 OO 作為設計的基礎假設,若是使用 functional programming 或是 Data-oriented programming,可能會有完全不同的想法。貧血模型不見得不好,就看團隊的喜好,會變成貧血模型,也不完全一定是因為 Database Driven Design,但自己的觀察是 Database Driven Design 確實比較容易變成貧血模型。

關於 boundary context 的症狀,單從 single responsibility 的角度來看 (參閱閒談軟體設計:Single Responsibility),一個物件承擔多種服務的設定是合適的嗎?

一個電商平台會提供多種服務,下單 (order)、支付 (payment)、物流 (logistics),不同的服務都有各自的設定,那大家會比較喜歡 Figure 3 中的 SuperMerchant 還是 SimpleMerchant 呢?


Figure 3 - SuperMerchant vs. SimpleMerchant

Figure 3 - SuperMerchant vs. SimpleMerchant


這一樣沒有標準答案,同樣提供幾個觀點去思考:

  • 假設設定的屬性之間有關連性,有邏輯需要驗證關聯性,例如若開啟信用卡,就必須開啟至少一種卡別,那邏輯放在 SuperMerchant 或是個別的物件中,哪個比較直覺?
  • 在實作 OrderService 時,取得一個 SuperMerchant 或是 OrderSetting,哪個比較直覺?當然也有可能 SimpleMerchantOrderSetting 都需要的情況。
  • 微服務流行了好一陣子,假設下單 (order)、支付 (payment)、物流 (logistics) 都拆分成微服務,在資料庫分離的情況下,哪個比較容易?

雖然說讓 Database Driven Design 背這個鍋其實有點尷尬,只是跟貧血模型的症狀很像,自己的觀察,Database Driven Design 好像特別容易會變成這樣。

好啦,來總結一下,如果正在嘗試用 DDD 開發應用程式,也許可以思考一下,是哪一種 DDD?有以上幾個症狀嗎?

52會員
102內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
閒談軟體設計:Deploy on Friday
閱讀時間約 7 分鐘
閒談軟體設計:State 與語言
閱讀時間約 10 分鐘
閒談軟體設計:多種 work 類型
閱讀時間約 16 分鐘
閒談軟體設計:再來一碗
閱讀時間約 8 分鐘
你可能也想看
【閒談雜記|01】記帳習慣,從觀察、養成到分析一、從小觀察 從上電腦課學會用 Excel 這個文書處理軟體時開始注意到爸爸有用 Excel 記帳的習慣(簡單記錄,有時候會忘記金額所以不一定確實),每次爸爸發現我在觀察他記帳的時候一定會對我說:「固定支出越少每個月才能存到更多錢。」以及「不要小看雜支,常常花最多花得最莫名其妙的就是雜支。」 高
Thumbnail
avatar
GTalent
2024-02-27
閒談 | 缺乏髮禁、體罰、紀律才釀禍?關心時事很好,也別忘了穩定情緒,釐清事件與其他議題的界線看到有人因為這次的事情在說這是因為沒有校園沒有髮禁、禁止體罰、延後到校時間導致學生逐漸沒有紀律,覺得自己什麼都能做......我第一個想到的是我爸說他高中時畢業典禮教官只要走得慢一點一定會被憤怒的學生蓋布袋拖去打。發生這樣的事情當然得檢討,但希望不是以一種意氣用事、情緒主導的心態......
Thumbnail
avatar
杏櫻子
2023-12-29
閒談雜記 一「一群人所決定的,就是對的嗎?」 這是民主的盲點也是缺點
Thumbnail
avatar
文字三分之一者
2023-12-27
從我的孕期怪事談起,閒聊人生的軟弱時刻。最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
avatar
兩寶媽的生活滋味與成長
2022-11-06
閒談-社區大樓觀景第一排公設圖書室,我們都要試著跟鄰居友好相處不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
avatar
紗綺創作小屋
2022-08-16
閒談 那天我問隊友,怎樣才算是一部小說呢?按字數計算嗎? 他說:「故事內起承轉合都有,就算」 所以我大膽地按著他的標準,將自己寫過的故事,粗略整理出一個明細。
Thumbnail
avatar
于小晨
2020-11-18
閒談纏論與李彪 纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
avatar
釋深明
2020-07-30
閒談末代皇帝電影:婚姻篇婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
avatar
金哲毅
2020-05-29
閒談末代皇帝電影:童年篇上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
avatar
金哲毅
2020-05-29
閒談末代皇帝電影:登基篇近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。
Thumbnail
avatar
金哲毅
2020-05-29