基本概念:
領域驅動設計(簡稱 ddd)概念來源於2004年著名建模專家eric evans發表的他最具影響力的書籍:《domain-driven design –tackling complexity in the heart of software》(中文譯名:領域驅動設計—軟件核心復雜性應對之道)一書。,書中提出了“領域驅動設計(簡稱 ddd)”的概念。
領域驅動設計一般分為兩個階段:
1. 以一種領域專家、設計人員、開發人員都能理解的“通用語言”作為相互交流的工具,在不斷交流的過程中發現和挖出一些主要的領域概念,然后將這些概念設計成一個領域模型;
2. 由領域模型驅動軟件設計,用代碼來表現該領域模型。領域需求的最初細節,在功能層面通過領域專家的討論得出。
領域驅動設計告訴我們,在通過軟件實現一個業務系統時,建立一個領域模型是非常重要和必要的,因為領域模型具有以下特點:
1. 領域模型是對具有某個邊界的領域的一個抽象,反映了領域內用戶業務需求的本質;領域模型是有邊界的,只反應了我們在領域內所關注的部分;
2. 領域模型只反映業務,和任何技術實現無關;領域模型不僅能反映領域中的一些實體概念,如貨物,書本,應聘記錄,地址,等;還能反映領域中的一些過程概念,如資金轉賬,等;
3. 領域模型確保了我們的軟件的業務邏輯都在一個模型中,都在一個地方;這樣對提高軟件的可維護性,業務可理解性以及可重用性方面都有很好的幫助;
4. 領域模型能夠幫助開發人員相對平滑地將領域知識轉化為軟件構造;
5. 領域模型貫穿軟件分析、設計,以及開發的整個過程;領域專家、設計人員、開發人員通過領域模型進行交流,彼此共享知識與信息;因為大家面向的都是同一個模型,所以可以防止需求走樣,可以讓軟件設計開發人員做出來的軟件真正滿足需求;
6. 要建立正確的領域模型並不簡單,需要領域專家、設計、開發人員積極溝通共同努力,然后才能使大家對領域的認識不斷深入,從而不斷細化和完善領域模型;
7. 為了讓領域模型看的見,我們需要用一些方法來表示它;圖是表達領域模型最常用的方式,但不是唯一的表達方式,代碼或文字描述也能表達領域模型;
8. 領域模型是整個軟件的核心,是軟件中最有價值和最具競爭力的部分;設計足夠精良且符合業務需求的領域模型能夠更快速的響應需求變化;
領域驅動設計中的一些基本概念:
1.實體(entity):
根據eric evans的定義,”一個由它的標識定義的對象叫做實體”。通常實體具備唯一id,能夠被持久化,具有業務邏輯,對應現實世界業務對象。
實體一般和主要的業務/領域對象有一個直接的關系。一個實體的基本概念是一個持續抽象的生命,可以變化不同的狀態和情形,但總是有相同的標識。
需要注意的是:
一些開發人員將實體當成了orm意義上的實體,而不是業務所有和業務定義的領域對象。在一些實現中采用了transaction script風格的架構,使用貧血的領域模型。這種認識上的混亂,在領域驅動架構中,不願意在領域對象中加入業務邏輯而導致貧血的領域模型,同時還可能使混亂的服務對象激增。
2.值對象(value object)
值對象的定義是:描述事物的對象;更准確的說,一個沒有概念上標識符描述一個領域方面的對象。這些對象是用來表示臨時的事物,或者可以認為值對象是實體的屬性,這些屬性沒有特性標識但同時表達了領域中某類含義的概念。
通常值對象不具有唯一id,由對象的屬性描述,可以用來傳遞參數或對實體進行補充描述。
作為實體屬性的描述時,值對象也會被存儲。在uml的類圖上顯現為一對多或一對一的關系。在orm映射關系上需要采用較復雜的一對多或一對一關系映射。
關於實體與值對象的一個例子:比如員工信息的屬性,如住址,電話號碼都可以改變;然而,同一個員工的實體的標識將保持不變。因此,一個實體的基本概念是一個持續抽象的生命,可以變化不同的狀態和情形,但總是有相同的標識。
實體與值對象的區別
實體具有唯一標識,而值對象沒有唯一標識,這是實體和值對象間的最大不同。
實體就是領域中需要唯一標識的領域概念。有兩個實體,如果唯一標識不一樣,那么即便實體的其他所有屬性都一樣,也認為是兩個不同的實體;一個實體的基本概念是一個持續抽象的生命,可以變化不同的狀態和情形,但總是有相同的標識。
不應該給實體定義太多的屬性或行為,而應該尋找關聯,發現其他一些實體或值對象,將屬性或行為轉移到其他關聯的實體或值對象上。
如果兩個對象的所有的屬性的值都相同,我們會認為它們是同一個對象的話,那么我們就可以把這種對象設計為值對象。值對象在判斷是否是同一個對象時是通過它們的所有屬性是否相同,如果相同則認為是同一個值對象;而實體是否為同一個實體的區分,只是看實體的唯一標識是否相同,而不管實體的屬性是否相同。
值對象另外一個明顯的特征是不可變,即所有屬性都是只讀的。因為屬性是只讀的,所以可以被安全的共享;當共享值對象時,一般有復制和共享兩種做法,具體采用哪種做法還要根據實際情況而定。
箴言:如果值對象時可共享的,它們應該是不可變的。(值對象應該保持盡量的簡單)
值對象的設計應盡量簡單,不要讓它引用很多其他的對象,因為本質上講值對象只是代表一個值。
3.聚合及聚合根(aggregate、aggregate root):
聚合是用來定義領域對象所有權和邊界的領域模式。聚合的作用是幫助簡化模型對象間的關系。聚合,它通過定義對象之間清晰的所屬關系和邊界來實現領域模型的內聚,並避免了錯綜復雜的難以維護的對象關系網的形成。聚合定義了一組具有內聚關系的相關對象的集合,我們把聚合看作是一個修改數據的單元。
划分aggregation是對領域模型的進一步深化,aggregation能闡釋領域模型內部對象之間的深層關聯.對aggregation的划分會直接映射到程序結構上.比如:ddd推薦按aggregation設計model的子包.每個aggregation配備一個repository.aggregation內部的非root對象是通過導航獲得的.
一個聚合是一組相關的被視為整體的對象。每個聚合都有一個根對象(聚合根實體),從外部訪問只能通過這個對象。根實體對象有組成聚合所有對象的引用,但是外部對象只能引用根對象實體。
只有聚合根才能使用倉儲庫直接查詢,其它的只能通過相關的聚合訪問。如果根實體被刪除,聚合內部的其它對象也將被刪除。
通常,我們把聚合組織到一個文件夾或一個包中。每一個聚集對應一個包,並且每個聚集成員包括實體、值對象,domain事件,倉儲接口和其它工廠對象。
聚合有以下一些特點:
1. 每個聚合有一個根和一個邊界,邊界定義了一個聚合內部有哪些實體或值對象,根是聚合內的某個實體;
2. 聚合內部的對象之間可以相互引用,但是聚合外部如果要訪問聚合內部的對象時,必須通過聚合根開始導航,絕對不能繞過聚合根直接訪問聚合內的對象,也就是說聚合根是外部可以保持對它的引用的唯一元素;
3. 聚合內除根以外的其他實體的唯一標識都是本地標識,也就是只要在聚合內部保持唯一即可,因為它們總是從屬於這個聚合的;
4. 聚合根負責與外部其他對象打交道並維護自己內部的業務規則;
5. 基於聚合的以上概念,我們可以推論出從數據庫查詢時的單元也是以聚合為一個單元,也就是說我們不能直接查詢聚合內部的某個非根的對象;
6. 聚合內部的對象可以保持對其他聚合根的引用;
7. 刪除一個聚合根時必須同時刪除該聚合內的所有相關對象,因為他們都同屬於一個聚合,是一個完整的概念。
如何識別聚合?
聚合中的對象關系是內聚的,即這些對象之間必須保持一個固定規則,固定規則是指在數據變化時必須保持不變的一致性規則。
當我們在修改一個聚合時,我們必須在事務級別確保整個聚合內的所有對象滿足這個固定規則。
作為一條建議,聚合盡量不要太大,否則即便能夠做到在事務級別保持聚合的業務規則完整性,也可能會帶來一定的性能問題。
有分析報告顯示,通常在大部分領域模型中,有70%的聚合通常只有一個實體,即聚合根,該實體內部沒有包含其他實體,只包含一些值對象;另外30%的聚合中,基本上也只包含兩到三個實體。這意味着大部分的聚合都只是一個實體,該實體同時也是聚合根。
如何識別聚合根?
如果一個聚合只有一個實體,那么這個實體就是聚合根;如果有多個實體,可以思考聚合內哪個對象有獨立存在的意義並且可以和外部直接進行交互。
並不是所有的實體都是聚集根,但只有實體才能成為聚集根。
4.工廠(factories):
工廠用來封裝創建一個復雜對象尤其是聚合時所需的知識,作用是將創建對象的細節隱藏起來。客戶傳遞給工廠一些簡單的參數,然后工廠可以在內部創建出一個復雜的領域對象然后返回給客戶。當創建 實體和值對象復雜時建議使用工廠模式。
不意味着我們一定要使用工廠模式。如果創建對象很簡單,使用構造器或者控制反轉/依賴注入容器足夠創建對象的依賴。此時,我們就不需要通用工廠模式來創建實體或值對象。
良好工廠的要求:
每個創建方法都是原子的。一個工廠應該只能生產透明狀態的對象。對於實體,意味着創建整個聚合時滿足所有的不變量。
一個單獨的工廠通常生產整個聚合,傳出一個根實體的引用,確保聚合的不變量都有。如果對象的內部聚合需要工廠,通常工廠方法的邏輯放在在聚合根上。這樣對外部隱藏了聚合內聚的實現,同時賦予了根確保聚合完整的職責。如果聚合根不是子實體工廠的合適的家,那么繼續創建一個單獨的工廠。
5.倉儲(repositories):
倉儲是用來管理實體的集合。
倉儲里面存放的對象一定是聚合,原因是domain是以聚合的概念來划分邊界的;聚合作為一個整體概念,要么一起被取出來,要么一起被刪除。外部訪問不會單獨對某個聚合內的子對象進行單獨操作。因此,我們只對聚合設計倉儲。
倉儲還有一個重要的特征就是分為倉儲定義部分和倉儲實現部分,我們在領域模型中定義倉儲的接口,而在基礎設施層實現具體的倉儲。也符合按照接口分離模式在領域層定義倉儲庫接口的原則。
注意:repositories本身是一種領域組件,但repositories的實現卻不是領域層中的。
respositories和dao:
dao和repository在領域驅動設計中都很重要。dao是面向數據訪問的,是關系型數據庫和應用之間的契約。
repository:位於領域層,面向aggregation root。repository是一個獨立的抽象,使用領域的通用語言,它與dao進行交互,並使用領域理解的語言提供對領域模型的數據訪問服務的“業務接口”。
dao方法是細粒度的,更接近數據庫,而repository方法的粒度粗一些,而且更接近領域。領域對象應該只依賴於repository接口。客戶端應該始終調用領域對象,領域對象再調用dao將數據持久化到數據 存儲中。
處理領域對象之間的依賴關系(比如實體及其repository之間的依賴關系)是開發人員經常遇到的典型問題。解決這個問題通 常的設計方案是讓服務類或外觀類直接調用repository,在調用repository的時候返回實體對象給客戶端。
6.服務(services):
服務這個詞在服務模式中是這么定義的:服務提供的操作是它提供給使用它的客戶端,並突出領域對象的關系。
所有的service只負責協調並委派業務邏輯給領域對象進行處理,其本身並真正實現業務邏輯,絕大部分的業務邏輯都由領域對象承載和實現了。
service可與多種組件進行交互,這些組件包括:其他的service、領域對象和repository 或 dao。
通常,應用中一般包括:domain模型服務和應用層服務:
* domain services encapsulate domain concepts that just are not naturally modeled as things.
* application services constitute the application, or service, layer.
當一個領域操作被視為一個重要的領域概念,一般就應該作為領域服務。 服務應該是無狀態的。
設計實現領域服務來協調業務邏輯,只在領域服務中實現領域邏輯的調用。
領域服務邏輯須以非常干凈簡潔的代碼實現。因此,我們必須實現對領域低層組件的調用。通常應用的調用,例如倉儲庫的調用,創建事務等,不應該在這里實現。這些操作應該在應用層實現。
通常服務對象名稱中都應包含一個動詞。 service接口的傳入傳出參數也都應該是dto,可能包含的工作有領域對象和dto的互轉換以及事務。
服務的3個特征:
a. 服務執行的操作涉及一個領域概念,這個領域概念通常不屬於一個實體或者值對象
b. 被執行的操作涉及到領域中其它的對象
c. 操作時無狀態的
推薦:最好顯式聲明服務,因為它創建了領域中一個清晰的特性,封裝了一個概念領域層服務和基礎設施層服務:均建立在領域實體和值對象的上層,以便直接為這些相關的對象提供所需的服務;
領域服務與domain對象的區別
一般的領域對象都是有狀態和行為的,而領域服務沒有狀態只有行為。需要強調的是領域服務是無狀態的,它存在的意義就是協調領域對象共同完成某個操作,所有的狀態還是都保存在相應的領域對象中。
通常,對開發人員來說創建不應該存在的服務相當容易;要么在服務中包含了本應存在於領域對象中的領域邏輯,要么扮演了缺失的領域對象角色,而這些領域對象並沒有作為模型的一部分去創建。
7.domain事件
domain event模式最初由udi dahan提出,發表在自己的博客上:http://www.udidahan.com/2009/06/14/domain-events-salvation/
企業級應用程序事件大致可以分為三類:系統事件、應用事件和領域事件。領域事件的觸發點在領域模型(domain model)中。它的作用是將領域對象從對repository或service的依賴中解脫出來,避免讓領域對象對這些設施產生直接依賴。它的做法就是當領域對象的業務方法需要依賴到這些對象時就發出一個事件,這個事件會被相應的對象監聽到並做出處理。
通過使用領域事件,我們可以實現領域模型對象狀態的異步更新、外部系統接口的委托調用,以及通過事件派發機制實現系統集成。另外,領域事件本身具有自描述性。它不僅能夠表述系統發生了什么事情,而且還能夠描述發生事件的動機。
domain事件也用表進行存儲。
8.DTO
dto- datatransfer object(數據傳輸對象):dto在設計之初的主要考量是以粗粒度的數據結構減少網絡通信並簡化調用接口。
領域驅動架構與n層架構設計
領域驅動架構
eric evans的“領域驅動設計- 應對軟件的復雜性“一書中描述和解釋了建議的n層架構高層次的圖:
user interface:
該層包含與其他系統/客戶進行交互的接口與通信設施,在多數應用里,該層可能提供包括web services、rmi或rest等在內的一種或多種通信接口。該層主要由facade、dto和assembler三類組件構成,三類組件均是典型的j2ee模式。
dto的作用最初主要是以粗粒度的數據結構減少網絡通信並簡化調用接口。在領域驅動設計中,采用dto模型,可以起到隱藏領域細節,幫助實現獨立封閉的領域模型的作用。
dto與領域對象之間的相互轉換工作多由assembler承擔,也有一些系統使用反射機制自動實現dto與領域對象之間的相互轉換,如apache common beanutils。
facade的用意在於為遠程客戶端提供粗粒度的調用接口。facade本身不處理任何的業務邏輯,它的主要工作就是將一個用戶請求委派給一個或多個service進行處理,同時借助assembler將service傳入或傳出的領域對象轉化為dto進行傳輸。
application:
application層中主要組件就是service。這里需要注意的是,service的組織粒度和接口設計依據與傳統transaction script風格的service是一致的,但是兩者的實現卻有質的區別。
transaction script(事務腳本)的核心是過程,通過過程的調用來組織業務邏輯,業務邏輯在服務(service)層進行處理。大部分業務應用都可以被看成一系列事務。
transaction script的特點是簡單容易理解,面向過程設計。 如果應用相對簡單,在應用的生命周期里不會有基礎設施技術的改變,尤其是業務邏輯很少會變動,采用transaction script風格簡單自然,性能良好,容易理解。
transaction script的缺點在於,對於復雜的業務邏輯難以保持良好的設計,事務之間的冗余代碼不斷增多。應用架構容易出現“胖服務層”和“貧血的領域模型”。同時,service層積聚越來越多的業務邏輯,導致可維護性和擴展性變差
領域模型屬於面向對象設計,領域模型具備自己的屬性行為和狀態,領域對象元素之間通過聚合配合解決實際業務應用。可復用,可維護,易擴展,可以采用合適的設計模型進行詳細設計。缺點是相對復雜,要求設計人員有良好的抽象能力。
transactionscript風格業務邏輯主要在service中實現,而在領域驅動設計的架構里,service只負責協調並委派業務邏輯給領域對象進行處理。因此,我們可以考察這一點來識別系統是transaction script架構還是domain model架構。在實踐中,設計良好的領域設計架構在開發過程中也容易向transaction script架構演變。
domain:
domain層是整個系統的核心層,該層維護一個使用面向對象技術實現的領域模型,幾乎全部的業務邏輯會在該層實現。domain層包含entity(實體)、valueobject(值對象)、domain event(領域事件)和repository(倉儲)等多種重要的領域組件。
infrastructure:
infrastructure(基礎設施層)為interfaces、application和domain三層提供支撐。所有與具體平台、框架相關的實現會在infrastructure中提供,避免三層特別是domain層摻雜進這些實現,從而“污染”領域模型。infrastructure中最常見的一類設施是對象持久化的具體實現。
n層架構設計
層(layers)被視為構成應用或服務的水平堆疊的一組邏輯上的組件。它們幫助區分完成不同任務的組件,提供一個最大化復用和可維護性的設計。簡言之,是關於在架構方面應用關注點分離的原則。 在傳統的多層架構中,每個解決方案的組件必須分隔到不同的層。每層的組件必須內聚而且有大約相同的抽象級別。每個一級層應該和其他的一級層松耦合。
從最底層的抽象級別看,例如第1層。這是系統的基礎層。這些抽象的步驟是一步一步的最后到最頂層。
多層應用的關鍵在於對依賴的管理。傳統的多層架構,層內的組件只能和同級或者低級層的組件交互。這有利於減少不同層內組件的依賴。通常有兩種多層架構的設計方法:嚴格和靈活的。 * “嚴格的層設計”限定層內的組件只能和同一層、或者下一層的組件通信。即第n層只能和第n-1層交互,n-1層只能和n-2層交互,等等。
* “靈活的層設計”允許層內的組件和任何低級別層交互。這種設計中,第n層可以和n-1,n-2層交互。
這種設計由於不需要對其他層進行重復的調用,從而可以提高性能。然而,這種設計不提供層之間的同層隔離級別,使得它難以在不影響多個高級層的時候替換一個低級的層。
由於層之間是通過定義明確的接口進行交互這一事實,很容易為各層添加替代的實現(例如 mock and stubs)。
因為高層的組件只能和底層的交互,在單獨的組件上進行測試是很容易的。
使用層的好處 - 功能容易確定位置,解決方案也就容易維護。層內高內聚,層間松耦合使得維護/組合層更容易。 - 其他的解決方案可以重用由不同層暴露的功能。 - 當項目按邏輯分層時,分布式的部署更容易實現。 - 把層分布到不同的物理層可以提高可伸縮性;然后這一步應該進行仔細的評估,因為可能對性能帶來負面影響。
面向領域架構的分層:
在面向領域架構中,關鍵是要清楚界定和分離領域模型層和其余的層。
領域驅動與項目開發
一般適合結合使用scrum(適用於項目管理)和xp(適用於軟件開發目標)方法對處理ddd實施項目。敏捷方法注重於交付商業價值,而ddd側重於結合軟件系統和業務模型。此 外,就ddd迭代的特性來說,scrum或dsdm這樣的敏捷方法對項目管理來說也是更好的框架。
ddd迭代周期的項目管理模型如圖所示。
本圖根據《domain driven design and development in practice》一文中插圖進行了部分修改。
領域建模結束時可以開始領域驅動設計。關於如何開始實現領域對象模型,ramnivas laddad推薦如下的步驟。他強調要更側重於領域模型中的領域對象,而不是服務。
* 從領域實體和領域邏輯開始。
* 不要一開始就從服務層開始,只添加那些邏輯不屬於任何領域實體或值對象的服務。
* 利用通用語言、契約式設計(dbc)、自動化測試、 ci和重構,使實現盡可能地與領域模型緊密結合。
設計領域模型的一般步驟:
1. 根據需求建立一個初步的領域模型,識別出一些明顯的領域概念以及它們的關聯,關聯可以暫時沒有方向但需要有(1:1,1:n,m:n)這些關系;可以用文字精確的沒有歧義的描述出每個領域概念的涵義以及包含的主要信息;
2. 分析主要的軟件應用程序功能,識別出主要的應用層的類;這樣有助於及早發現哪些是應用層的職責,哪些是領域層的職責;
3. 進一步分析領域模型,識別出哪些是實體,哪些是值對象,哪些是領域服務;
4. 分析關聯,通過對業務的更深入分析以及各種軟件設計原則及性能方面的權衡,明確關聯的方向或者去掉一些不需要的關聯;
5. 找出聚合邊界及聚合根,這是一件很有難度的事情;因為你在分析的過程中往往會碰到很多模棱兩可的難以清晰判斷的選擇問題,所以,需要我們平時一些分析經驗的積累才能找出正確的聚合根;
6. 為聚合根配備倉儲,一般情況下是為一個聚合分配一個倉儲,此時只要設計好倉儲的接口即可;
7. 走查場景,確定我們設計的領域模型能夠有效地解決業務需求;
8. 考慮如何創建領域實體或值對象,是通過工廠還是直接通過構造函數;
9. 停下來重構模型。尋找模型中覺得有些疑問或者是蹩腳的地方,比如思考一些對象應該通過關聯導航得到還是應該從倉儲獲取?聚合設計的是否正確?考慮模型的性能怎樣,等等;
領域建模是一個不斷重構,持續完善模型的過程,大家會在討論中將變化的部分反映到模型中,從而是模型不斷細化並朝正確的方向走。
從設計和實現的角度來看,典型的ddd框架應該支持以下特征。
* 應該是一個以pojo為基礎的架構。
* 應該支持使用ddd概念的業務領域模型的設計和實現。
* 應該支持像依賴注入(di)和面向方向編程(aop)這些概念的開箱即用。
* 與單元測試框架整合。
* 與其它java/java ee框架進行良好的集成,比如jpa、hibernate、toplink等。
一些反模式:
* 貧血的領域對象
* 重復的dao
* 肥服務層:服務類在這里最終會包含所有的業務邏輯。
* 依戀情結(feature envy):函數對某個類的興趣高過對自己所處類的興趣。
一些思考
1. 建立完整自封閉的領域模型。
領域驅動架構相對比較容易理解,但建立一個完整自封閉的領域模型卻很困難。“領域模型”是一個針對業務邏輯抽象的分析模型,它反映出對領域問題的整體描述。領域模型不是編程的實現模型,而是一組抽象概念的集合。一個領域概念不一定映射成一個類,也有可能會映射很多的類(包括多個實體或值對象)。領域需求的最初細節,在功能層面通過領域專家的討論得出。領域專家並不一定需要熟知軟件開發領域的知識,相反強調的是具有領域中的相關知識。領域需求在相互討論中不斷得到細化,還有可能在開發過程出現需求的反復或變更,這都要求領域模型的建立完善是一個反復重構的過程。敏捷開發是一種應對快速變化的需求的一種軟件開發能力。強調程序員團隊與業務專家之間的緊密協作、面對面的溝通(認為比書面的文檔更有效)、頻繁交付新的軟件版本、緊湊而自我組織型的團隊、能夠很好地適應需求變化的代碼編寫和團隊組織方法。故采用敏捷開發有利於領域模型的建立完善,以更能符合用戶的實際需求。
關於領域模型分析存在有多種分析方法。也許並不是能經常能有機會去實踐這些分析方法或分析領域模型。但關於領域驅動架構的理解,有助於幫助我們去理解領域驅動的設計,實現一些高內聚、低耦合的代碼實現。
2. 領域服務建模
建立和識別領域服務也比較容易出錯。通常的ssh分層架構與領域驅動架構相近,而ssh架構開發更容易導致transaction script架構而非領域驅動架構。在ssh分層架構上,開發人員更容易建立”貧血”模型,而在service里實現業務邏輯。而ddd強調“充血模型”,“薄”service層。建立領域服務需要識別出領域業務邏輯,並將業務實現到領域模型中。一方面,業務需求充滿着變化,在開發過程中難以把握。當業務不明需求不清時,“貧血模型”就更容易被人接受。另一方面,在構建領域模型時,orm映射顯示十分重要並且也非常復雜,包括類繼承體系與數據庫的映射,抓取策略和緩存管理在內的一系列問題等.“貧血模型”有時會簡化這種映射關系,同時,在處理對象依賴關系上顯得更加靈活性。而領域模型強調了領域邊界,對領域對象的訪問總是通過聚合根開始,在有時候,模型的某些遍歷會帶來更大的性能和穩定性上的問題。而解決這些問題時,又常常會從實效性上出發而犧牲模型個別的清晰性和純潔性。
3.領域對象、領域服務以及repository之間的互相依賴
在實際開發中,開發人員會經常需要處理領域對象之間的依賴關系,以及領域對象與repository間的依賴。通常可能的方案是讓service或façade直接調用repository,從而獲得返回的領域對象。這種方式導致各層間的依賴,通常我們應該考慮解耦這種依賴。當前實現組件解耦常用的技術無非是:控制反轉(ioc)、依賴注入(di)、面向方面編程(aop)以及分布式服務接口。因此,解決依賴的一種思路利用di或aop將repository和服務注入到領域對象中。spring框架提供了相關的機制。在spring環境中,利用spring實例化對象,注入依賴,將服務、repository等領域對象聯系起來。