在領域驅動的設計,貧乏的領域模型,代碼生成,依賴項注入等方面……

埃里克·埃文斯(Eric Evans)已制定了什么是域驅動設計(DDD)。 Martin Fowler是DDD的大力支持者和擁護者。 這些都是非凡的名字,幾乎可以肯定的是,他們正在支持一些有價值的東西。 我不是在這里對此爭論。 也許我正在試圖證明我編寫軟件的方式的合理性,或者也許我只是試圖清除事物并具有建設性。 讓我們來看看。

什么是領域驅動設計的核心- 域模型通用語言的抽象的概念。 我不會對此進行詳細介紹–對于那些感興趣的人,有維基百科(在頁腳中有很多參考文獻可供閱讀)。 從理論上講,這一切都是非常好的,并且域驅動的構建軟件的方式應該對所有人都具有吸引力–畢竟,構建該軟件是為了該領域的利益,而不是建筑師,開發人員或QA的利益。

但是現在談到了實際部分–如何實施DDD? 我將在當代的背景下回答這個問題,即使用spring和hibernate之類的框架。 我會證明它們的用法合理。 Spring是一個非侵入性的依賴注入框架。 Fowler也強烈支持DI,并且DI被認為是實現DDD的好方法。 休眠是使用關系數據庫時使用對象的一種方法。 另一種方法是使用JDBC并手動構造對象,但這很繁瑣。 因此,休眠不會影響體系結構部分-它是一種實用程序(當然,功能非常強大)。

在本文中,我將使用“休眠”和“彈簧”作為“給定的”,盡管它們可以通過任何DI框架以及任何依賴對象的ORM或其他持久性機制進行更改。

使用spring和hibernate實現DDD的公認方法是:

  • 使用@Configurable使域對象適合進行依賴項注入(它們不會在spring之前實例化,因此它們需要這種特殊方法)
  • 在域對象中注入存儲庫對象,以允許域對象執行與持久性相關的操作
  • 使用薄的,無狀態的事務服務層(外觀)來協調域對象

這篇廣泛的文章顯示了這種方法的實現和描述。 另一個示例(沒有Spring)是http://dddsample.sourceforge.net/ 。 稍后再討論。

這種方法的替代方法是貧血域模型 。 它被認為是一種反模式,但同時非常普遍并且經常使用。 貧血數據模型的功能很簡單–域對象內部沒有業務邏輯–它們只是數據持有者。 而是將業務邏輯放在服務中。

之所以將其視為反模式,是因為,首先,這似乎是一種程序方法。 它破壞了封裝,因為對象的內部狀態完全不是內部狀態。 其次,由于領域對象是設計的中心,因此,如果它的操作不屬于它,而改為多個無狀態服務類,則很難更改它。 域驅動的設計針對中型到大型應用程序,這些應用程序發生了很大變化,并且需要一種簡便的方法來快速進行更改,而又不會破壞其他功能。 因此,在對象本身內具有對象的所有功能非常重要。 這也可以確保減少重復代碼。

因此,代替讓服務來計算價格: ProductServiceImpl.calculatePrice(complexProduct),我們應該簡單地擁有ComplexProduct.calculatePrice() 。 因此,每當領域專家說價格計算機制發生變化時,更改它的地方就是一種,也是最直接的一種。

如果考慮簡單的操作,這看起來很容易。 但是,當一個域對象需要另一個域對象來完成其工作時,它將變得更加復雜。 使用貧血數據模型,只需將另一個Service注入當前Service并調用其方法即可實現。 使用建議的DDD,可以通過將域對象作為參數來實現。

在我看來,域對象(它也是休眠實體)已經設置了其依賴項。 但不是在Spring之前,因為Spring無法確切知道要注入哪個領域對象。 它們由休眠“注入”,因為它確切知道應將哪個(由主鍵標識)域對象放置在另一個域對象中。 因此,有一個奇怪的例子–如果產品腐爛并且必須在倉庫中分配氣味,則必須調用例如Warehouse.increaseSmellLevel(getSmellCoeficient()) 。 并且它有精確的倉庫,不受彈簧的干擾。

現在,我不同意這一點。 大多數來源(包括上面鏈接的兩個來源)都指出應該將存儲庫/ DAO注入域對象中。 不,他們不應該。 只需調用“保存”或“更新”就不需要了解對象的內部狀態。 Hibernate仍然知道一切。 因此,我們只是將整個對象傳遞到存儲庫。

讓我們將其分為兩個部分- 業務邏輯基礎架構邏輯 。 域對象應該對基礎結構一無所知。 那可能意味著它不應該知道它被保存在某個地方。 產品是否關心其存儲方式? 不,這是“感興趣”的存儲機制。 這是實際的缺點:

  • 通過簡單地將存儲庫調用包裝在所有域對象中來實現CRUD –代碼重復
  • 域對象可傳遞地依賴于持久性–即它不是純域對象,并且如果存儲庫發生更改,則也必須對其進行更改。 從理論上講,僅當域規則和屬性更改時,才應更改
  • 人們很容易將事務,緩存和其他邏輯包含在域對象中

在上面的一篇文章中,我將在此處打開有關建議的解決方案的括號,以使代碼重復和樣板代碼更易于處理。 建議生成代碼。 而且我認為代碼生成是一種罪過。 它將無法刪除重復的或非常相似的代碼并將其抽象化為工具。 最引人注目的示例是生成ProductDAO,CategoryDAO,WarehouseDAO等。生成的代碼難以管理,無法擴展且嚴重依賴于外部元數據,這絕對不是面向對象的方法。

說到存儲庫,在建議的示例中,每個域對象都應該有一個存儲庫,該存儲庫又將調用持久性機制。 那我們得到什么:

用戶在UI中按“保存”>保存在服務上的UI調用(以便獲得事務支持)>保存在域對象上的服務調用>保存在資源庫上的域對象調用>保存在持久性機制上的資源庫調用>持久性機制保存對象。

是我自己,還是在這里調用域對象是多余的。 這是一種不增加任何內容的直通方法。 而且由于很多功能與CRUD有關(是的,即使在大型企業應用程序中也是如此),這對我來說似乎很糟糕。

最后,我發現@Configurable方法是一個hack。 它在后臺做了一些魔術,這不是任何通用語言功能(也不是設計模式),并且為了了解它是如何發生的,您需要大量的經驗。

所以,總結上面的大混亂

  • 域對象不應由Spring(IoC)進行管理,它們不應具有DAO或與基礎結構有關的任何內容
  • 域對象具有由休眠(或持久性機制)設置的它們依賴的域對象
  • 域對象執行業務邏輯,就像DDD的核心思想一樣,但這不包括數據庫查詢或CRUD –僅對對象內部狀態進行的操作
  • 幾乎不需要DTO-在大多數情況下,域對象本身就是DTO(這節省了一些樣板代碼)
  • 服務執行CRUD操作,發送電子郵件,協調域對象,基于多個域對象生成報告,執行查詢等。
  • 服務(應用程序)層并不薄,但不包括域對象固有的業務規則
  • 應避免生成代碼。 應該使用抽象,設計模式和DI來克服代碼生成的需求,并最終–擺脫代碼重復。

參考:有關 領域驅動設計,貧乏領域模型,代碼生成,依賴項注入等的信息 ,請參見Bozho技術博客上的JCG合作伙伴 Bozho。

相關文章 :

  • Spring和AspectJ的領域驅動設計
  • 在域驅動設計中使用狀態模式
  • ORM問題
  • 什么是依賴倒置? 是IoC嗎?
  • 框架使開發人員愚蠢嗎?
  • 每個程序員都應該知道的事情
  • JDK中的設計模式
  • Java最佳實踐

翻譯自: https://www.javacodegeeks.com/2011/09/on-domain-driven-design-anemic-domain.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374896.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374896.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374896.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Javascript模塊化工具require.js教程

轉自:http://www.w3cschool.cc/w3cnote/requirejs-tutorial-1.html, http://www.w3cschool.cc/w3cnote/requirejs-tutorial-2.html 隨著網站功能逐漸豐富,網頁中的js也變得越來越復雜和臃腫,原有通過script標簽來導入一個個的js文件這種方式已…

數據值、列類型和數據字段屬性

數據值:數值型、字符型、日期型和空值等。數據列類型 2.1 數值類的數據列類型2.2 字符串類數據列類型 2.3 日期和時間型數據數據列類型 另外,也可以使用整形列類型存儲UNIX時間戳,代替日期和時間列類型,這是基于PHP的web項目中常…

全文搜索Apache Lucene簡介

在本教程中,我想談談Apache Lucene 。 Lucene是一個開源項目,提供基于Java的索引和搜索技術。 使用其API,很容易實現全文搜索 。 我將處理Lucene Java版本 ,但請記住,還有一個名為Lucene.NET的.NET端口,以及…

函數scanf

本節介紹輸入函數 scanf 的用法。scanf 和 printf 一樣&#xff0c;非常重要&#xff0c;而且用得非常多&#xff0c;所以一定要掌握。 概述 scanf 的功能用一句話來概括就是“通過鍵盤給程序中的變量賦值”。該函數的原型為&#xff1a; # include <stdio.h> int scanf(…

C語言中定義變量位置

C標準的問題 C89規定&#xff0c;在任何執行語句之前&#xff0c;在塊的開頭聲明所有局部變量。 即應該如下&#xff1a;定義變量只能在最開始&#xff0c;scanf等執行語句之前 int N 0;double sum 0;scanf("%d",&N);在C99以及C中則沒有這個限制&#xff0c;即…

Java中的低GC:使用原語而不是包裝器

總覽 有兩個很好的理由在可能的地方使用原語而不是包裝器。 明晰。 通過使用原語&#xff0c;您可以清楚地知道null值是不合適的。 性能。 使用原語通常更快。 清晰度通常比性能更重要&#xff0c;并且是使用它們的最佳理由。 但是&#xff0c;本文討論了使用包裝程序對性能…

C# 連接Oracle數據庫以及一些簡單的操作

拖了很久今天終于在博客園寫了自己第一篇隨筆&#xff1a; 話不多說&#xff0c;我們直接進入正題&#xff1a; 1.連接數據庫 using (OracleConnection conn new OracleConnection("data source192.168.97.60/orcl;User Idabc;Passwordabc;")) { …

markdownb編輯器

這是H1 這是H2 這是H3 這是一個標題。 這是第一行列表項。這是第二行列表項。給出一些例子代碼&#xff1a; return shell_exec("echo $input | $markdown_script"); 轉載于:https://www.cnblogs.com/xcl461330197/p/4605163.html

Java Secret:使用枚舉構建狀態機

總覽 Java中的枚舉比許多其他語言更強大&#xff0c;這可能導致令人驚訝的用途。 在本文中&#xff0c;我概述了Java 枚舉的一些單獨功能&#xff0c;并將它們組合在一起形成一個狀態機。 單例和實用程序類的枚舉 您可以非常簡單地將枚舉用作Singleton或Utility。 enum Si…

數組部分筆記

對于數組的初始化需要注意以下幾點&#xff1a; 可以只給部分元素賦值。當{ }中值的個數少于元素個數時&#xff0c;只給前面部分元素賦值。例如&#xff1a; int a[10]{12, 19, 22 , 993, 344};表示只給 a[0]~a[4] 5個元素賦值&#xff0c;而后面 5 個元素自動初始化為 0。 …

指向函數的指針

指向函數的指針變量的一般形式為&#xff1a;數據類型 &#xff08;*指針變量名&#xff09;&#xff08;函數參數表列&#xff09;&#xff1b;如&#xff1a; int (*p)(int ,int );1、int (*p)(int ,int );表示定義一個指向函數的指針變量p&#xff0c;它不是固定只能指向…

核心Java面試答案不正確

總覽 在Internet上&#xff0c;Java面試問題和答案從一個網站復制到另一個網站。 這可能意味著錯誤或過時的答案可能永遠不會得到糾正。 這是一些不太正確或已經過時的問題和答案。 即是Java 5.0之前的版本。 每個提供的問題后都有兩個部分。 斜體的第一部分指示答案不完整/錯…

138.括號序列(區間型DP)

3657 括號序列 時間限制: 1 s空間限制: 256000 KB題目等級 : 黃金 Gold題解查看運行結果題目描述 Description我們用以下規則定義一個合法的括號序列&#xff1a; &#xff08;1&#xff09;空序列是合法的 &#xff08;2&#xff09;假如S是一個合法的序列&#xff0c;則 (S) …

C# 執行批處理文件(*.bat)的方法代碼

代碼如下:static void Main(string[] args){Process proc null;try{ string targetDir string.Format("D:\adapters\setup");//this is where mybatch.bat liesproc new Process();proc.StartInfo.WorkingDirectory targetDir;proc.StartInfo.Fil…

C語言空格怎么表示

1.直接敲空格就行&#xff0c;或者使用ASCII碼值賦值為32。 空格沒有轉義字符。 printf("12%c45 58",32);輸出 12 45 582.合法轉義字符如下&#xff1a;\a 響鈴(BEL) 、\b 退格(BS)、\f 換頁(FF)、\n 換行(LF)、\r 回車(CR)、\t 水平制表(HT)、\v 垂直制表(VT) 0、…

Tomcat中的零停機部署(和回滾); 演練和清單

親愛的大家&#xff0c; 如果您認為Tomcat不能再進步&#xff0c;那您就錯了。 Tomcat 7引入了所謂的并行部署 。 這是由SpringSource / VMWare貢獻的。 簡而言之&#xff0c;并行部署是一種能夠并行部署一個以上版本的Web應用程序的功能&#xff0c;使所有版本都可以在完全相…

javaweb 學習資源

http://jinnianshilongnian.iteye.com/category/231099轉載于:https://www.cnblogs.com/sishahu/p/5368018.html

HDU 1863 暢通工程(最小生成樹,prim)

題意&#xff1a; 給出圖的邊和點數&#xff0c;要求最小生成樹的代價&#xff0c;注&#xff1a;有些點之間是不可達的&#xff0c;也就是可能有多個連通圖。比如4個點&#xff0c;2條邊:1-2&#xff0c;3-4。 思路&#xff1a; 如果不能連通所有的點&#xff0c;就輸出‘?’…

2000年不算在21世紀

練習3-5 輸出閏年 (15 分) 輸出21世紀中截止某個年份以來的所有閏年年份。注意&#xff1a;閏年的判別條件是該年年份能被4整除但不能被100整除、或者能被400整除。 想當然地以為21世紀是2000~2099&#xff0c;當然沒有通過 if(N > 2000&&N < 2099){for(int i …

使用迭代器時如何避免ConcurrentModificationException

Java Collection類是快速失敗的&#xff0c;這意味著如果在使用迭代器遍歷某個線程的同時更改了Collection&#xff0c;則iterator.next&#xff08;&#xff09;將拋出ConcurrentModificationException 。 在多線程以及單線程環境下都可能出現這種情況。 讓我們通過以下示例探…