在企業級應用的架構設計中,貧血模型和充血模型一直是架構師們爭論的熱點話題。兩者背后分別代表著“事務腳本模式”和“領域模型模式”兩種截然不同的設計思想。而理解這兩者的差異,有助于開發者根據實際業務場景做出更合理的架構決策。
貧血模型:事務腳本模式的延續
貧血模型(Anemic Domain Model)最早大規模應用于 EJB2 時代,后由 Spring 發揚光大。其核心思想是將狀態與行為解耦:
- 狀態:由一組僅包含屬性、getter 和 setter 的對象承載,這類對象通常被稱為 VO(Value Object)。
- 行為:由一組服務類(如 Service、Manager、Logic)來承載,負責執行所有的業務邏輯。
這種模式本質上延續了面向過程編程的思想。正如 Spring 創始人 Rod Johnson 所言,Spring 在某種程度上只是 EJB2 事務腳本模式的現代延續。
看似合理,實則反模式
乍一看,貧血模型在命名上貼近業務領域,各種對象之間結構清晰,看似具備良好的領域建模能力。但深入觀察便會發現,這些“領域對象”大多是空殼,缺乏任何實際行為,僅僅是一堆字段加上 getter/setter 方法。
業務邏輯被統一放入 Service 層中,使得原本面向對象的架構逐漸滑向面向過程。這不僅違背了面向對象設計“將狀態與行為綁定”的核心原則,也導致了如下問題:
- 設計成本與收益不匹配:雖然引入了領域模型的架構和成本(如 ORM 映射),但卻沒有享受到領域建模帶來的好處。
- 失去封裝優勢:邏輯分散在大量服務方法中,難以復用和擴展。
- 邏輯難以歸屬:領域邏輯到底屬于哪個 Service,往往眾說紛紜,導致職責混亂。
Martin Fowler 曾在《企業應用架構模式》中指出,領域模型并不總是最合適的選擇。但如果選擇了它,就必須充分利用其優勢——將業務邏輯封裝在模型之中。
這不是分層設計的對立面
有人誤以為,把行為寫進領域對象,就破壞了分層架構。其實不然。只要對象中承載的是與該對象語義強相關的行為(如驗證、計算、規則),就屬于領域邏輯,是完全可以也應該放在模型內部的。而像數據源操作、UI邏輯等當然應該放在其他層處理。
Evans 是怎么說的?
在《領域驅動設計》一書中,Eric Evans 對不同層次的職責做了清晰的劃分:
- 應用層(Service):協調任務、調度模型、提供任務狀態,不包含業務規則。
- 領域層(Model):承載全部業務知識,操作業務狀態,是系統的核心。
他強調,Service 層應該很薄,所有重要邏輯都應由模型來執行。可惜,很多人沒有深入理解這一思想,導致架構異化為純 Service 操作模型數據的腳本系統。
為什么貧血模型廣泛存在?
很大程度上,是因為:
- 許多開發者從未見識過優秀的領域建模實例。
- 傳統的 CRUD 思維、數據導向開發方式深入人心。
- 技術歷史遺留(如 EJB)讓人習慣于數據和邏輯分離。
但代價是巨大的。一個項目若把所有行為塞進 Service 層,不僅邏輯難以維護,甚至會徹底喪失面向對象設計的優勢。
貧血模型的優點
不可否認,貧血模型也有其存在的合理性:
- 簡單直觀:對于業務邏輯極其簡單的系統,使用貧血模型可以快速上手。
- 易于開發:更貼合數據庫結構,便于初學者理解和操作。
但其缺點也十分明顯:
- 難以支撐復雜業務:例如某些合同的收入確認規則會因簽署時間、地區等條件而變化,這類規則用 if/else 寫在 Service 中將變得臃腫不堪。
- 邏輯難以復用與測試:行為分散、模型空洞,導致邏輯難以解耦與單元測試。
充血模型:回歸面向對象的本質
與貧血模型不同,充血模型(Rich Domain Model)強調“對象即行為的載體”。一個對象應當既有狀態(數據),也有行為(邏輯)。
舉個簡單的例子:
傳統貧血設計:
User user = new User();
user.setName("Tom");
userManager.save(user);
充血設計則更符合面向對象原則:
User user = new User("Tom");
user.save();
此時,User 本身擁有保存自己的能力,而不再依賴一個冗余的 UserManager 來操作。
這種設計更具語義性、可讀性和可維護性。它不只是代碼風格的變化,而是設計哲學的轉變。
設計難點:什么邏輯該進模型?
真正難的是界定“哪些邏輯適合放在對象中”。這要求團隊:
- 理解對象的職責邊界
- 善于識別業務語義
- 能夠將復雜規則提煉為清晰的行為
在實踐中,應遵循“高內聚、低耦合”的原則。如果對象內部包含多個子對象,應將相應的職責委托給更細粒度的子對象,從而自然實現責任劃分。這時,策略模式、組合模式等也能更好地發揮作用,取代丑陋的 if/else。
總結
貧血模型與充血模型,代表了面向過程與面向對象的兩種哲學。它們沒有絕對的好壞,關鍵在于是否能根據實際業務需求、團隊能力做出平衡。
- 簡單業務:貧血模型更容易理解和實現;
- 復雜系統:充血模型更能保持架構清晰,邏輯內聚。
但如果項目目標是長期演進、高可維護性的企業級系統,充血模型無疑才是面向對象設計的正道。
參考資料:
- 《領域驅動設計》(Eric Evans)
- Martin Fowler 的博客文章:Anemic Domain Model
- 知乎相關討論:貧血模型與充血模型的區別與實踐經驗