微服務架構中的業務邏輯設計

前言:
企業應用程序的核心是業務邏輯,業務邏輯實現了業務規則。但是由於業務邏輯分散在多個服務上,因此在微服務架構中開發複雜的業務邏輯更具有挑戰性。

我們需要解決兩個問題:

  1. 首先,典型的領域模型是由各種類交織在一起的一個網絡。雖然這在單體應用程序中不是問題,但是在微服務架構中,類分散在不同的服務中,就需要跨越服務邊界(也就是進程)的對象應用
  2. 其次,是在設計微服務架構下的業務邏輯,這些業務邏輯受到微服務下事務管理的種種約束。我們的業務邏輯可以在一個服務內部使用ACID事務,它必須使用Saga模式來維護服務之間的數據一致性

好在,我們可以使用領域驅動設計中的聚合模式來解決這些問題。聚合模式下,服務的業務邏輯通過多個聚合組成的一個集合來體現。聚合是一組對象,可以作爲一個單元來處理。在微服務架構中開發業務邏輯時,聚合可以起到以下兩個重要的作用:

  1. 使用聚合可以避免任何跨服務邊界的對象引用,因爲聚合之間通過主鍵進行引用,而不是通過對象的地址進行引用
  2. 由於單個事務只能創建或更新單個聚合,因此聚合滿足微服務事務模型的約束

業務邏輯組織模式

  • 業務邏輯是六邊形架構的核心。業務邏輯的周圍是入站和出站適配器。入站適配器處理來自客戶端的請求並調用業務邏輯。出站適配器被業務邏輯調用,然後它們再調用其他服務和外部應用程序。圖5-1顯示了一個典型的服務架構
 例如 go-micro微服務框架流程:
 - proto/.micro.go:文件是入站適配器,接收來自客戶端的rpc請求,找到請求對象的方法,調用業務邏輯(Handler)
 - Handler:就是業務邏輯目錄,在handler中放置業務邏輯類,業務邏輯根據入站適配器傳入的出站適配器結構體參數,根據出站適配器,返回數據,調用出站適配器,完成業務邏輯的處理
 - proto/.pd.go:文件是出站適配器,定義了返回給客戶端的參數,以及出站的業務邏輯。
 - 業務邏輯通常是服務中最複雜的部分。在開發業務邏輯時必須做出的關鍵決策是選用面向對象,還是面向過程。組織業務邏輯有兩種模式:面向過程的事務腳本模式和麪向對象的領域建模模式。
 - models:即是我們自己定義擴展的Databases 數據模型

使用事務腳本模式設計業務邏輯

  • 雖然我們一直提倡使用面向對象的方式,但是在某些情況下使用面向對象的方式會有一種“殺雞用刀到的感覺”,例如在開發簡單的業務邏輯的時候。這種情況下,更好的方法是使用面向過程的方式
  • 模式:事務腳本
    將業務邏輯組織爲面向過程的事務腳本集合,每種類型的請求都有一個腳本。

使用領域模型模式設計業務邏輯

  • 模式:領域模型
    將業務邏輯組織爲具有狀態和行爲的類構成的對象模型

關於領域驅動設計
領域驅動設計(DDD)是對面向對象設計的改進,是開發複雜業務邏輯的一種方法。領域驅動設計的子域概念有助於把應用程序分解爲服務。使用DDD時,每個服務都有自己的領域模型,這就避免了在單個應用程序全局範圍內的領域模型問題。子域和相關聯的界限上下文的相關概念是兩種戰略性DDD模式
開發人員廣泛採用的基本元素包括以下幾種:

  1. 實體:具有持久化ID的對象。具有相同屬性值的兩個實體仍然是不同的對象
  2. 值對象:作爲值集合的對象。具有相同屬性值的兩個值對象可以互換使用。值對象的一個例子是Money類,它由幣種和金額組成
  3. 工廠:負責實現對象創建邏輯的對象或方法該邏輯過於複雜,無法由類的構造函數直接完成。它還可以隱藏被實例化的具體類。工廠方法一般可實現爲類的靜態方法
  4. 服務:實現不屬於實體或值對象的業務邏輯的對象

使用聚合模式設計領域模型

  • 聚合擁有明確的邊界
    聚合是一個邊界內的領域對象的集羣,可以將其視爲一個單元。它由一個根實體和可能的一個或多個其他實體和值對象組成。許多業務對象都被建模爲聚合

  • 模式:聚合
    將領域模型組織爲聚合的集合,每個聚合都是可以作爲一個單元進行處理的一組對象構成的圖。
    聚合將領域模型分解爲塊,單獨的每一塊更容易理解。它們還闡明瞭加載、更新和刪除等操作的範圍。這些操作作用於整個聚合而不是部分聚合。聚合通常從數據庫中完整加載,從而避免了延遲加載所導致的任何複雜性。刪除聚合會從數據庫中刪除其所有對象。

  • 聚合代表了一致的邊界
    更新整個聚合而不是聚合的一部分,可以解決一致性的問題。在聚合根上調用更新操作,這會強制執行各種不變量約束。此外,可以使用例如,版本號或數據庫級鎖鎖定聚合根來處理併發性。例如,客戶端必須在Order聚合的根上調用方法,而不是直接更新訂單項的數量,這會強制執行訂單的一些規則約束。

  • 識別聚合是關鍵
    在領域驅動設計中,設計領域模型的關鍵部分是識別聚合,以及它們的邊界和根。聚合內部結構的細節是次要的。然而,聚合的價值不僅僅是幫助我們設計模塊化的領域模型。更重要的是聚合必須遵守某些規則

  • 聚合的規則
    領域驅動設計要求聚合遵守一組規則。這些規則確保聚合是一個可以強制執行各種不變量約束的自包含單元
    規則一:只引用聚合根
    它要求聚合根是聚合中唯一可以由外部類引用的部分。客戶端只能通過調用聚合根上的方法來更新聚合。例如,服務使用存儲庫從數據庫加載聚合並獲取對聚合根的引用。它通過在聚合根上調用方法來更新聚合。此規則可確保聚合能夠強制執行各種不變量約束。

    規則二:聚合之間的引用必須使用主鍵
    另一個規則是引用聚合必須通過標識(列如,主鍵)而不是對象引用

    規則三:在一個事務中,只能創建或更新一個聚合
    聚合必須遵守的另一個規則是一個事務只能創建或更新一個聚合。在開發單體應用時,因爲有數據庫的事務可以更新多個聚合,所以用處不大,但是在微服務架構中,這個約束是完美的。它可以確保單個事務的範圍不超越服務的邊界。

     這個規則讓創建或更新多個聚合的操作變得更加複雜。但這是正式Saga旨在解決的問題。Saga的每一步都只創建或更新一個聚合
    
  • 聚合的顆粒度:
    在開發領域模型時,我們必須做出的關鍵決策是決定每個聚合的大小。一方面,聚合理想上應該很小。由於每個聚合的更新都是序列化的,因此更細粒度的聚合將提高應用程序能同時處理的請求數量,從而提高可擴展性。它還將改善用戶體驗,因爲它降低了兩個用戶嘗試同時更新一個聚合而引發衝突的可能性。但是,另一方面,因爲聚合事務的範圍,所以我們可能需要定義更大聚合以使特定的聚合更新操作滿足事務的原子性。

  • 使用聚合設計業務邏輯:
    在典型的微服務中,大部分業務邏輯由聚合組成。其餘業務邏輯存在於領域服務和Saga中。Saga編排本地事務的序列,以確保數據的一致性。服務是業務邏輯的入口,由入站適配器調用。服務使用存儲庫從數據庫中檢索聚合或聚合保存到數據庫。每個存儲庫都由數據庫的出站適配器實現

總結:

  1. 事務腳本模式通常是實現簡單業務邏輯的好方法。但是在實現複雜的業務邏輯時,應該考慮使用面向對象的領域模型模式
  2. 設計服務的業務邏輯的好方法是使用DDD聚合。DDD聚合很有用,因爲它們把領域模型模塊化,消除了服務之間對象的直接引用,並確保每個ACID事務都在服務內
  3. 創建或更新聚合時應發佈領域事件。領域事件具有廣泛的用途。領域事件的訂閱者還可以通知用戶和其他應用程序,並將WebSocket消息發佈到用戶的瀏覽器。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章