2012-04-11 56 views
27

我意識到像Morphia和Hibernate這樣的持久性框架依靠域對象上的註釋來實現它們的魔力。在某種程度上,在我看來,這是將持久性問題插入域層,這是我們應該努力避免的。域對象中的持久性註釋是一種不好的做法嗎?

這是我應該嘗試通過使用外部配置文件或可能從域模型單獨的DTO躲閃嗎?或者,持久層和域之間的這個小漏洞通常被認爲是可以接受的?

+2

看到這個演示http://www.infoq.com/presentations/Clean-Model-Tim-McCarthy – MikeSW 2012-04-11 05:41:11

+0

謝謝,這看起來是一個偉大的演講!現在看... – HolySamosa 2012-04-11 16:34:30

回答

12

在我使用Spring和Hibernate的現有系統上的最新版本中,我開始轉向類似的問題。首次實現Hibernate模型時,我努力通過數據訪問對象將服務類中的應用程序邏輯與持久性邏輯分開。去年建立一個新系統時,我允許大多數持久化對象作爲域對象,因爲這是一個權宜之計。

但是,我正在根據不斷變化的業務需求重新設計相同的系統,而我又傾向於分離這些問題。我只是進入新設計的幾天,但是我發現自己寧願有一個代表內存中關注對象的對象,而使用單獨的基於持久性的對象將其狀態更改存儲到數據庫。例如,我有一個Lead用於持久性,並有一個平行的ActiveLead,它跨越事務處理。

我還不確定這是最好的方法,但它在腸道上有意義。我渴望擁有持久性不可知的集合 - 不,持久性 - 無知 - 不考慮標準CRUD簡化而保持跨數據庫事務的內存駐留的對象集合。但我明白到最後所有的數據庫操作都是作爲CRUD來實現的。兩個世界必須相互碰撞,訣竅在於讓他們和諧地跳舞。

域對象上的Hibernate註釋?在我看來,這是在易於實現和易於維護之間的一個很好的折衷。

+2

這也使您有機會對持久性對象與內存中對象的狀態進行更嚴格/更嚴格的規定。我試圖非常瞭解接口的輸入和輸出,並以這種方式分離這些問題。 – alexwen 2012-04-11 06:06:04

4

在我看來,沒有必要複製域對象以便能夠將它們從持久層中分離出來。它可以處理重複的代碼,並且通過使用與DTO相同的對象完全可行。如果需要的話,如果需要的話,你可以隨時使用單獨的課程,但是我不會把它作爲一個經驗法則,這會花費你的時間,我們都知道時間是有價值的。

+3

我不同意。域對象的本質不同於持久性模型對象,它們有不同的用途。如果有時域並不是真正的主導,持久化實體可以直接使用,那就是一個簡單的巧合,一個實現細節。這個想法是,你開始建模域,忽略所有的持久性。最近我在這個博客上發佈了這樣的情況:http://www.sapiensworks.com/blog/post/2012/04/07/Just-Stop-It!-The-Domain-Model-Is-Not-The-Persistence-Model。 aspx – MikeSW 2012-04-11 05:31:39

+0

@MikeSW根據我的經驗,域對象和表* * * * *的差別很大。出現的差異應該很容易通過ORM的定製選項進行轉換。在我看來,一個好的ORM應該有助於簡化這一過程。 – Timo 2017-05-29 12:22:23

+0

完全同意@MikeSW和他的博客文章。例如,我不想被迫在我的領域模型中使用醜陋的getter/setter來滿足ORM的需求。我希望我的領域模型不受任何持久性世界的限制。 – Mik378 2017-07-02 03:32:23

7

我最近工作一個相當複雜的系統上有一個單獨的持久層,它是在屁股巨大的痛苦和可維護性非常糟糕。你基本上是在看YAGNI和Single Responsibility之間的衝突。在我看來,YAGNI是更重要的一個(唉,也是更常被忽視的一個)。

在絕大多數情況下,如果您使用ORM,直接堅持域對象要好得多,除非您有強制持久性實體的結構不同的具體要求(如果它們具有完全相同的結構,除了象牙塔參數外沒有理由將它們分開)。

可以肯定的是:總是在單獨的服務/ DAO層中執行實際的持久性內容(調用ORM函數)!那樣的話,如果你發現你需要的話,以後很容易引入一個持久層。

+1

使用相同對象遇到麻煩的一種情況是來自多個線程/服務器的樂觀鎖定。例如,如果嘗試保存對「firstName」的更改,並且發現有其他人更改了該對象,則需要重新加載對象以進行更改。如何將新加載和修改的對象插回到域對象樹中? – 2012-04-11 09:17:00

+2

投了下來,我不同意。太多的註釋涉及太多的問題,這些問題往往會迫使你的課堂結構進入不同的方向,再加上行爲,這使得你的課堂難以閱讀和維護,這很糟糕。 – dalvarezmartinez1 2014-10-31 20:09:03

+0

@dalvarezmartinez - 如果註釋不僅用於數據持久性問題,還用於域問題?例如:將一個域對象上的Name屬性標記爲'[Required]'使得該域是必需的,同時也通知驗證層DB層它們不應該接受空值。 – Sam 2015-03-17 03:44:23

3

簡答:我喜歡持久的,豐富的域對象。

龍答:

近10年來,我使用Spring和Hibernate一個相當大的系統〜500K LOC上工作。在開始時,我們開始使用「交易腳本」(參見Fowler)方法,部分原因是我們不太相信Hibernate。然而,在很短的時間內,我們開始相信Hibernate,並且由於我早期的純粹OO培訓,我成爲結合領域驅動設計方法的暫態持久性的忠實擁護者。我們基本上認爲我們的系統支持ODBMS(有大量的小漏洞:-))。

我把我們的架構稱爲「域內核」,因爲書DDD還沒有寫出來。這是在Hibernate的早期階段,所以域模型沒有被註釋污染。持久性的獨立關注在XML映射中保持獨立。

再次,隨着時間的推移,我們更好地將行爲向下推入領域層。我們有一個非常傳統的控制器 - >服務 - > dao - >域分層方案,這是通過編譯時間依賴性強制執行的。我觀察到,隨着時間的推移,這種模式對我們的系統非常有效,它代表了401(k)計劃管理相當複雜的領域的每個方面,包括計劃設置,交易,會計,一致性測試,銷售,品牌等。具有(相對)透明的「神奇」持久性的豐富域模型是我們能夠根據域模型中的現有特徵構建新特徵的關鍵。

我們的服務層只是協調技術服務(如電子郵件,文件I/O,排隊等)之間的交互,並幫助在必要時跨越域包。服務層還定義了事務邊界(通過Spring)。服務只接收或發出DTO或原語。很多人討厭DRY,但我們發現它在定義服務接口和使用它們的代碼時保持誠實。這也讓後來的事情變得很容易。

這種方法允許我們用一個非常小的團隊(我們是一個Scrum團隊)來構建高質量的軟件。

所以,請認爲我是持久域對象的信徒。不知道我的故事是否有用,但我想分享。

9

域對象中的持久性註釋是一種不好的做法嗎?

。隨着NoSQL的興起,你不能依賴單一的持久性策略。

例如,今天我堅持我的域對象(比方說使用嗎啡)到MongoDB。 如果明天我想堅持域對象到Neo4j會怎麼樣?

或者,可能需要將域對象持久化到像關係(Postgres/MySQL),MongoDB(文檔存儲)和Neo4J(圖形數據庫)這樣的三種數據庫,以便進行評估。

在所有這些情況下,最好能有獨立的持久化策略,而不是僅僅依賴於域對象

最佳實踐:傳遞持久戰略的戰略格局可能的幫助。但是在設計你的類/對象時必須小心。

+0

我認爲有太多「如果......」的問題。我沒有看到任何改變數據庫的項目,也沒有在單個模塊中使用三種不同的數據庫技術,這可能會犧牲維護額外策略/映射程序等的成本。根據我的經驗,從這種方法開始,項目要困難得多,而不是簡單地註釋領域層。 – thilko 2015-02-10 09:36:32

+0

@thilko我認爲你從公平角度講,實際上講。儘管如此,這使我得出結論:**需要將持久性框架與存儲庫模式**結合使用更容易 - 尤其是對於DDD和/或TDD。 – Timo 2017-05-29 12:13:27

0

我寧願富有的域對象,它有註釋。即使埃文斯在他的sample app中也使用這種方法。他使用XMl而不是註釋,但他仍然堅持相同的對象。

也許分離域和持久性會更乾淨,但是不要只是爲了能夠在將來選擇不同的數據庫技術。這是複雜的地獄之路,YAGNI先生會咬你。

4

我相信我會在我的域名上使用註釋,如果我已經決定了我將要使用的持久性框架,但是如果您遵循Hexagonal體系結構和TDD,XML會更方便。如果您預先使用特定的框架註釋您的域,那麼您的域將與持久集成結合在一起,並且無法以技術/框架不可知的目標測試核心功能。

相關問題