《微服務架構設計模式》讀書筆記 | 第5章 微服務架構中的業務邏輯設計

多氯環己烷發表於2021-08-31


前言

這是一本關於微服務架構設計方面的書,這是本人閱讀的學習筆記。首先對一些符號做些說明:

()為補充,一般是書本里的內容;
[]符號為筆者筆注;


1. 業務邏輯組織模式

組織業務邏輯有兩種主要的模式:程式導向的事務指令碼模式和麵向物件的領域建模模式。

1.1 一個典型的服務架構

業務邏輯周圍是入站和出站介面卡。

  • 入站介面卡:處理來自客戶端的請求並呼叫業務邏輯;
  • 出站介面卡:被業務邏輯呼叫,然後它們再呼叫其他服務和外部程式應用;

Order Service具有六邊形架構

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 聚合及其邊界

Order 聚合及其邊界

  • 聚合代表了一致的邊界
    • 更新整個聚合可以解決一致性問題;
  • 識別聚合是關鍵
    • 在驅動領域設計中,設計領域模型的關鍵部分是識別聚合,以及它們的邊界和根;

2.2 聚合規則

聚合規則可以確保聚合是一個可以強制執行各種不變數約束的自包含單元。

  • 規則一:只引用聚合根
    • 聚合根是聚合中唯一可以由外部類引用的部分;客戶端只能通過呼叫聚合根上的方法來更新聚合;
    • 如:服務使用儲存庫從資料庫載入聚合並獲取聚合根的引用;
  • 規則二:聚合間的引用必須使用主鍵
    • 如:Order使用consumerId引用其Consumer而不是直接引用Consumer物件;
  • 規則三:在一個事務中,只能建立或更新一個聚合
    • 這個約束可以確保單個事務的範圍不超越服務的邊界;還滿足大多數NoSQL資料庫的受限事務模型;
    • 這個規則讓建立或更新多個聚合的操作變得更加複雜,但可以通過Saga解決;

事務只能建立或更新單個聚合


2.3 聚合的顆粒度

  • 由於每個聚合的更新都是序列化的,因此更細顆粒度的聚合間提高應用程式能同時處理的請求數量,從而提高可擴充套件性;
  • 另一方面,因為聚合是事務的範圍,所以可能需要定義更大的聚合以使特定的聚合更新操作滿足事務的原子性;
  • 因此,在開發領域模型時,必須做出的關鍵決策是決定每個聚合的大小;

2.4 使用聚合設計業務

  • 在典型的微服務中,大部分業務邏輯由聚合組成;其餘業務邏輯存在與領域服務和Saga中;
  • Saga編排本地事務的序列,以確保資料的一致性;
  • 服務是業務邏輯的入口,由入站介面卡呼叫;
  • 服務使用儲存庫從資料庫中檢索聚合或將聚合儲存到資料庫;
  • 每個儲存庫都由訪問資料庫的出站介面卡實現;

2.5 Order Service基於聚合設計的業務邏輯

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 識別領域事件

可以使用事件風暴方法,其結果是一個以事件為中心的領域模型,它由聚合和事件組成;包括以下三個步驟:

  1. 頭腦風暴:請求領域專家集體討論領域事件;
  2. 識別事件觸發器:請求領域專家確定每個事件的觸發器(如:使用者操作、外部系統、另一個領域事件、時間的流逝等);
  3. 識別聚合:請求領域專家識別那些使用命令的聚合併發出相應的事件;

3.5 生成領域事件

在聚合和呼叫它的服務(或類)之間分配職責。

  • 服務可以使用依賴注入來獲取對訊息傳遞API的引用,從而輕鬆釋出事件;
  • 只要狀態發生變化,聚合就會生成事件並將它們返回給服務;
  • 聚合可以通過以下方法將事件返回給服務:

在聚合方法的返回值中包括一個事件列表


該服務呼叫聚合根方法,然後釋出事件;


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

3.6 釋出領域事件

服務必須使用事務性訊息來發布事件,以確保領域事件是作為更新資料庫中聚合的事務的一部分對外發布;

Eventuate Tram框架提供DomainEventPublisher介面

DomainEventPublisher介面
讓服務實現AbstractAggregateDomainEventPublisher的子類:它為釋出領域事件提供了型別安全的介面;

AbstractAggregateDomainEventPublishe介面

實現AbstractAggregateDomainEventPublisher子類

3.7 消費領域事件

領域事件是接收方使用更高階的API,如:Eventuate Tram框架的DomainEventDispatcher等。其可以將領域事件排程到適當的處理程式方法。

  • 每當餐館的選單更新時,KitchenServiceEventConsumer都會訂閱Restaurant Service釋出事件;
  • 它負責使Kitchen Service的資料副本保持最新;

4. Kichen Service的業務邏輯

該服務的主要功能是負責實現餐館的訂單管理功能。其兩個主要聚合是Restaurant和Ticket;

4.1 Kichen Service的設計

Kitchen 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表。


Ticket類的結構

4.3 Ticket聚合的行為


Ticket的一些方法

  • create():建立Ticket的工廠方法;
  • accept():餐館已接收訂單;
  • preparing():餐館已開始準備訂單,意味著訂單無法再更改或取消;
  • readyForPickup():訂單可以派送;

4.4 KitchenService的領域服務

KitchenService由服務入站介面卡呼叫;定義了用於更改訂單狀態的各種方法(如accept()、reject()、preparing()等);每個方法載入指定的聚合,在聚合根上呼叫相應的方法,併發布領域事件;如下accept()方法所示:

accept()方法更新Ticket
accept()方法的兩個引數

  • orderId:要接受訂單的ID;
  • readyBy:訂單可被派送的預計時間;

4.5 KitchenServiceCommandHandler類

KitchenServiceCommandHandler類是一個介面卡,負責處理Order Service實現的各種Saga傳送的命令式訊息;

KitchenServiceCommandHandler類


5. Order Service的業務邏輯

5.1 Order Service的設計

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聚合的設計
Order類和它的欄位

此類使用JPA持久化,並對映到ORDERS表。


Order類和它的欄位

5.3 Order聚合狀態機

為了建立或更新訂單,Order Service必須使用Saga與其他服務協作。

Order聚合狀態機
建立Order過程中呼叫的Order方法

建立Order過程中呼叫的方法

更新Order需要呼叫的方法


更新Order需要呼叫的方法

5.4 OrderService類

該類定義了用於建立和更新Orders的方法。

OrderService類


6. 微服務與單體應用程式的業務邏輯異同點

  • 相同點
    • 由諸如服務、JPA支援的實體類和儲存庫等這樣的類組成;
  • 不同點
    • 領域模型被組織為一組DDD聚合,在其上可以施加各種約束;
    • 與傳統的物件模型不同,不同聚合中的類之間的引用是基於主鍵而不是物件引用;
    • 事務只能建立或更新單個聚合;聚合在狀態發生變化時會發布領域事件;
    • 服務通常使用Saga來維護多個服務之間的資料一致性;

7. 本章小結

  • 事務指令碼模式通常是實現簡單業務的好辦法。但在實現複雜的業務邏輯時,應該考慮使用物件導向的領域模型模式;
  • 設計服務的業務邏輯的好辦法是使用DDD聚合。DDD聚合很有用,因為它們把領域模型模組化,消除了服務之間物件的直接引用,並確保每個ACID事務都在服務內;
  • 建立或更新聚合時應釋出領域事件。領域事件具有廣泛的用途。第4章討論瞭如何實現協同式Saga。第7章中將討論如何使用領域事件來更新從其他服務複製來的資料。領域事件的訂閱者還可以通知使用者和其他應用程式,並將WebSocket訊息釋出到使用者的瀏覽器。

最後

新人制作,如有錯誤,歡迎指出,感激不盡!
歡迎關注公眾號,會分享一些更日常的東西!
如需轉載,請標註出處!
《微服務架構設計模式》讀書筆記 | 第5章 微服務架構中的業務邏輯設計

相關文章