前言
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)是一種軟件開發(fā)方法論,其核心思想是將業(yè)務(wù)領(lǐng)域的知識(shí)和業(yè)務(wù)邏輯融入到軟件設(shè)計(jì)和開發(fā)中,以實(shí)現(xiàn)更加符合業(yè)務(wù)需求和更易于維護(hù)的軟件系統(tǒng)。
在傳統(tǒng)的軟件開發(fā)方法中,開發(fā)人員往往關(guān)注的是技術(shù)實(shí)現(xiàn)而非業(yè)務(wù)領(lǐng)域本身。而在DDD的方法論中,開發(fā)人員需要與業(yè)務(wù)專家緊密合作,深入了解業(yè)務(wù)領(lǐng)域,將業(yè)務(wù)領(lǐng)域的知識(shí)和業(yè)務(wù)邏輯轉(zhuǎn)化為軟件設(shè)計(jì)和開發(fā)中的概念和實(shí)現(xiàn)。
DDD的核心概念包括:
- 領(lǐng)域模型:對業(yè)務(wù)領(lǐng)域進(jìn)行建模,將業(yè)務(wù)領(lǐng)域中的概念和規(guī)則轉(zhuǎn)化為軟件系統(tǒng)中的對象和方法。
- 實(shí)體:具有唯一標(biāo)識(shí)和生命周期的領(lǐng)域?qū)ο蟆?/li>
- 值對象:沒有唯一標(biāo)識(shí)和生命周期的領(lǐng)域?qū)ο蟆?/li>
- 聚合:一組具有內(nèi)聚性的實(shí)體和值對象的集合。
- 領(lǐng)域服務(wù):對領(lǐng)域模型的操作和行為進(jìn)行抽象和封裝的服務(wù)。
- 限界上下文:領(lǐng)域模型的上下文邊界,規(guī)定了領(lǐng)域模型中的概念和規(guī)則的適用范圍。
通過將業(yè)務(wù)領(lǐng)域的知識(shí)和業(yè)務(wù)邏輯融入到軟件設(shè)計(jì)和開發(fā)中,DDD可以幫助開發(fā)人員實(shí)現(xiàn)更加符合業(yè)務(wù)需求的軟件系統(tǒng),并提高軟件系統(tǒng)的可維護(hù)性、可擴(kuò)展性和可測試性。
一、從六邊形架構(gòu)談起
六邊形架構(gòu)是一種軟件架構(gòu),用于為每種外部類型提供一個(gè)適配器。它可以幫助我們從新的角度來看待整個(gè)系統(tǒng),并將系統(tǒng)分為外部區(qū)域和內(nèi)部區(qū)域兩個(gè)部分。
外部區(qū)域是指處理不同客戶端的輸入請求的部分。它包含了各種適配器,用于將不同類型的客戶輸入轉(zhuǎn)化為程序內(nèi)部API所理解的輸入。在六邊形架構(gòu)中,每種類型的客戶都有自己的適配器。其中,每種適配器對應(yīng)著一個(gè)不同種類型的端口,端口要么處理輸入,要么處理輸出。無論采用哪種方式對端口進(jìn)行劃分,當(dāng)客戶請求到達(dá)時(shí),都應(yīng)該有相應(yīng)的適配器對輸入進(jìn)行轉(zhuǎn)化,然后適配器將調(diào)用應(yīng)用程序的某個(gè)操作或者向應(yīng)用程序發(fā)送一個(gè)事件,控制權(quán)由此交給內(nèi)部區(qū)域。
內(nèi)部區(qū)域是指負(fù)責(zé)處理持久化數(shù)據(jù)并對程序輸出進(jìn)行存儲(chǔ)和轉(zhuǎn)發(fā)的部分。它包含了應(yīng)用層,領(lǐng)域?qū)雍突A(chǔ)設(shè)施層。
- 應(yīng)用層 是整個(gè)系統(tǒng)的業(yè)務(wù)邏輯層,它負(fù)責(zé)接收用戶的請求,調(diào)用領(lǐng)域?qū)幽P秃头?wù)完成業(yè)務(wù)邏輯,然后將結(jié)果返回給用戶接口層。應(yīng)用層可以包含如下內(nèi)容:service、command、query、dto和mq等。
- 領(lǐng)域?qū)?/strong> 是整個(gè)系統(tǒng)的核心,它包含了系統(tǒng)的業(yè)務(wù)規(guī)則和業(yè)務(wù)邏輯。領(lǐng)域?qū)拥暮诵氖穷I(lǐng)域模型,它是對業(yè)務(wù)領(lǐng)域的建模。領(lǐng)域?qū)涌梢园缦聝?nèi)容:model、service、repository、event和facade等。
- 基礎(chǔ)設(shè)施層 是整個(gè)系統(tǒng)的基礎(chǔ)設(shè)施,它包含了與具體技術(shù)相關(guān)的代碼和邏輯。基礎(chǔ)設(shè)施層可以包含如下內(nèi)容:dal、mapper和factory等。
六邊形架構(gòu)是一種非常靈活和通用的架構(gòu),可以幫助我們更好地組織和管理大型軟件系統(tǒng)。

二、依賴倒置
依賴倒置原則(DIP)由Robert C. Martin提出,其核心定義如下:
- 高層模塊不應(yīng)該依賴于底層模塊,兩者都應(yīng)該依賴于抽象。
- 抽象不應(yīng)該依賴于實(shí)現(xiàn)細(xì)節(jié),實(shí)現(xiàn)細(xì)節(jié)應(yīng)該依賴于接口。
根據(jù)DIP原則,領(lǐng)域?qū)涌梢圆辉僖蕾囉诨A(chǔ)設(shè)施層,基礎(chǔ)設(shè)施層通過注入持久化實(shí)現(xiàn)完成對領(lǐng)域?qū)拥慕怦?。采用依賴注入原則的新分層架構(gòu)模型如下:

采用依賴注入方式后,我們可以發(fā)現(xiàn)實(shí)際上已經(jīng)沒有分層概念了。無論是高層還是底層,都只依賴于抽象,整個(gè)分層結(jié)構(gòu)被推平了。
三、聚合
在DDD中,聚合是指一組具有內(nèi)聚性的實(shí)體和值對象的集合。它們共同形成了一個(gè)有邊界的上下文,這個(gè)上下文可以看作是一個(gè)單個(gè)的單元。這個(gè)單元可以通過聚合根(Aggregate Root)進(jìn)行訪問和修改,聚合根是聚合中的唯一訪問點(diǎn)。聚合根擔(dān)任了保護(hù)聚合內(nèi)部完整性和一致性的角色。
聚合的設(shè)計(jì)目的是保持領(lǐng)域模型的內(nèi)聚性和一致性。將相關(guān)實(shí)體和值對象聚合在一起,可以更好地保護(hù)和管理領(lǐng)域模型,減少了并發(fā)沖突的可能性,提高了系統(tǒng)的可維護(hù)性。
在聚合內(nèi)部,實(shí)體和值對象的訪問應(yīng)該受到限制,只能通過聚合根進(jìn)行訪問。這個(gè)限制可以通過使用封裝和訪問控制等技術(shù)來實(shí)現(xiàn)。聚合根應(yīng)該提供足夠的方法來支持聚合的業(yè)務(wù)需求,同時(shí)也應(yīng)該避免暴露過多的實(shí)現(xiàn)細(xì)節(jié)。
在設(shè)計(jì)聚合時(shí),需要注意以下幾個(gè)原則:
- 通過一致性邊界內(nèi)建模真正的不變條件來封裝實(shí)體的不變性,實(shí)現(xiàn)對象數(shù)據(jù)的一致性。該原則保證了聚合的業(yè)務(wù)高內(nèi)聚性。
- 采用設(shè)計(jì)小聚合的方式來降低實(shí)體之間管理的復(fù)雜性,避免高并發(fā)操作帶來的沖突和數(shù)據(jù)庫鎖等問題。小聚合設(shè)計(jì)也提高了領(lǐng)域模型的適應(yīng)性,以適應(yīng)業(yè)務(wù)變化。
- 通過唯一標(biāo)識(shí)引用其它聚合,避免直接對象引用的方式。該原則減少了聚合之間的耦合度,避免聚合邊界不清晰的問題。
- 在邊界之外使用最終一致性,保證聚合內(nèi)部數(shù)據(jù)的強(qiáng)一致性,而聚合之間數(shù)據(jù)的最終一致性。該原則通過異步修改相關(guān)聚合的領(lǐng)域事件來實(shí)現(xiàn)聚合之間的解耦。
- 通過應(yīng)用層實(shí)現(xiàn)跨聚合的服務(wù)調(diào)用,避免跨聚合的領(lǐng)域服務(wù)調(diào)用和跨聚合的數(shù)據(jù)庫表關(guān)聯(lián)。該原則實(shí)現(xiàn)了微服務(wù)內(nèi)聚合之間的解耦,支持未來以聚合為單位的微服務(wù)組合和拆分。
聚合根、實(shí)體、值對象
在DDD中,聚合是一組相關(guān)的對象的集合,它們具有內(nèi)在的一致性和完整性規(guī)則,并且共享一個(gè)邊界。
- 聚合根:
ProductAggregateRoot- 聚合根是聚合中的唯一訪問點(diǎn),擔(dān)任了保護(hù)聚合內(nèi)部完整性和一致性的角色。在本示例中,
ProductAggregateRoot是整個(gè)聚合的入口,提供了增刪改查等操作。 - 聚合根是聚合中最重要的對象,因?yàn)樗蔷酆系倪吔?,也是聚合?nèi)部所有對象之間的協(xié)調(diào)者。
- 聚合根可以包含多個(gè)實(shí)體和值對象。實(shí)體和值對象都是聚合根的子對象。
- 聚合根是聚合中的唯一訪問點(diǎn),擔(dān)任了保護(hù)聚合內(nèi)部完整性和一致性的角色。在本示例中,
- 實(shí)體:
Product- 實(shí)體是具有唯一標(biāo)識(shí)和生命周期的領(lǐng)域?qū)ο?。在本示例中?code>Product表示商品對象。
- 實(shí)體是聚合中最重要的對象之一,因?yàn)樗鼈兪蔷酆系暮诵暮椭饕獏⑴c者。
- 實(shí)體可以包含多個(gè)值對象。值對象通常作為實(shí)體的屬性存在,用于描述實(shí)體的某個(gè)方面。
- 實(shí)體之間可以相互引用。這種引用通常是通過聚合根進(jìn)行的,以確保聚合根在協(xié)調(diào)實(shí)體之間的關(guān)系時(shí)發(fā)揮其作用。
- 值對象:
ProductSpec- 值對象是沒有唯一標(biāo)識(shí)和生命周期的領(lǐng)域?qū)ο?。在本示例中?code>ProductSpec表示商品規(guī)格對象。
- 值對象通常用于描述實(shí)體的某個(gè)方面,例如商品的重量、顏色、尺寸等等。
- 值對象不能單獨(dú)存在,它們總是作為實(shí)體的屬性存在。
- 值對象的相等性僅由其屬性值決定,不同值對象之間可以相等。這意味著如果兩個(gè)值對象的屬性值相同,它們被認(rèn)為是相等的,即使它們不是同一個(gè)對象。
四、DDD 分層
整體架構(gòu)圖

整體代碼結(jié)構(gòu)
ddd-domin
├── pom.xml
└── src
└── main
└── java
└── org
└── example
├── App.java
├── adapter
│ ├── market
│ └── product
│ ├── kafka
│ │ └── ProductConsumer.java
│ ├── socket
│ │ └── ProductSocket.java
│ └── web
│ └── ProductController.java
├── application
│ ├── event
│ │ ├── EventManager.java
│ │ └── IEvent.java
│ ├── market
│ └── product
│ ├── ProductFactory.java
│ ├── ProductService.java
│ ├── command
│ │ ├── AddCountProductCommand.java
│ │ └── CreateProductCommand.java
│ ├── dto
│ │ └── ProductDTO.java
│ ├── event
│ │ ├── AbstractProductEvent.java
│ │ ├── AddNumProjectEvent.java
│ │ └── CreateProjectEvent.java
│ └── mapstruct
│ └── ProductStruct.java
├── common
│ └── exception
│ └── BusException.java
├── domain
│ ├── market
│ ├── product
│ │ └── Product.java
│ └── repository
│ ├── IProductRepository.java
│ └── entity
│ └── ProductEntity.java
└── infrastructure
├── cache
├── repository
│ └── impl
│ └── ProductRepositoryImpl.java
└── sqlmapper
3.1 用戶接口層
用戶接口層作為對外的門戶,將網(wǎng)絡(luò)協(xié)議與業(yè)務(wù)邏輯解耦。它是整個(gè)系統(tǒng)的重要組成部分,可以包含如下內(nèi)容:
- 鑒權(quán):驗(yàn)證用戶的身份和權(quán)限,保證系統(tǒng)的安全性。
- Session管理:管理用戶的會(huì)話,保證用戶操作的一致性。
- 限流:限制用戶的訪問頻率,保證系統(tǒng)的穩(wěn)定性。
- 異常處理:處理用戶請求過程中可能出現(xiàn)的異常,保證系統(tǒng)的健壯性。
在項(xiàng)目應(yīng)用中,用戶接口層僅負(fù)責(zé)封裝協(xié)議請求的處理,鑒權(quán)、Session管理和限流等其他任務(wù)則由獨(dú)立的應(yīng)用程序負(fù)責(zé)。這種設(shè)計(jì)方法確保了用戶接口層不會(huì)負(fù)擔(dān)過多的額外職責(zé),使其可以專注于處理請求和響應(yīng)。通過將鑒權(quán)、Session管理和限流等任務(wù)委托給獨(dú)立的應(yīng)用程序,整個(gè)系統(tǒng)可以更好地組織和更加靈活,從而更易于維護(hù)和擴(kuò)展。
3.2 應(yīng)用層
應(yīng)用層是整個(gè)系統(tǒng)的業(yè)務(wù)邏輯層,是領(lǐng)域?qū)雍陀脩艚涌趯又g的橋梁。應(yīng)用層接收用戶的請求,調(diào)用領(lǐng)域?qū)幽P秃头?wù)完成業(yè)務(wù)邏輯,然后將結(jié)果返回給用戶接口層。應(yīng)用層可以包含如下內(nèi)容:
- service:應(yīng)用層服務(wù)接口定義。它定義了應(yīng)用層的服務(wù)接口,包括對外提供的方法和參數(shù)等。
- command:命令定義,例如訂單創(chuàng)建、商品更新等。它定義了應(yīng)用層的命令類型,包括命令的名稱、參數(shù)等。
- query:查詢定義,例如商品詳情查詢、訂單列表查詢等。它定義了應(yīng)用層的查詢類型,包括查詢的名稱、參數(shù)等。
- dto:數(shù)據(jù)傳輸對象定義。它定義了應(yīng)用層的數(shù)據(jù)傳輸對象,包括數(shù)據(jù)的類型、屬性等。
- mq:消息隊(duì)列定義。它定義了應(yīng)用層的消息隊(duì)列,包括消息的類型、屬性等。
3.3 領(lǐng)域?qū)?/h3>
領(lǐng)域?qū)邮钦麄€(gè)系統(tǒng)的核心,它包含了系統(tǒng)的業(yè)務(wù)規(guī)則和業(yè)務(wù)邏輯。領(lǐng)域模型是領(lǐng)域?qū)拥暮诵?,它是對業(yè)務(wù)領(lǐng)域的建模。領(lǐng)域?qū)涌梢园缦聝?nèi)容:
- model:領(lǐng)域模型定義。它定義了領(lǐng)域模型的類型、屬性等。
- service:領(lǐng)域服務(wù)接口定義。它定義了領(lǐng)域?qū)拥姆?wù)接口,包括對外提供的方法和參數(shù)等。
- repository:領(lǐng)域倉儲(chǔ)接口定義。它定義了領(lǐng)域?qū)拥膫}儲(chǔ)接口,包括對數(shù)據(jù)的讀取、寫入等操作。
- event:領(lǐng)域事件定義。它定義了領(lǐng)域?qū)拥氖录愋?,包括事件的名稱、參數(shù)等。
- facade:領(lǐng)域門面定義,負(fù)責(zé)領(lǐng)域模型和應(yīng)用層服務(wù)的協(xié)調(diào)。它定義了領(lǐng)域?qū)拥拈T面類型,包括門面的名稱、方法等。
在我們的項(xiàng)目中,我們遵循領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的方法,并在不同層之間進(jìn)行了明確的關(guān)注點(diǎn)分離。Repository 層僅負(fù)責(zé)處理聚合根相關(guān)的操作,而不是查詢。因此,我們決定將查詢從應(yīng)用層直接連接到基礎(chǔ)設(shè)施層。通過這樣做,我們確保領(lǐng)域?qū)涌梢愿鼘W⒂趯?shí)體操作,而不會(huì)被查詢所干擾。
將查詢移到基礎(chǔ)設(shè)施層中允許我們在實(shí)現(xiàn)和可擴(kuò)展性方面具有更大的靈活性。我們可以選擇最適合處理查詢的技術(shù),并優(yōu)化系統(tǒng)的性能。
總的來說,這個(gè)設(shè)計(jì)決策通過分離職責(zé)并確保每一層都有明確的目的,提供了一個(gè)更健壯和可維護(hù)的系統(tǒng)。
3.4 基礎(chǔ)設(shè)施層
基礎(chǔ)設(shè)施層是整個(gè)系統(tǒng)的基礎(chǔ)設(shè)施,它包含了與具體技術(shù)相關(guān)的代碼和邏輯。基礎(chǔ)設(shè)施層可以包含如下內(nèi)容:
- dal:數(shù)據(jù)訪問層,包括DO和DAO。它定義了數(shù)據(jù)訪問層的類型、屬性等。
- mapper:數(shù)據(jù)映射定義。它定義了數(shù)據(jù)映射的類型、屬性等。
- factory:工廠定義,例如對象工廠、配置工廠等。它定義了工廠的類型、屬性等。