目錄
- 前言
- 1. 業務邏輯組織模式
- 1.1 一個典型的服務架構
- 1.2 使用事務腳本模式設計業務邏輯
- 1.3 使用領域模型模式設計業務邏輯
- 1.4 關于領域驅動設計
- 2. 使用聚合模式設計領域模型
- 2.1 聚合擁有明確的邊界
- 2.2 聚合規則
- 2.3 聚合的顆粒度
- 2.4 使用聚合設計業務
- 2.5 Order Service基于聚合設計的業務邏輯
- 3. 發布領域事件
- 3.1 領域事件的應用場景
- 3.2 領域事件的特點
- 3.3 事件增強
- 3.4 識別領域事件
- 3.5 生成領域事件
- 3.6 發布領域事件
- 3.7 消費領域事件
- 4. Kichen Service的業務邏輯
- 4.1 Kichen Service的設計
- 4.2 Ticket類的結構
- 4.3 Ticket聚合的行為
- 4.4 KitchenService的領域服務
- 4.5 KitchenServiceCommandHandler類
- 5. Order Service的業務邏輯
- 5.1 Order Service的設計
- 5.2 Order聚合的結構
- 5.3 Order聚合狀態機
- 5.4 OrderService類
- 6. 微服務與單體應用程式的業務邏輯異同點
- 7. 本章小結
- 最后
前言
這是一本關于微服務架構設計方面的書,這是本人閱讀的學習筆記,首先對一些符號做些說明:
()為補充,一般是書本里的內容;
[]符號為筆者筆注;
1. 業務邏輯組織模式
組織業務邏輯有兩種主要的模式:面向程序的事務腳本模式和面向物件的領域建模模式,
1.1 一個典型的服務架構
業務邏輯周圍是入站和出站配接器,
- 入站配接器:處理來自客戶端的請求并呼叫業務邏輯;
- 出站配接器:被業務邏輯呼叫,然后它們再呼叫其他服務和外部程式應用;
Order Service具有六邊形架構:

此服務由業務邏輯和以下配接器組成:
- REST API adapter:入站配接器,實作REST API,這些API會呼叫業務邏輯;
- OrderCommandHandlers:入站配接器,它接受來自訊息代理的出站配接器,并呼叫業務邏輯;
- Database Adapter:由業務邏輯呼叫以訪問資料庫的出站配接器;
- Domain Event Publishing Adapter:將事件發布到訊息代理的出站配接器;
1.2 使用事務腳本模式設計業務邏輯
事務腳本:將業務邏輯組織為面向程序的事務腳本的集合,每種型別的請求都有一個腳本,

特點:
- 實作行為的類與存盤狀態的類是分開的;
- 腳本通常在服務類中;
- 每個服務都有一個用于請求或系統操作的方法;這個方法實作請求的業務邏輯;
1.3 使用領域模型模式設計業務邏輯
領域模型:將業務邏輯組織為由具有狀態和行為的類構成的物件模型,

特點:
- 服務方法通常很簡單(因為服務方法幾乎總是呼叫持久化領域物件,這些物件中包含大量業務邏輯);
- 如:Order類具有狀態和方法,狀態是私有的,只能通過它的方法間接訪問;
- 易于理解、維護、測驗和擴展;
1.4 關于領域驅動設計
領域驅動設計(DDD)是對面向物件設計的改進,是開發復雜業務邏輯的一種方法,其基本元素如下:
- 物體(entity):具有持久化ID的物件,具有相同屬性值的兩個物體仍然是不同的物件;
- 值物件(value object):作為值集合的物件,具有相同屬性值的兩個值物件可以互換使用;
- 工廠(factory):負責實作物件創建邏輯的物件或方法,該邏輯過于復雜,無法由類的建構式直接完成,它還可以隱藏被實體化的具體類,工廠方法一般可實作為類的靜態方法;
- 存盤庫(repository):用來訪問持久化物體的物件,儲存庫也封裝了訪問資料庫的底層機制;
- 服務(service):實作不屬于物體或值物件的業務邏輯物件;
2. 使用聚合模式設計領域模型
傳統領域模型缺少每個業務物件的明確邊界,DDD聚合旨在解決此問題,
2.1 聚合擁有明確的邊界
聚合模式:將領域模型組織為聚合的集合,每個聚合都是可以作為一個單元進行處理的一組物件構成的圖,
Order 聚合及其邊界:

- 聚合代表了一致的邊界;
- 更新整個聚合可以解決一致性問題;
- 識別聚合是關鍵;
- 在驅動領域設計中,設計領域模型的關鍵部分是識別聚合,以及它們的邊界和根;
2.2 聚合規則
聚合規則可以確保聚合是一個可以強制執行各種不變數約束的自包含單元,
- 規則一:只參考聚合根;
- 聚合根是聚合中唯一可以由外部類參考的部分;客戶端只能通過呼叫聚合根上的方法來更新聚合;
- 如:服務使用儲存庫從資料庫加載聚合并獲取聚合根的參考;
- 規則二:聚合間的參考必須使用主鍵;
- 如:Order使用consumerId參考其Consumer而不是直接參考Consumer物件;
- 規則三:在一個事務中,只能創建或更新一個聚合;
- 這個約束可以確保單個事務的范圍不超越服務的邊界;還滿足大多數NoSQL資料庫的受限事務模型;
- 這個規則讓創建或更新多個聚合的操作變得更加復雜,但可以通過Saga解決;

2.3 聚合的顆粒度
- 由于每個聚合的更新都是序列化的,因此更細顆粒度的聚合間提高應用程式能同時處理的請求數量,從而提高可擴展性;
- 另一方面,因為聚合是事務的范圍,所以可能需要定義更大的聚合以使特定的聚合更新操作滿足事務的原子性;
- 因此,在開發領域模型時,必須做出的關鍵決策是決定每個聚合的大小;
2.4 使用聚合設計業務
- 在典型的微服務中,大部分業務邏輯由聚合組成;其余業務邏輯存在與領域服務和Saga中;
- Saga編排本地事務的序列,以確保資料的一致性;
- 服務是業務邏輯的入口,由入站配接器呼叫;
- 服務使用存盤庫從資料庫中檢索聚合或將聚合保存到資料庫;
- 每個存盤庫都由訪問資料庫的出站配接器實作;
2.5 Order Service基于聚合設計的業務邏輯

- 業務邏輯由Order聚合、OrderService服務類、OrderRepository和一個或多個Saga組成;
- OrderService呼叫OrderRepository來保存和加載Order;
- 對于能在服務內部完成處理的簡單請求,服務直接更新Order聚合;
- 如果更新請求跨越多個服務,OrderService將創建一個Saga;
3. 發布領域事件
領域事件:聚合在被創建時,或發生其他重大更改時發布領域事件,
3.1 領域事件的應用場景
- 使用基于編排的Saga維護服務之間的資料一致性【第四章】;
- 通知維護資料副本的服務,源資料已經發生了更改;這種方法稱為命令查詢職責隔離(CQRS)【第七章】;
- 通過Webhook或訊息代理通知不同的應用程式,以觸發下一個業務流程;
- 按順序通知同一應用程式的不同組件;
- 向用戶發送短信或電子郵件通知,告訴他們訂單發貨、航班延誤等訊息;
- 監控領域事件以驗證應用程式是否正常運行;
- 分析領域事件,為用戶行為建模;
3.2 領域事件的特點
- 命名領域事件時,往往選擇動詞的過去分詞;
- 領域事件的每個屬性都是原始值或值物件;
- 如:OrderCreated事件類具有orderId屬性;
- 領域事件通常具有元資料,如事件ID和時間戳;
- 元資料可以是事件物件的一部分,可能在超類中定義;
OrderCreated事件是領域事件的一個例子:

DomainEvent介面是一個標識介面,用于將類標識為領域事件;OrderDomainEvent是Order聚合發布的事件的標識介面(如OrderCreated);DomainEventEnvelope是一個包含事件元資料和事件物件的類;
3.3 事件增強
當OrderCreated的事件接收方需要訂單的詳細資訊時,一種辦法是從Order Service中檢索該資訊,讓事件接收方查詢聚合服務,缺點是會產生服務請求的開銷;
另一種方案是事件增強:
- 事件包含接收方需要的資訊;
- 缺點:可能會使領域事件的穩定性降低;每當接收方的需求發生改變時,事件類都可能需要更改;可能會降低可維護性;

3.4 識別領域事件
可以使用事件風暴方法,其結果是一個以事件為中心的領域模型,它由聚合和事件組成;包括以下三個步驟:
- 頭腦風暴:請求領域專家集體討論領域事件;
- 識別事件觸發器:請求領域專家確定每個事件的觸發器(如:用戶操作、外部系統、另一個領域事件、時間的流逝等);
- 識別聚合:請求領域專家識別那些使用命令的聚合并發出相應的事件;
3.5 生成領域事件
在聚合和呼叫它的服務(或類)之間分配職責,
- 服務可以使用依賴注入來獲取對訊息傳遞API的參考,從而輕松發布事件;
- 只要狀態發生變化,聚合就會生成事件并將它們回傳給服務;
- 聚合可以通過以下方法將事件回傳給服務:
在聚合方法的回傳值中包括一個事件串列:

該服務呼叫聚合根方法,然后發布事件;

聚合根在一個內部欄位中累積保存事件:然后服務檢索這些事件并發布它們;

3.6 發布領域事件
服務必須使用事務性訊息來發布事件,以確保領域事件是作為更新資料庫中聚合的事務的一部分對外發布;
Eventuate Tram框架提供DomainEventPublisher介面:

讓服務實作AbstractAggregateDomainEventPublisher的子類:它為發布領域事件提供了型別安全的介面;



3.7 消費領域事件
領域事件是接收方使用更高級的API,如:Eventuate Tram框架的DomainEventDispatcher等,其可以將領域事件調度到適當的處理程式方法,

- 每當餐館的選單更新時,KitchenServiceEventConsumer都會訂閱Restaurant Service發布事件;
- 它負責使Kitchen Service的資料副本保持最新;
4. Kichen Service的業務邏輯
該服務的主要功能是負責實作餐館的訂單管理功能,其兩個主要聚合是Restaurant和Ticket;
4.1 Kichen Service的設計

- 兩個聚合:
- Restaurant:知道餐館的選單和營業事件,并可以驗證訂單;
- Ticket:工單烹飪完成后由送餐員負責派送;
- 核心業務:
- KitchenService:業務入口,定義了創建和更新Restaurant及Ticket聚合的方法;
- TicketRepository:定義了持久化Tickets的方法;
- RestaurantRepository:定義了持久化Restaurants的方法;
- 三個入站配接器:
- REST API:餐館作業人員通過他們的用戶界面呼叫這些REST API;
- KitchenServiceCommandHandler:由Saga呼叫的基于異步請求 / 回應的API;它呼叫KitchenService來創建和更新Ticket;
- KitchenServiceEventConsumer:訂閱Restaurant Service發布的事件;它呼叫KitchenService來創建和更新Restaurant聚合;
- 兩個出站配接器:
- DB Adapter:實作TicketRepository和RestaurantRepository介面并訪問資料庫;
- DomainEventPublishingAdapter:實作DomainEventPublisher介面并發布Ticket領域事件;
4.2 Ticket類的結構
該類使用JPA進行持久化,并映射到TICKETS表,


4.3 Ticket聚合的行為


- create():創建Ticket的工廠方法;
- accept():餐館已接收訂單;
- preparing():餐館已開始準備訂單,意味著訂單無法再更改或取消;
- readyForPickup():訂單可以派送;
4.4 KitchenService的領域服務
KitchenService由服務入站配接器呼叫;定義了用于更改訂單狀態的各種方法(如accept()、reject()、preparing()等);每個方法加載指定的聚合,在聚合根上呼叫相應的方法,并發布領域事件;如下accept()方法所示:

accept()方法的兩個引數:
- orderId:要接受訂單的ID;
- readyBy:訂單可被派送的預計時間;
4.5 KitchenServiceCommandHandler類
KitchenServiceCommandHandler類是一個配接器,負責處理Order Service實作的各種Saga發送的命令式訊息;


5. Order Service的業務邏輯
5.1 Order Service的設計

- 幾個入站配接器:
- REST API:供消費者利用用戶界面呼叫的REST API;它呼叫OrderService來創建和更新Order;
- OrderEventConsumer:訂閱Restaurant Service發布的活動;它呼叫OrderService來創建和更新其Restaurant副本;
- OrderCommandHandlers:由Saga呼叫的基于異步請求 / 相應的API;它呼叫OrderService來更新Order;
- SagaReplyAdapter:訂閱Saga回復通道并呼叫Saga;
- 一些出站配接器:
- DB Adapter:實作OrderRepository介面并訪問Order Service的資料庫;
- DomainEventPublishingAdapter:實作DomainEventPublisher介面并發布Order領域事件;
- OutboundCommandMessageAdapter:實作CommandPublisher介面并向Saga參與方發送命令式訊息;
5.2 Order聚合的結構
Order類是Order聚合的根;Order聚合還包括了值物件,如:OrderLineItem、DeliveryInfo和PaymentInfo,

Order類和它的欄位:
此類使用JPA持久化,并映射到ORDERS表,


5.3 Order聚合狀態機
為了創建或更新訂單,Order Service必須使用Saga與其他服務協作,

創建Order程序中呼叫的Order方法:


更新Order需要呼叫的方法:


5.4 OrderService類
該類定義了用于創建和更新Orders的方法,


6. 微服務與單體應用程式的業務邏輯異同點
- 相同點:
- 由諸如服務、JPA支持的物體類和存盤庫等這樣的類組成;
- 不同點:
- 領域模型被組織為一組DDD聚合,在其上可以施加各種約束;
- 與傳統的物件模型不同,不同聚合中的類之間的參考是基于主鍵而不是物件參考;
- 事務只能創建或更新單個聚合;聚合在狀態發生變化時會發布領域事件;
- 服務通常使用Saga來維護多個服務之間的資料一致性;
7. 本章小結
- 事務腳本模式通常是實作簡單業務的好辦法,但在實作復雜的業務邏輯時,應該考慮使用面向物件的領域模型模式;
- 設計服務的業務邏輯的好辦法是使用DDD聚合,DDD聚合很有用,因為它們把領域模型模塊化,消除了服務之間物件的直接參考,并確保每個ACID事務都在服務內;
- 創建或更新聚合時應發布領域事件,領域事件具有廣泛的用途,第4章討論了如何實作協同式Saga,第7章中將討論如何使用領域事件來更新從其他服務復制來的資料,領域事件的訂閱者還可以通知用戶和其他應用程式,并將WebSocket訊息發布到用戶的瀏覽器,
最后

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/296416.html
標籤:架構設計
