领域驱动设计之领域模型_在领域驱动的设计,贫乏的领域模型,代码生成,依赖项注入等方面……...
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之領(lǐng)域模型
埃里克·埃文斯(Eric Evans)已制定了域驅(qū)動(dòng)設(shè)計(jì)(DDD)。 Martin Fowler是DDD的大力支持者和擁護(hù)者。 這些都是非凡的名字,幾乎可以肯定的是,他們正在支持一些有價(jià)值的東西。 我不是在這里爭論。 也許我正在試圖證明我編寫軟件的方式的合理性,或者也許我只是試圖清除事物并具有建設(shè)性。 讓我們來看看。什么是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的核心- 域 , 域模型 , 通用語言的抽象的概念。 我不會(huì)對此進(jìn)行詳細(xì)介紹–對于那些感興趣的人,有維基百科(在頁腳中有很多參考文獻(xiàn)可供閱讀)。 從理論上講,這都是非常好的,并且域驅(qū)動(dòng)的構(gòu)建軟件的方式應(yīng)該對所有人都有吸引力–畢竟,構(gòu)建該軟件是為了該領(lǐng)域的利益,而不是建筑師,開發(fā)人員或QA的利益。
但是現(xiàn)在是實(shí)際部分–如何實(shí)施DDD? 我將在當(dāng)代的背景下回答這個(gè)問題,即使用spring和hibernate之類的框架。 我會(huì)證明它們的用法合理。 Spring是一個(gè)非侵入性的依賴注入框架。 Fowler也強(qiáng)烈支持DI,并且DI被認(rèn)為是實(shí)現(xiàn)DDD的好方法。 Hibernate是使用關(guān)系數(shù)據(jù)庫時(shí)使用對象的一種方法。 另一種方法是使用JDBC并手動(dòng)構(gòu)造對象,但這很繁瑣。 因此,Hibernate不會(huì)影響體系結(jié)構(gòu)部分-它是一種實(shí)用程序(當(dāng)然,功能非常強(qiáng)大)。
在本文中,我將使用“Hibernate”和“彈簧”作為“給定的”,盡管它們可以通過任何DI框架以及任何依賴對象的ORM或其他持久性機(jī)制進(jìn)行更改。
用spring和hibernate實(shí)現(xiàn)DDD的公認(rèn)方法是:
- 使用@Configurable使域?qū)ο笥匈Y格進(jìn)行依賴注入(它們不會(huì)在spring之前實(shí)例化,因此它們需要這種特殊方法)
- 在域?qū)ο笾凶⑷氪鎯?chǔ)庫對象,以允許域?qū)ο髨?zhí)行與持久性相關(guān)的操作
- 使用薄的,無狀態(tài)的事務(wù)服務(wù)層(外觀)來協(xié)調(diào)域?qū)ο?
這篇廣泛的文章中介紹了這種方法的實(shí)現(xiàn)和描述。 另一個(gè)示例(沒有Spring)是http://dddsample.sourceforge.net/ 。 稍后再討論。
這種方法的替代方法是貧血域模型 。 它被認(rèn)為是一種反模式,但同時(shí)非常普遍并且經(jīng)常使用。 貧血數(shù)據(jù)模型的功能很簡單–域?qū)ο髢?nèi)部沒有業(yè)務(wù)邏輯–它們只是數(shù)據(jù)持有者。 而是將業(yè)務(wù)邏輯放在服務(wù)中。
之所以將其視為反模式,是因?yàn)?#xff0c;首先,這似乎是一種程序方法。 它破壞了封裝,因?yàn)閷ο蟮膬?nèi)部狀態(tài)完全不是內(nèi)部狀態(tài)。 其次,由于領(lǐng)域?qū)ο笫窃O(shè)計(jì)的中心,因此如果它的操作不屬于它,而改為多個(gè)無狀態(tài)服務(wù)類,則很難更改它。 域驅(qū)動(dòng)的設(shè)計(jì)針對中型到大型應(yīng)用程序,這些應(yīng)用程序發(fā)生了很大變化,并且需要一種簡便的方法來快速進(jìn)行更改,而又不會(huì)破壞其他功能。 因此,在對象本身內(nèi)具有對象的所有功能非常重要。 這也可以確保減少重復(fù)代碼。
因此,代替讓服務(wù)來計(jì)算價(jià)格: ProductServiceImpl.calculatePrice(complexProduct),我們應(yīng)該簡單地?fù)碛?strong>ComplexProduct.calculatePrice() 。 因此,每當(dāng)領(lǐng)域?qū)<艺f價(jià)格計(jì)算機(jī)制發(fā)生變化時(shí),更改的地方恰好是一種,也是最直接的一種。
如果考慮簡單的操作,這看起來很容易。 但是,當(dāng)一個(gè)域?qū)ο笮枰硪粋€(gè)域?qū)ο髞硗瓿善涔ぷ鲿r(shí),它將變得更加復(fù)雜。 使用貧血數(shù)據(jù)模型,只需將另一個(gè)Service注入當(dāng)前Service并調(diào)用其方法即可實(shí)現(xiàn)。 使用建議的DDD,可以通過將域?qū)ο笞鳛閰?shù)來實(shí)現(xiàn)。
在我看來,域?qū)ο?#xff08;也是Hibernate實(shí)體)已經(jīng)設(shè)置了依賴項(xiàng)。 但不是在Spring之前,因?yàn)镾pring無法確切知道要注入哪個(gè)領(lǐng)域?qū)ο蟆?它們由Hibernate“注入”,因?yàn)樗_切知道應(yīng)將哪個(gè)(由主鍵標(biāo)識(shí))域?qū)ο蠓胖迷诹硪粋€(gè)域?qū)ο笾小?因此,有一個(gè)奇怪的例子–如果產(chǎn)品腐爛并且必須在倉庫中分配氣味,則必須調(diào)用例如Warehouse.increaseSmellLevel(getSmellCoeficient()) 。 而且它有精確的倉庫,不受彈簧的干擾。
現(xiàn)在,我不同意這一點(diǎn)。 大多數(shù)來源(包括上面鏈接的兩個(gè)來源)都指出應(yīng)將存儲(chǔ)庫/ DAO注入域?qū)ο笾小?不,他們不應(yīng)該。 只需調(diào)用“保存”或“更新”就不需要了解對象的內(nèi)部狀態(tài)。 Hibernate仍然知道一切。 因此,我們只是將整個(gè)對象傳遞到存儲(chǔ)庫。
讓我們將其分為兩個(gè)部分: 業(yè)務(wù)邏輯和基礎(chǔ)架構(gòu)邏輯 。 域?qū)ο蟛粦?yīng)該了解基礎(chǔ)結(jié)構(gòu)。 那可能意味著它不應(yīng)該知道它被保存在某個(gè)地方。 產(chǎn)品是否關(guān)心其存儲(chǔ)方式? 不,這是“感興趣”的存儲(chǔ)機(jī)制。 這里是實(shí)際的缺點(diǎn):
- CRUD通過簡單地將存儲(chǔ)庫調(diào)用包裝在所有域?qū)ο笾衼韺?shí)現(xiàn)–代碼重復(fù)
- 域?qū)ο罂蓚鬟f地依賴于持久性–即它不是純域?qū)ο?#xff0c;并且如果存儲(chǔ)庫發(fā)生更改,則也必須對其進(jìn)行更改。 從理論上講,僅當(dāng)域規(guī)則和屬性更改時(shí),才應(yīng)更改
- 人們很容易將事務(wù),緩存和其他邏輯包含在域?qū)ο笾?
在上面的一篇文章中,我將在此處為建議的解決方案打開一個(gè)括號(hào),以使代碼重復(fù)和樣板代碼更易于處理。 建議生成代碼。 而且我認(rèn)為代碼生成是一種罪過。 它將無法刪除重復(fù)的或非常相似的代碼并將其抽象化為工具。 最引人注目的示例是生成ProductDAO,CategoryDAO,WarehouseDAO等。生成的代碼難以管理,無法擴(kuò)展且嚴(yán)重依賴于外部元數(shù)據(jù),這絕對不是面向?qū)ο蟮姆椒ā?
說到存儲(chǔ)庫,在建議的示例中,每個(gè)域?qū)ο蠖紤?yīng)該有一個(gè)存儲(chǔ)庫,該存儲(chǔ)庫又將調(diào)用持久性機(jī)制。 那我們得到什么:
用戶在UI中按“保存”>保存在服務(wù)上的UI調(diào)用(以便獲得事務(wù)支持)>保存在域?qū)ο笊系姆?wù)調(diào)用>保存在資源庫上的域?qū)ο笳{(diào)用>保存在持久性機(jī)制上的資源庫調(diào)用>持久性機(jī)制保存對象。
是我自己,還是在這里調(diào)用域?qū)ο笫嵌嘤嗟摹?這是一種不增加任何內(nèi)容的直通方法。 而且由于很多功能與CRUD相關(guān)(是的,即使在大型企業(yè)應(yīng)用程序中也是如此),這對我來說似乎很糟糕。
最后,我發(fā)現(xiàn)@Configurable方法是一個(gè)hack。 它在后臺(tái)做了一些魔術(shù),這不是任何通用語言功能(也不是設(shè)計(jì)模式),并且為了了解它是如何發(fā)生的,您需要大量的經(jīng)驗(yàn)。
所以,總結(jié)上面的大混亂
- 域?qū)ο蟛粦?yīng)由Spring(IoC)進(jìn)行管理,它們不應(yīng)具有DAO或任何與基礎(chǔ)結(jié)構(gòu)相關(guān)的內(nèi)容
- 域?qū)ο缶哂杏蒆ibernate(或持久性機(jī)制)設(shè)置的它們依賴的域?qū)ο?
- 域?qū)ο髨?zhí)行業(yè)務(wù)邏輯,就像DDD的核心思想一樣,但這不包括數(shù)據(jù)庫查詢或CRUD –僅對對象內(nèi)部狀態(tài)進(jìn)行的操作
- 幾乎不需要DTO-在大多數(shù)情況下,域?qū)ο蠖际荄TO本身(這節(jié)省了一些樣板代碼)
- 服務(wù)執(zhí)行CRUD操作,發(fā)送電子郵件,協(xié)調(diào)域?qū)ο?#xff0c;基于多個(gè)域?qū)ο笊蓤?bào)告,執(zhí)行查詢等。
- 服務(wù)(應(yīng)用程序)層并不薄,但不包括域?qū)ο蠊逃械臉I(yè)務(wù)規(guī)則
- 應(yīng)避免生成代碼。 應(yīng)該使用抽象,設(shè)計(jì)模式和DI來克服代碼生成的需求,并最終–擺脫代碼重復(fù)。
參考:有關(guān) 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),貧乏領(lǐng)域模型,代碼生成,依賴項(xiàng)注入等的信息 ,請參見Bozho的技術(shù)博客上的JCG合作伙伴 Bozho。
相關(guān)文章 :
- Spring和AspectJ的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
- 在域驅(qū)動(dòng)設(shè)計(jì)中使用狀態(tài)模式
- ORM問題
- 什么是依賴倒置? 是IoC嗎?
- 框架使開發(fā)人員愚蠢嗎?
- 每個(gè)程序員都應(yīng)該知道的事情
- JDK中的設(shè)計(jì)模式
- Java最佳實(shí)踐
翻譯自: https://www.javacodegeeks.com/2011/09/on-domain-driven-design-anemic-domain.html
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之領(lǐng)域模型
總結(jié)
以上是生活随笔為你收集整理的领域驱动设计之领域模型_在领域驱动的设计,贫乏的领域模型,代码生成,依赖项注入等方面……...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FatCow vs Bluehost:流
- 下一篇: apache lucene_全文搜索Ap