天天看點

DDD(領域驅動設計)總結

基本概念:

  領域驅動設計(簡稱 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層架構高層次的圖:

DDD(領域驅動設計)總結

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疊代周期的項目管理模型如圖所示。

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等領域對象聯系起來。

繼續閱讀