前言
最近有空會(huì)跟同事討論DDD架構(gòu)的實(shí)踐落地的情況,但真實(shí)情況是,實(shí)際中對于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的實(shí)體,值對象,聚合根,領(lǐng)域事件這些戰(zhàn)術(shù)類的實(shí)踐落地,每個(gè)人理解依然因人而異,大概率是因?yàn)檫@些概念還是有一些抽象,同時(shí)有有別于傳統(tǒng)的MVC架構(gòu)開發(fā)。
在此,通過小demo的方式跟大家分享一下我對DDD中戰(zhàn)術(shù)層級(jí)的理解,算是拋磚引玉,該理解僅代表我個(gè)人在現(xiàn)階段的一個(gè)理解,也可能未來隨著業(yè)務(wù)經(jīng)驗(yàn)深入,還會(huì)有不同的理解。
既然說是小demo,還是要從業(yè)務(wù)場景出發(fā),也就是我最熟知的電商業(yè)務(wù)場景說起。但是該篇文章里, 我會(huì)簡化一些實(shí)際業(yè)務(wù)場景中的復(fù)雜度,通過最小顆粒度的demo,來反映實(shí)踐過程中的基本問題。
一個(gè)簡單的demo業(yè)務(wù)場景
話不多說,我先拋出我自己假設(shè)的一個(gè)業(yè)務(wù)場景,就是我們熟知的電商網(wǎng)站下單購物的場景。具體細(xì)節(jié)如下:
1. 實(shí)體:
? 商品:擁有唯一標(biāo)識(shí)、名稱、價(jià)格、庫存等屬性。
? 訂單:擁有唯一標(biāo)識(shí)、下單時(shí)間、狀態(tài)等屬性。訂單包含多個(gè)訂單項(xiàng)。
2. 值對象:
? 地址:擁有省、市、區(qū)、詳細(xì)地址等屬性。
3. 領(lǐng)域事件:
? 訂單創(chuàng)建事件:當(dāng)用戶下單時(shí)觸發(fā)該事件,包含訂單信息、商品信息等數(shù)據(jù)。
? 訂單支付事件:當(dāng)用戶完成支付時(shí)觸發(fā)該事件,包含訂單信息、支付金額等數(shù)據(jù)。
? 訂單發(fā)貨事件:當(dāng)商家發(fā)貨時(shí)觸發(fā)該事件,包含訂單信息、快遞公司、快遞單號(hào)等數(shù)據(jù)。
4. 聚合根:
? 商品聚合根:包含商品實(shí)體和相關(guān)的值對象,負(fù)責(zé)商品的創(chuàng)建、修改、查詢等操作。
? 訂單聚合根:包含訂單實(shí)體和相關(guān)的值對象,負(fù)責(zé)訂單的創(chuàng)建、修改、查詢等操作。
5. 對外接口服務(wù):
? 創(chuàng)建訂單接口:用戶提交購買請求后,系統(tǒng)創(chuàng)建相應(yīng)的訂單,并觸發(fā)訂單創(chuàng)建事件。
? 支付訂單接口:用戶完成支付后,系統(tǒng)更新訂單狀態(tài),并觸發(fā)訂單支付事件。
? 發(fā)貨接口:商家發(fā)貨后,系統(tǒng)更新訂單狀態(tài),并觸發(fā)訂單發(fā)貨事件。
? 查詢訂單接口:用戶可以根據(jù)訂單號(hào)等條件查詢自己的訂單信息。
該demo中,商品和訂單是兩個(gè)核心領(lǐng)域概念,分別由對應(yīng)的聚合根負(fù)責(zé)管理。同時(shí),通過定義領(lǐng)域事件,實(shí)現(xiàn)了不同業(yè)務(wù)場景下的數(shù)據(jù)更新和通知。最后,對外提供了一組簡單的接口服務(wù),方便系統(tǒng)的使用和擴(kuò)展。
demo的java代碼實(shí)現(xiàn)
好了,有了以上我們對業(yè)務(wù)場景的充分剖析,確定了子域,接下來我們該寫我們的代碼。
1.商品實(shí)體類:
// 省略getter/setter方法 public class Product { private Long id; private String name; private BigDecimal price; private Integer stock; }
2. 訂單實(shí)體類
// 省略getter/setter方法 public class Order { private Long id; private LocalDateTime createTime; private Integer status; private List orderItems; }
3. 訂單項(xiàng)實(shí)體類
// 省略getter/setter方法 public class OrderItem { private Long id; private Product product; private Integer quantity; private BigDecimal price; }
4. 地址值對象
// 省略getter/setter方法 public class Address { private String province; private String city; private String district; private String detail; }
5. 領(lǐng)域事件類
//訂單創(chuàng)建領(lǐng)域事件 public class OrderCreatedEvent { private Order order; private List orderItems; public OrderCreatedEvent(Order order, List orderItems) { this.order = order; this.orderItems = orderItems; } } //訂單支付領(lǐng)域事件 public class OrderPaidEvent { private Order order; private BigDecimal amount; public OrderPaidEvent(Order order, BigDecimal amount) { this.order = order; this.amount = amount; } } //訂單 public class OrderShippedEvent { private Order order; private String expressCompany; private String expressNo; public OrderShippedEvent(Order order, String expressCompany, String expressNo) { this.order = order; this.expressCompany = expressCompany; this.expressNo = expressNo; } }
6. 商品聚合根
public class ProductAggregate { private ProductService productService; public void createProduct(Product product) { productService.create(product); } public void updateProduct(Product product) { productService.update(product); } public void deleteProduct(Long productId) { productService.delete(productId); } public Product getProductById(Long productId) { return productService.getById(productId); } }
7. 訂單聚合根
public class OrderAggregate { private OrderService orderService; public void createOrder(Order order, List orderItems) { orderService.create(order); // 觸發(fā)訂單創(chuàng)建事件 DomainEventPublisher.publish(new OrderCreatedEvent(order, orderItems)); } public void payOrder(Long orderId, BigDecimal amount) { orderService.pay(orderId, amount); // 觸發(fā)訂單支付事件 DomainEventPublisher.publish(new OrderPaidEvent(orderService.getById(orderId), amount)); } public void shipOrder(Long orderId, String expressCompany, String expressNo) { orderService.ship(orderId, expressCompany, expressNo); // 觸發(fā)訂單發(fā)貨事件 DomainEventPublisher.publish(new OrderShippedEvent(orderService.getById(orderId), expressCompany, expressNo)); } public Order getOrderById(Long orderId) { return orderService.getById(orderId); } }
總結(jié)
通過以上demo,對于實(shí)體和值對象,大家會(huì)很好理解,并且很直觀。但是, 我額外想重點(diǎn)解釋一下聚合根和領(lǐng)域事件的概念
1. 聚合根
從上面的demo可以看出,在合根類中,我們定義了商品和訂單的增、刪、查等操作,并且為訂單定義了創(chuàng)建訂單、支付訂單、發(fā)貨等業(yè)務(wù)邏輯代碼。
聚合根是一個(gè)對象,它代表一組相關(guān)聯(lián)的對象的整體。在聚合根內(nèi)部,可以包含多個(gè)實(shí)體對象和值對象。聚合根通常可以通過唯一標(biāo)識(shí)符來進(jìn)行識(shí)別和訪問。它是整個(gè)聚合的管理者,負(fù)責(zé)維護(hù)聚合之內(nèi)的一致性,并協(xié)調(diào)各個(gè)實(shí)體對象之間的關(guān)系。聚合根通常具有豐富的行為和操作,可以對聚合內(nèi)部的對象進(jìn)行復(fù)雜的操作。
所以說,真正的聚合根內(nèi)的方法是基于充血模型封裝的,而不是僅僅是對對象的數(shù)據(jù)封裝。在聚合根中,對象不僅封裝了數(shù)據(jù),還包含了相應(yīng)的行為和業(yè)務(wù)邏輯。這意味著在一個(gè)聚合根中,對象可以自己處理自己的業(yè)務(wù)邏輯,而不需要外部的控制。就如同demo中所寫的那樣,訂單對象可能包含一些關(guān)于訂單處理和交付的方法,如確認(rèn)訂單、取消訂單、發(fā)貨等。
2. 領(lǐng)域事件
領(lǐng)域事件是DDD中最重要的概念之一,他是解決子域之間耦合的重要手段,因?yàn)樗鼈兲峁┝艘环N將領(lǐng)域概念和業(yè)務(wù)語言轉(zhuǎn)化為代碼的方法。當(dāng)一個(gè)領(lǐng)域事件發(fā)生時(shí),它會(huì)觸發(fā)一些操作,這些操作可能會(huì)更改系統(tǒng)的狀態(tài),也可能會(huì)導(dǎo)致其他領(lǐng)域事件的發(fā)生。通過對領(lǐng)域事件進(jìn)行建模,我們可以更好地了解業(yè)務(wù)過程并設(shè)計(jì)出更加符合實(shí)際需求的系統(tǒng)。
在DDD中,領(lǐng)域事件通常由三個(gè)部分組成:
1.事件名稱:這個(gè)名稱應(yīng)該能夠簡潔明了地描述事件所代表的業(yè)務(wù)意義。
2.相關(guān)數(shù)據(jù):這些數(shù)據(jù)包含了事件發(fā)生時(shí)與事件相關(guān)的所有信息。例如,在一個(gè)電子商務(wù)系統(tǒng)中,如果訂單被提交,則訂單信息以及買家和賣家的信息都應(yīng)該包括在該事件中。
3.發(fā)送者和接收者:發(fā)送者通常是觸發(fā)事件的對象,接收者則是事件處理的對象。
領(lǐng)域事件在DDD中有很多用途。例如,它們可以用來觸發(fā)其他業(yè)務(wù)流程、更新數(shù)據(jù)庫或通知其他子系統(tǒng)。它們還可以用于解決一些復(fù)雜的業(yè)務(wù)邏輯問題,例如并發(fā)、數(shù)據(jù)同步和錯(cuò)誤處理等等。
總之,領(lǐng)域事件是DDD架構(gòu)中非常重要的概念,它可以幫助我們更好地理解業(yè)務(wù)過程,設(shè)計(jì)出更加符合實(shí)際需求的系統(tǒng),并提高系統(tǒng)的可維護(hù)性和可擴(kuò)展性。
?審核編輯 黃宇
-
接口
+關(guān)注
關(guān)注
33文章
8861瀏覽量
152844 -
JAVA
+關(guān)注
關(guān)注
20文章
2982瀏覽量
106398 -
代碼
+關(guān)注
關(guān)注
30文章
4872瀏覽量
69913 -
ddd
+關(guān)注
關(guān)注
0文章
23瀏覽量
3003
發(fā)布評論請先 登錄
相關(guān)推薦
評論