DDD的奇幻世界:從小積木到藝術(shù)品的設(shè)計(jì)之旅
1. 背景
DDD 是一個(gè)門檻很高的設(shè)計(jì)方法,里面涉及眾多概念,各概念間相互關(guān)聯(lián)相互制約,大大增加了落地的難度。但,當(dāng)真正落地之后,你會(huì)發(fā)現(xiàn)還是有很多技巧能大幅降低學(xué)習(xí)成本,實(shí)現(xiàn)快速上手。
學(xué)習(xí) DDD 最關(guān)鍵的一點(diǎn)便是:使用面向?qū)ο笏季S去思考問(wèn)題。
這個(gè)說(shuō)起來(lái)很抽象,面向?qū)ο笃鋵?shí)很簡(jiǎn)單,就像孩子們玩的樂(lè)高積木:
- 【組件】每個(gè)小積木都有自己的形狀(對(duì)象自身功能),也都有自己的凸起或插槽(對(duì)象暴露的接口或能力);
- 【關(guān)系】多個(gè)小積木可以組裝成一個(gè)大積木,多個(gè)大積木可以組裝成更大的積木(通過(guò)對(duì)象間的組合實(shí)現(xiàn)更強(qiáng)大的功能);
- 【功能】多個(gè)大積木最終組成“藝術(shù)品”(通過(guò)對(duì)象間的協(xié)作實(shí)現(xiàn)某個(gè)功能);
- 在 DDD 中也是一樣的:
- 【組件】聚合根是DDD中最核心的組件,對(duì)內(nèi)維護(hù)高內(nèi)聚的對(duì)象集合,對(duì)外提供原子業(yè)務(wù)能力;
- 【關(guān)系】應(yīng)用服務(wù)、領(lǐng)域服務(wù)、領(lǐng)域事件 對(duì)多個(gè)組件進(jìn)行編排,實(shí)現(xiàn)業(yè)務(wù)流程;
- 【功能】領(lǐng)域內(nèi)部能力 通過(guò) 應(yīng)用服務(wù) 暴露給外部調(diào)用者,從而滿足業(yè)務(wù)需求;
2. 原子能力
這里的原子能力主要指的是聚合根所提供的業(yè)務(wù)方法,從業(yè)務(wù)或技術(shù)視角都是不可拆分的最小操作單元。
2.1. 聚合根
在 DDD 中,聚合根是一個(gè)重要的概念,它是一組具有內(nèi)在一致性的相關(guān)對(duì)象的根,用來(lái)限制對(duì)象的邊界,可以保證聚合內(nèi)部的對(duì)象間關(guān)聯(lián)關(guān)系和業(yè)務(wù)規(guī)則得到統(tǒng)一的管理和維護(hù)。
聚合根是聚合的一個(gè)實(shí)體,作為整個(gè)聚合的唯一入口點(diǎn),通過(guò)它才能訪問(wèn)整個(gè)聚合。在DDD中,聚合被定義為一組相關(guān)對(duì)象的集合,這些對(duì)象在業(yè)務(wù)上有著緊密的聯(lián)系,需要被當(dāng)作一個(gè)整體來(lái)對(duì)待。聚合根是這個(gè)整體的根節(jié)點(diǎn),它負(fù)責(zé)維護(hù)整個(gè)聚合的完整性和一致性。
很抽象,讓我們看個(gè)示例:
電商訂單系統(tǒng)主要由以下幾個(gè)實(shí)體組成:
- 訂單(Order)。記錄用戶的一次生單,主要保存用戶、支付金額、訂單狀態(tài)等;
- 訂單項(xiàng)(OrderItem)。購(gòu)買的單個(gè)商品,主要保存商品單價(jià)、售價(jià)、應(yīng)付金額等;
- 支付記錄(Pay)。用戶的支付信息,包括支付渠道、支付金額、支付時(shí)間等;
- 收貨地址(Address)。用戶的收貨地址;
這幾個(gè)實(shí)體存在非常強(qiáng)的一致性保障,特別是在金額方面:
- 訂單的支付金額等于所有訂單項(xiàng)金額總和;
- 支付記錄的待支付金額必須與訂單支付金額一致;
如何保障訂單、訂單項(xiàng)、支付記錄上的金額是強(qiáng)一致的呢?小心編碼+謹(jǐn)慎測(cè)試?
那加入更多的應(yīng)用場(chǎng)景又該怎么處理呢?比如 優(yōu)惠券、優(yōu)惠活動(dòng)、手工改價(jià)、調(diào)整快遞費(fèi)用等,這將變成一個(gè)燙手山芋。
解決方案便是將這幾個(gè)實(shí)體作為一個(gè)整體來(lái)思考,也就是聚合的概念。
聚合是DDD的一種設(shè)計(jì)模式,它的本質(zhì)是建立比對(duì)象粒度更大的邊界,聚合了那些緊密聯(lián)系的對(duì)象,形成了一個(gè)業(yè)務(wù)上的整體。
圖片
如上圖所示:
- Order、OrderItem、Pay 和 Address 不在單獨(dú)處理,而是組成了一個(gè)更大的對(duì)象,也就是聚合;
- Order 是這個(gè)聚合的聚合根,對(duì)內(nèi)協(xié)調(diào)各個(gè)對(duì)象,對(duì)外提供唯一的訪問(wèn)入口;
- OrderItem、Pay、Address 作為非聚合根的內(nèi)部實(shí)體,不可直接對(duì)外提供服務(wù),僅接受 Order 的調(diào)用;
這樣的調(diào)整,能否保障 Order、OrderItem、Pay 三者間的強(qiáng)一致關(guān)系呢?讓我們從代碼層面進(jìn)行細(xì)致分析:
生單:
// 靜態(tài)工廠,封裝復(fù)雜的 Order 創(chuàng)建邏輯,并保障創(chuàng)建的 Order 對(duì)象是有效的
public static Order create(CreateOrderCommand createOrderCommand) {
Order order = new Order(createOrderCommand.getUserId());
order.setAddress(Address.create(createOrderCommand));
order.setPay(new Pay());
order.addItems(createOrderCommand.getItems());
order.init();
return order;
}
// 添加 OrderItem,并計(jì)算總金額
private void addItems(List<OrderItemDTO> items) {
if (!CollectionUtils.isEmpty(items)){
items.forEach(item ->{
// orderItem.setPrice(orderItem.getPrice() * orderItem.getQuantity());
OrderItem orderItem = OrderItem.create(item);
this.orderItems.add(orderItem);
this.totalSellingPrice += item.getPrice();
});
}
this.totalPrice = totalSellingPrice;
this.pay.updatePrice(this.totalPrice);
}
// 設(shè)置狀態(tài)完成對(duì)象的初始化
private void init() {
this.status = OrderStatus.CREATED;
}
所有流程全部封裝在 Order 的靜態(tài)方法 create 上,包括:
- 構(gòu)建內(nèi)部實(shí)體。根據(jù)輸入信息創(chuàng)建 Pay、Address 等關(guān)聯(lián)實(shí)體;
- 金額計(jì)算。創(chuàng)建 OrderItem 實(shí)體并添加到集合中,在添加流程完成金額計(jì)算:
根據(jù)單價(jià)和購(gòu)買數(shù)量計(jì)算 OrderItem 需付金額;
對(duì) OrderItem 需付金額進(jìn)行累計(jì),更新 Order 的需支付金額;
將 Order 需付金額同步到 Pay 實(shí)體;
- 設(shè)置訂單狀態(tài)。調(diào)用 init 方法,將訂單狀態(tài)設(shè)置為 CREATED;
然后看下改價(jià)流程:
public void changePrice(Long newPrice) {
if (newPrice <= 0) {
throw new IllegalArgumentException("金額必須大于0");
}
long discount = getTotalPrice() - newPrice;
if (discount == 0){
return;
}
// Item 均攤折扣
discountForItem(discount);
// Order 折扣
discountForOrder(discount);
// Pay 折扣
syncForPay();
}
// Item 均攤
private void discountForItem(long discount) {
Long totalAmount = getTotalPrice();
Long allocatedDiscount = 0L;
for (int i = 0; i < getOrderItems().size(); i++) {
OrderItem item = getOrderItems().get(i);
Long itemAmount = item.getSellingPrice();
if (i != getOrderItems().size() - 1) {
// 按比例進(jìn)行均攤
Long itemDiscount = itemAmount / totalAmount * discount;
// 重新設(shè)置金額
item.setPrice(item.getPrice() - itemDiscount);
// 記錄累加金額
allocatedDiscount += itemDiscount;
}else {
// 分?jǐn)傆嘞碌膬?yōu)惠金額到最后一個(gè)訂單
Long lastItemDiscount = discount - allocatedDiscount;
item.setPrice(item.getPrice() - lastItemDiscount);
}
}
}
// Order 折扣
private void discountForOrder(long discount) {
Long newTotalPrice = getTotalPrice() - discount;
setTotalPrice(newTotalPrice);
}
// 將價(jià)格同步到 Pay
private void syncForPay() {
this.pay.updatePrice(getTotalPrice());
}
改價(jià)流程由 Order 的實(shí)體方法 changePrice 承載,核心流程如下:
- 入?yún)⑿r?yàn)。修改后的金額不可為負(fù)數(shù);
- Item金額均攤。按OrderItem金額對(duì)折扣信息按比例進(jìn)行均攤,并更新 Item 上的應(yīng)付金額;
- Order金額更新。重新計(jì)算 Order 應(yīng)付金額并對(duì)金額進(jìn)行更新(也可以使用所有 Item 的金額總和);
- Pay金額同步。將Order上的最新金額同步到 Pay 實(shí)體;
如此操作,便可以保證聚合根內(nèi)各個(gè)實(shí)體對(duì)象間的強(qiáng)一致性關(guān)系。
那原子能力又體現(xiàn)在哪呢?
- 不可分割。從業(yè)務(wù)視角,每個(gè)操作都是一個(gè)不可被拆分的最小執(zhí)行單元;從技術(shù)視角,操作過(guò)程不會(huì)被打斷;
- 不可破壞規(guī)則。必須從一個(gè)有效狀態(tài)轉(zhuǎn)換到另一個(gè)有效狀態(tài),聚合根內(nèi)多個(gè)實(shí)體間的業(yè)務(wù)規(guī)則不允許被破壞;
- 持久化層的原子性保障。整個(gè)聚合根的狀態(tài)要么全部更新到DB,要么全不更新,不允許只更新部分信息;
聚合根上的這些“原子業(yè)務(wù)”操作形成了聚合根的生命周期,如下圖所示:
圖片
這是 Order 聚合根的生命周期,每個(gè)業(yè)務(wù)操作均由以下部分組成:
- 業(yè)務(wù)驗(yàn)證。包括入?yún)⑿r?yàn)、前置條件校驗(yàn);
- 狀態(tài)或數(shù)據(jù)更新。對(duì)聚合內(nèi)實(shí)體狀態(tài)或數(shù)據(jù)進(jìn)行更新;
- 領(lǐng)域事件發(fā)布。對(duì)外發(fā)布領(lǐng)域事件;
以訂單的支付成功為例:
public void paySuccess(){
// 前置校驗(yàn)
if (getStatus() != OrderStatus.CREATED){
throw new RuntimeException("非待支付狀態(tài),無(wú)法操作");
}
// 更新?tīng)顟B(tài)
setStatus(OrderStatus.PAID);
// 如果 OrderItem 需要更新的話進(jìn)行調(diào)用
// paySuccessForItem();
// 發(fā)布事件
publishEvent(new OrderPaidEvent(this));
}
2.2. 聚合根設(shè)計(jì)原則
想設(shè)計(jì)出好的聚合根非常不容易,需要豐富的經(jīng)驗(yàn),更需要對(duì)領(lǐng)域概念有很深的認(rèn)知,但有些原則可以幫助你避免不少坑:
- 盡量簡(jiǎn)單。聚合根不應(yīng)該包含太多的業(yè)務(wù)對(duì)象,應(yīng)該保持簡(jiǎn)單,以保證可維護(hù)性;
- 對(duì)內(nèi)部實(shí)體提供保護(hù)。非聚合根實(shí)體或值對(duì)象只能通過(guò)聚合根來(lái)間接訪問(wèn),不能被外部直接訪問(wèn);
- 業(yè)務(wù)操作具備原子性。內(nèi)部實(shí)體操作必須滿足聚合根的業(yè)務(wù)規(guī)則,以保證數(shù)據(jù)的一致性和完整性;
- 謹(jǐn)慎處理與其他聚合根的關(guān)系。聚合根不能包含其它聚合根的引用,聚合根之間可以通過(guò) ID 引用來(lái)關(guān)聯(lián),而不能直接引用其它聚合根,以避免概念的擴(kuò)大;
- 有效的版本管理。聚合根是狀態(tài)變化的載體,為了避免并發(fā)情況下?tīng)顟B(tài)不一致,需要有效的版本管理對(duì)其進(jìn)行保障,比如最常見(jiàn)的樂(lè)觀鎖機(jī)制;
- 具有清晰的職責(zé)。聚合根只處理自己的領(lǐng)域問(wèn)題,不應(yīng)該處理其它聚合根的領(lǐng)域問(wèn)題;
2.3. 聚合根的優(yōu)勢(shì)
聚合根的引入會(huì)對(duì)系統(tǒng)產(chǎn)生非常大的影響,具體如下:
- 簡(jiǎn)化系統(tǒng)架構(gòu)。使用聚合根將整個(gè)領(lǐng)域劃分為多個(gè)聚合,可以使系統(tǒng)架構(gòu)更加清晰。每個(gè)聚合都有自己的聚合根,聚合根之間只有松散的關(guān)聯(lián),這樣可以降低系統(tǒng)的復(fù)雜度和耦合度;
- 提高系統(tǒng)性能。聚合根可以作為一組數(shù)據(jù)的操作單元,因此可以減少數(shù)據(jù)庫(kù)的訪問(wèn)次數(shù)。例如,在多次更新一個(gè)聚合后,最終狀態(tài)只需要一次數(shù)據(jù)庫(kù)操作便能完成;
- 支持并發(fā)處理。使用聚合根可以支持更好的并發(fā)處理,聚合是一個(gè)最小的操作單元,同一聚合可以使用樂(lè)觀鎖等機(jī)制來(lái)控制并發(fā),而聚合之間不存在共享數(shù)據(jù)可以進(jìn)行并行處理;
- 提高可測(cè)試性。將整個(gè)領(lǐng)域劃分為多個(gè)聚合,可以將測(cè)試場(chǎng)景限定在單個(gè)聚合內(nèi),從而提高測(cè)試的可控性和可重復(fù)性;
3. 流程編排
有了小“積木塊”(聚合根上的原子業(yè)務(wù)操作),接下來(lái)就是如何將其組裝成更大的業(yè)務(wù)操作。常見(jiàn)的編排方式有:
- 應(yīng)用服務(wù)。對(duì)模型中的組件進(jìn)行編排,為調(diào)用者提供完整的業(yè)務(wù)能力,比如生單、改價(jià)等;
- 領(lǐng)域服務(wù)。應(yīng)用服務(wù)之下,主要解決跨聚合根操作的業(yè)務(wù)流程,比如轉(zhuǎn)賬等;
- 事件編排。應(yīng)用服務(wù)之上,對(duì)多個(gè)業(yè)務(wù)域的操作進(jìn)行編排,實(shí)現(xiàn)聚合間、服務(wù)間的解耦,比如支付成功后需要給用戶發(fā)送短信等;
3.1. 應(yīng)用服務(wù)
應(yīng)用服務(wù)是DDD中重要的一層,它主要根據(jù)業(yè)務(wù)場(chǎng)景對(duì)流程進(jìn)行編排,從而滿足不同場(chǎng)景對(duì)領(lǐng)域能力的不同訴求。應(yīng)用服務(wù)位于領(lǐng)域?qū)雍徒涌趯又g,對(duì)內(nèi)統(tǒng)一協(xié)調(diào)多個(gè)組件,對(duì)外滿足不同場(chǎng)景下的 User Story。
下圖是應(yīng)用服務(wù)所在的位置:
圖片
簡(jiǎn)單一句話形容便是:應(yīng)用服務(wù)在領(lǐng)域模型各個(gè)組件的能力之上進(jìn)行流程編排,以滿足上層不同場(chǎng)景下的業(yè)務(wù)需求。
應(yīng)用服務(wù)使用最多的便是對(duì)單個(gè)聚合的流程編排,主要包括創(chuàng)建和更新:
創(chuàng)建流程如下:
圖片
更新流程如下:
圖片
兩者最大的區(qū)別在于聚合根獲取方式:
- 創(chuàng)建流程,使用工廠完成復(fù)雜對(duì)象的創(chuàng)建,從而開(kāi)啟聚合的生命周期;
- 更新流程,使用倉(cāng)庫(kù)從持久化存儲(chǔ)中恢復(fù)聚合對(duì)象,從而推動(dòng)聚合生命周期的演進(jìn);
那這個(gè)的價(jià)值在哪呢?因?yàn)橐?guī)范所以可以做到非常好的封裝,具體見(jiàn)代碼:
生單并改價(jià)流程編排:
@Transactional
public void createAndChangePrice(CreateOrderAndChangePriceCommand command) {
// 1. 檢查庫(kù)存,如果足夠則進(jìn)行鎖定;如果不夠,則拋出異常
this.inventoryService.checkIsEnoughAndLock(command.getItems());
// 2. 流程編排
// 設(shè)置存儲(chǔ)倉(cāng)庫(kù)
creatorFor(this.orderRepository)
// 配置事件發(fā)布器
.publishBy(eventPublisher)
// 配置聚合根初始化
.instance(() -> Order.create(command))
// 配置執(zhí)行額外操作
.update(order -> order.changePrice(command.getNewPrice()))
// 執(zhí)行操作
.call();
}
支付成功流程編排:
@Transactional
public void paySuccess(Long orderId){
// 流程編排
// 設(shè)置存儲(chǔ)倉(cāng)庫(kù)
updaterFor(this.orderRepository)
// 設(shè)置 id
.id(orderId)
// 配置事件發(fā)布器
.publishBy(this.eventPublisher)
// 未找到時(shí)拋出異常
.onNotExist(id -> new AggregateNotFountException(id))
// 配置業(yè)務(wù)動(dòng)作
.update(order -> order.paySuccess())
// 執(zhí)行操作
.call();
}
代碼的可讀性得到很大的提升。
3.2. 領(lǐng)域服務(wù)
領(lǐng)域服務(wù)通常是無(wú)狀態(tài)操作,當(dāng)一些職責(zé)不適合放在任何一個(gè)領(lǐng)域?qū)ο笊蠒r(shí),我們可以考慮將其放在一個(gè)領(lǐng)域服務(wù)中。
整個(gè)流程是一個(gè)標(biāo)準(zhǔn)的業(yè)務(wù)概念,并且流程中涉及多個(gè)領(lǐng)域?qū)ο螅?dāng)這個(gè)操作放在哪個(gè)領(lǐng)域?qū)ο笊隙疾缓线m時(shí),可以將其放在一個(gè)單獨(dú)的服務(wù)中,這個(gè)服務(wù)就是領(lǐng)域服務(wù)。
領(lǐng)域服務(wù)只做流程編排,不直接處理業(yè)務(wù)邏輯,業(yè)務(wù)邏輯直接調(diào)用領(lǐng)域模型中的其他對(duì)象。
一個(gè)例子就是轉(zhuǎn)賬,“轉(zhuǎn)賬”作為一個(gè)標(biāo)準(zhǔn)的業(yè)務(wù)概念,需要提供一個(gè)轉(zhuǎn)賬服務(wù)來(lái)承載這個(gè)領(lǐng)域概念。轉(zhuǎn)賬服務(wù)需要協(xié)調(diào)兩個(gè)領(lǐng)域?qū)ο螅涸促~戶和目標(biāo)賬戶,源賬號(hào)做轉(zhuǎn)出,目標(biāo)賬號(hào)做轉(zhuǎn)入,從而實(shí)現(xiàn)轉(zhuǎn)賬邏輯。
簡(jiǎn)單的單機(jī)場(chǎng)景可以基于數(shù)據(jù)庫(kù)事務(wù)進(jìn)行保障,具體如下:
圖片
image
從嚴(yán)格意義上講,在一個(gè)事務(wù)中只能對(duì)一個(gè)聚合進(jìn)行修改,這條原則更適合于分布式系統(tǒng)。在單機(jī)系統(tǒng)中,直接使用數(shù)據(jù)庫(kù)本地事務(wù)對(duì)一致性進(jìn)行保障,是一種投入產(chǎn)出比極高的事情。
在復(fù)雜的分布式場(chǎng)景需要引入“協(xié)調(diào)器”來(lái)對(duì)流程進(jìn)行總控,以實(shí)現(xiàn)系統(tǒng)的最終一致性,具體如下:
圖片
image
【注】領(lǐng)域服務(wù)只做流程編排,不處理業(yè)務(wù)邏輯!!!!
領(lǐng)域服務(wù)不會(huì)直接暴露給業(yè)務(wù)方使用,而是由應(yīng)用服務(wù)負(fù)責(zé)協(xié)作。切記應(yīng)用服務(wù)是領(lǐng)域模型的門面(Facade)。
領(lǐng)域服務(wù)是基于面向過(guò)程編程范式構(gòu)建的,目的是降低領(lǐng)域模型之間的耦合關(guān)系。切記不要被濫用,將太多的邏輯放在領(lǐng)域服務(wù)中,這會(huì)導(dǎo)致領(lǐng)域服務(wù)變得臃腫和難以維護(hù)。
3.3. 領(lǐng)域事件編排
領(lǐng)域事件是一種輕量級(jí)的通信機(jī)制,聚合根可以發(fā)布領(lǐng)域事件來(lái)通知其他聚合根或外部系統(tǒng)發(fā)生了某個(gè)事件,而其他聚合根或外部系統(tǒng)則可以訂閱這些事件,進(jìn)行相應(yīng)的處理,從而推動(dòng)流程向下發(fā)展。
在系統(tǒng)中,基于領(lǐng)域事件的流程編排極為重要,他是系統(tǒng)間解耦的利器,也是分布式環(huán)境下最終一致性的保障。
首先,看一個(gè)簡(jiǎn)單場(chǎng)景:
圖片
訂單支付成功后,向外發(fā)布領(lǐng)域事件,下游接受到領(lǐng)域事件后,做如下動(dòng)作:
- 為用戶發(fā)送購(gòu)買成功的短信通知;
- 為用戶增加積分;
- 通知倉(cāng)庫(kù)進(jìn)行發(fā)貨;
然后,看一個(gè)更復(fù)雜的外賣場(chǎng)景:
圖片
在分布式系統(tǒng)中極為常見(jiàn),將事件串聯(lián)起來(lái)從而完成一個(gè)復(fù)雜的業(yè)務(wù)操作:
- 用戶完成支付,訂單服務(wù)向外發(fā)布“支付成功”事件;
- 餐廳服務(wù)接收到“支付成功”事件后,執(zhí)行下單動(dòng)作,將菜品增加到大廚的制菜清單上;
- 餐廳做完菜后對(duì)外發(fā)送“飯菜準(zhǔn)備好”事件;
- 物流系統(tǒng)接收到“飯菜準(zhǔn)備好”事件后,通知外賣小哥上門取餐(這點(diǎn)不太合適,應(yīng)該是支付成功后,快遞小哥便收到通知。為了更好的描述流程,先忽略現(xiàn)實(shí)操作)
- 快遞小哥完成取餐、送餐后,物流系統(tǒng)發(fā)出“外賣已送達(dá)”事件;
- 訂單服務(wù)接收到“外賣已送到”事件后,更新訂單為“已完成”狀態(tài);
不管簡(jiǎn)單場(chǎng)景還是復(fù)雜場(chǎng)景,事件玩的就是一個(gè)連線游戲:
- 各個(gè)系統(tǒng)已經(jīng)提供了豐富的業(yè)務(wù)操作,也就是游戲中的“節(jié)點(diǎn)”;
- MQ中間件是一個(gè)標(biāo)準(zhǔn)的橋梁,用于連接消息發(fā)送方和訂閱方,也就是游戲中的“線”;
- 根據(jù)業(yè)務(wù)流程,將業(yè)務(wù)操作與消息發(fā)送端&消費(fèi)端鏈接起來(lái),并完成了整個(gè)業(yè)務(wù)操作;
4. 小節(jié)
在一個(gè)系統(tǒng)中,“元素”的數(shù)量是有限的,“元素”間的“關(guān)系”是無(wú)限的。我們需要用好流程編排這把利器,在有限“元素”基礎(chǔ)上,構(gòu)建無(wú)限的“關(guān)系”,從而應(yīng)對(duì)多變的業(yè)務(wù)場(chǎng)景。
- 原子能力。主要以聚合根為中心,對(duì)外暴露的各種 原子業(yè)務(wù)操作;
- 流程編排。通過(guò)多種手段,將原子能力和基礎(chǔ)設(shè)施編排起來(lái),最終實(shí)現(xiàn)業(yè)務(wù)需求:
應(yīng)用服務(wù)。對(duì)領(lǐng)域模型中的組件進(jìn)行組裝,以實(shí)現(xiàn)不同的業(yè)務(wù)訴求;
領(lǐng)域服務(wù)。對(duì)于有明確的業(yè)務(wù)概念,但找不到合適的領(lǐng)域?qū)ο笞鞒休d的操作,可以封裝成領(lǐng)域服務(wù);
領(lǐng)域事件編排。主要解決聚合之間、服務(wù)之間耦合問(wèn)題,對(duì)于有明確的 “因果” 關(guān)系的場(chǎng)景最為實(shí)用;