EnriqueMark
設計模式與架構

資料導向型的模組設計

2026-04 繁體中文

我現在發現,與其將各種狀態都耦合在程式碼內部,不如用資料庫做統一的、共用的持久狀態管理。這樣各個功能模組彼此之間就可以去耦合,而以模組——抽象的單位積木——為存在彼此只關注資料,而非在內部進行複雜的狀態管理和互動。

這種做法在跨模組共享狀態的時候尤其好用,各個模組只需要專注於自己的功能實現,而不必考慮外部的狀態。當狀態需要更新時,直接朝專門的狀態管理模組傳送請求即可,狀態的獲取和更改都可以集中管理。在專案初期,可以自行修改資料庫欄位的前提下,這樣的資料導向型設計可以讓所有功能模組的狀態同步省下很多功夫。持久化的狀態儲存當然也可以存在記憶體或磁碟中,但這樣就會吃不到資料庫定期備份和統一管理的優勢。

當然,這個做法不是沒有代價,IO會增加,對資料庫的壓力也會增加。但對於小規模、並發數不高的內部軟體來説,這樣的設計完全夠用了。除非有明確的即時性或IO不便的離線場合,否則用資料庫來存狀態是個蠻不錯的設計。

除此之外,以上方案實際上並未徹底的將耦合去除——這有點像是熵的增減,並非封閉系統的功能程式碼將自身的熵排放了出去,轉移給了資料庫。那也就意味著資料庫查承擔了全部的狀態管理功能,一旦發生變動,這裡的改變是連鎖的。比如增加或者減少欄位,導致整體都出問題。這裡的連鎖依賴實際上和組合和繼承裡面説過的類的問題差不多,當父類更改,子類依賴的某個關鍵要素沒了,直接報錯。而且這是在構建父類時不可預知的。同樣,資料庫會被誰、在什麼時候修改,也是我作為程式碼構建者不可預知的,甚至哪天我自己改了什麼東西都不好説。

因此,為了增強整體的穩健性,在資料庫和功能模組中間增加一個抽象的介面更好,讓功能模組對介面負責,而不是對資料庫直接負責。然後再用專門的工具去維護這個介面,將資料庫的內容裝填好——這裡有點像是工廠模式的變體,只是從處理物件變成了處理資料。底層的積木只對介面負責在去耦合的思想上和DI是一致的,即控制反轉,資料怎麼處置被外放,而功能模組自身只關注介面或是說結構。就像上述所説,這裡的問題幾乎和繼承一模一樣,因此解決方案也是殊途同歸。

再者,依賴資料庫的另一個問題就是競態條件,併發修改的問題是一切外部狀態都可能出現的,如果不加鎖,這裡就會導致依賴上的廣播錯誤:A收到的資訊是A,但其實B已經把它改成B了。資料庫更適合存的是原子化和持久化的狀態,而可能快速變更的短期狀態,可能不適合放在上面。