Eric Evans的《領域驅動設計》問世已經14年之久,到今天幾乎全部業務團隊都或多或少有涉及DDD。然而若是較真會發現,認真遵循DDD設計原則的團隊還是少數,在多數團隊的現都是:領域模型=數據庫關係。DDD崇尚的是oo式表達,也就是常說的充血模型,對以關係型數據庫實體關係爲中心的關係模型甚至是能夠用鄙夷來形容。sql
以數據庫關係指導編程實踐,是關係對程序的外延入侵,是預假設關係經存在再按圖索驥將執行邏輯映射到關係,最終收口是落在數據庫而非程序自己。程序自己成了一條條執行通道,每一條通道服務於特定場景的關係,後果必然是過程思惟和麪條代碼。數據庫
假設有業務場景--向購物車添加商品,以關係爲中心,代碼組織以下:編程
絕大多數人都應該會有相似代碼的編寫經歷,最多見在經典三分層架構中的Service層,它本質上就是一個類存儲過程,對其執行過程作翻譯:json
業務處理就是在有序執行一條條sql,老外給它取了個好聽的名字叫事務腳本。事務腳本是很是典型的過程式表述,相似是串聯sql完成一段完整的業務,能夠用求和數學公式「事務腳本=∑fi,fi代指一條sql」來定義架構
架構模式上管這種代碼叫貧血模型,即無行爲,表達力貧瘠。Martin Fowler在《企業應用架構模式》中nosql
定義它是反模式,簡單系統使用它開發沒問題,而對於複雜業務,業務邏輯、各類狀態散佈在大量的函數中,維護擴展的成本會變得很高。
數據庫中心的設計是」修改一處,全量回歸「的悲劇源頭,也是代碼寫久後枯燥、無聊、以爲都是重複勞動的源頭。過程式代碼並不須要精心設計和組織,天然寫代碼也就成了無心義的翻譯器。函數
oo模型
假設內存無限大且永不宕機,即已經沒有持久化必要,換句話說徹底能夠不使用數據庫,此時應該如何編寫代碼?性能
• 是使用與現實世界的活動實體作鏈接、特徵和行爲封裝在一塊兒,職責明確、邏輯合理分佈的有狀態對象,再按場景將合適類聯繫起來?spa
• 仍是使用僅映射活動實體特徵的pojo,按場景忠實反應發生過程依次get/set操做pojo屬性?翻譯
Jdk以及各種優秀中間件都是之內存操做爲主,能夠參考它們的選擇:即使是主推pojo規範的ejb都沒有選擇貧血模型,反而是極度充血-- 有行爲,有聯繫、表達力強,容易組織。
數據持久化應只被當成是程序的暫停而非結束,對暫停而言,下一次再執行時須要忠實還原對象的上次執行後狀態,而對結束則下一次是一個新的開始。即load; do; 和new; setter/getter; persist的區別。若是從這個角度出發,對象就變得近似是常駐在內存。
在Vaughn Vernon的《實現領域驅動設計》中關於「六邊形架構」如何在領域實踐應用中對數據庫和DDD的關係有很清晰的闡述很:
數據庫僅僅只是Domain Model的右向適配(被驅動者)。
數據庫只是持久化手段,是一種基礎設施,不應做爲指導程序運行的模型。寫代碼時要時刻保持一種警戒,若是把關係型數據庫替換成json、普通文本或者無schema的nosql數據庫,要如何保證邏輯層的無感?
正確的方法是以對象和對象聯繫而非數據庫表關係做爲指導程序運行的基礎。一次完整的業務操做由各實體對象行爲協同完成,結果最終會反映在內存中各對象實例的內在屬性上。這樣不管怎麼修改持久化方案,都只須要改變與特定持久化方案的適配策略。
某逆向交替系統,其逆向狀態是記錄在正向交易上的,| order_id | order_status |pay_fee|item_id|refund_amount
|refund_status|attributes|...... |
refund_amount和refund_status分別表明逆向退款金額以及退款狀態。很顯然,這樣的表設計會致使兩個問題:1) 沒法屢次逆向,前一次狀態在下一次發起後被覆蓋,即逆向沒法追溯;2)逆向須要更新交易訂單屬性,方式是調用交易接口更新,所以某些狀況下可能會有樂觀鎖問題-- 逆向更新了鎖版本致使交易再去更新失敗。在小規模試跑階段,業務上會嚴格限制一筆訂單一次逆向,同時對於樂觀鎖問題採用屢次重試機制,所以問題並不明顯。逐漸的業務開始起來,首先業務量大以後重試致使的性能問題凸顯--
在加事務的狀況下,數據庫鏈接是一直持有直到提交或回滾,屢次重試至關於增長了幾倍持有鏈接時間,所以會明顯明顯下降數據庫吞吐;其次,對於不能屢次逆向合做夥伴也開始有反彈。所以交易和逆向的表拆分變得勢在必行。由於逆向在設計時使用的充血+六邊形架構,實際上遷移並無很大工做量,只是從新建了表,而後對數據庫適配進行修改,將輸出表由A指向B。
而若是使用貧血模型,對錶結構作了較大變動的狀況下,邏輯代碼必定會須要修改。能夠用事務腳本的公式作個簡單的逆推導:
表變化=sql變化=事務腳本(邏輯執行)變化。
本文做者:鷹波
本文爲雲棲社區原創內容,未經容許不得轉載。