使用領域分析來建立微服務的模型

微服務最大的挑戰之一是如何定義個別服務之間的界限。 一般規則是服務應該執行「一件事」,但讓該規則實務需要仔細思考。 並沒有機械化的流程可以產生「正確的」設計。 您必須深入思考您的業務領域、需求和目標。 否則,您的設計可能會變成一場災難,表現出某些不理想的特性,例如在服務之間存有隱藏的相依性、彼此密不可分,或介面的設計不良。 本文說明設計微服務的網域驅動方法。

本文使用無人機遞送服務作為執行中的範例。 您可以 在這裡深入瞭解案例和對應的參考實作。

簡介

微服務應該依照商務功能來設計,而非依照水平層 (例如資料存取或傳訊)。 此外,微服務應該具有鬆散地結合和高度功能一致性的特性。 若您可以變更一項服務,而不需要同時更新其他服務,就表示微服務是「鬆散地結合」。 若微服務具有單一、明確定義的用途 (例如管理使用者帳戶或追蹤遞送記錄),就表示微服務具有「一致性」。 服務應納入領域知識,並從用戶端摘錄該知識。 例如,即使用戶端不知道排程演算法的詳細資訊或管理無人機大隊的方式,也應該能夠排程無人機。

領域導向的設計 (DDD) 可提供一套架構,讓您設計良好的微服務。 DDD 有兩個不同的階段:策略階段和戰術階段。 在策略性 DDD 中,您會定義系統的大規模結構。 策略性 DDD 有助於確保您的架構持續聚焦在商務功能上。 戰術性 DDD 提供了一組設計模式,您可以用來建立領域模型。 這些模式包括實體、彙總和領域服務。 這些戰術性模式可協助您設計既是鬆散地結合,又具有一致性的微服務。

領域導向的設計 (DDD) 程序圖

在本文中和下一篇文章中,我們將逐步執行下列步驟,並將其套用至無人機遞送應用程式:

  1. 先從分析業務領域開始,以了解應用程式的功能需求。 此步驟結果是領域的非正式描述,可以精簡成更加正式的一套領域模型。

  2. 接下來,定義領域的「限界內容」。 每個限界內容都會包含領域模型,後者代表較大應用程式的特定子領域。

  3. 在限界內容中,套用戰術性 DDD 模式來定義實體、彙總和領域服務。

  4. 使用來自上一個步驟的結果,以識別應用程式中的微服務。

在本文中,我們將討論前三個步驟,主要與 DDD 有關。 在下一篇文章中,我們將識別微服務。 不過,請務必記住 DDD 是種反覆且持續進行的流程。 服務界限並非一成不變的。 隨著應用程式的發展,您可能會決定將服務分割成數個較小的服務。

注意

此文章不會顯示完整和全方位的領域分析。 我們刻意讓範例保持簡短,以說明重點。 如需有關 DDD 的詳細背景資訊,我們推薦由 Eric Evans 所著、首先介紹 DDD 一詞的《Domain-Driven Design (領域導向的設計)》一書。 另一本良好的參考是 Vaughn Vernon 所著的《Implementing Domain-Driven Design》(實作領域驅動設計)。

案例:無人機遞送

Fabrikam, Inc. 正在推動無人機遞送服務。 該公司經營一個無人機隊。 企業會註冊此服務,而使用者可要求無人機收取貨物進行遞送。 當客戶排程取件後,後端系統就會指派一台無人機並將預估的遞送時間通知使用者。 在遞送過程中,客戶可以使用持續更新的 ETA 來追蹤無人機的位置。

此案例牽涉到相當複雜的領域。 其中一些企業的疑慮包括無人機排程、追蹤包裹、管理使用者帳戶,以及儲存和分析歷史資料。 此外,Fabrikam 希望能快速上市,而後快速反覆運作,並新增各項功能。 此應用程式必須以最高服務等級目標 (SLO) 在雲端規模運作。 Fabrikam 也預期不同的系統部分會有非常不同的資料儲存和查詢需求。 上述所有考量導致 Fabrikam 針對無人機遞送應用程式選擇微服務架構。

分析領域

使用 DDD 方法可協助您設計微服務,讓每個服務都能自然地貼近功能上的業務需求。 它可以協助您避開陷阱,讓您的設計不會受到組織界限或技術選擇的擺佈。

在撰寫任何程式碼之前,您需要以鳥瞰視野來檢視正在建立的系統。 DDD 首先將商務領域模型化,並建立 領域模型。 領域模型是業務領域的抽象模型。 它抽取並組織了領域知識,然後提供共通語言給開發人員和領域專家。

先從對應所有商務功能及其連線開始。 這可能是共同作業的工作,牽涉到領域專家、軟體架構設計人員與其他專案關係人。 您不需要使用任何特定的形式。 只需草繪圖表或在白板上繪圖即可。

繪出圖表後,您就可以開始識別不同的子領域。 有哪些功能是密切相關的? 哪些功能是業務核心?又有哪些功能會提供附屬服務? 什麼是相依性關係圖? 在此初始階段,您不必擔心技術或實作細節。 話雖如此,您仍應注意應用程式需要在何處與外部系統 (例如 CRM、付款處理或計費系統) 整合。

範例:無人機遞送應用程式

Fabrikam 小組在進行一些初始領域分析後,提出了用以說明無人機遞送領域的概略草圖。

無人機遞送領域圖

  • 由於出貨是業務的核心,因此位於圖表的中央。 在圖表中的所有其他項目,都是為了達成這項功能而存在。
  • 無人機管理也是業務的核心。 與無人機管理密切相關的功能包括無人機修復,以及使用預測性分析來預測無人機何時需要維護和維修。
  • 預計到達時間分析可提供預估的收件和遞送時間。
  • 若包裹無法完全交由無人機運送,第三方運輸會讓應用程式能夠排程替代運輸方式。
  • 無人機共用是核心業務可能的擴充功能。 公司在特定時段可能會有過剩的無人機,若不將其出租便會閒置。 在初始版本中不會有這項功能。
  • 視訊監視是公司稍後可能會擴及的另一個領域。
  • 使用者帳戶發票開立話務中心都是支援核心業務的子領域。

請注意,截至流程中的這個階段為止,我們尚未做出任何關於實作或技術的決策。 部分子系統可能會涉及外部軟體系統或第三方服務。 即便如此,應用程式仍需要與這些系統和服務互動,因此請務必將後兩者包含在領域模型中。

注意

應用程式要依存外部系統時,會有外部系統的資料結構描述或 API 洩露到您應用程式中的風險,最後可能會導致架構設計受到危害。 對可能未遵照新式最佳做法,以及可能是使用複雜曲折的資料結構描述或過時 API 的繼承系統來說,風險尤其大。 在此情況下,請務必要在這些外部系統與應用程式之間,妥善定義出界限。 請考慮針對此目的使用 Strangler Fig 模式反損毀層模式

定義限界內容

領域模型會包括真實世界中事物的代表 — 使用者、無人機、包裹等。 但這並不表示針對相同的事物,系統的每個部分都需要使用相同的代表。

例如,處理無人機修復和預測性分析的子系統必須代表無人機的許多實體特性,例如其維護歷程記錄、年齡、年齡、型號、效能特性等等。 但在排程遞送時,我們不會在意這些特性。 正在排程的子系統只需要知道無人機是否可用,以及收件和遞送的預計到達時間。

若我們嘗試針對這兩個子系統建立單一模型,該模型會過於複雜。 這也會讓模型難以隨著時間而進化,因為任何變更都需要能夠讓在不同子系統上工作的多個小組滿意。 因此,設計不同的模型,來代表兩個不同內容中的同一個真實世界實體 (在此案例中是無人機),通常是較好的方式。 每一個模型都只包含在其特定內容中相關的功能和屬性。

這是 限定內容的 DDD 概念開始作用的位置。 限界內容只是領域內的界限,系統會在此套用特定的領域模型。 對照先前的圖表,我們可以根據各種功能是否會共用單一的領域模型,來將功能分組。

限界內容圖

限界內容之間不一定彼此獨立。 在此圖中,連接限界內容的實心線條,代表兩個限界內容彼此互動的位置。 例如,「出貨」會依賴「使用者帳戶」來取得關於客戶的資訊,並依賴「無人機管理」對無人機大隊中的無人機進行排程。

在《Domain Driven Design (領域導向的設計)》一書中,Eric Evans 描述了在與其他限界內容互動時,數種維護領域模型完整性的模式。 其中一個微服務的主要原則是,服務會透過妥善定義的 API 進行通訊。 此方法可對應到 Evans 稱為「開放主機服務」和「發行的語言」的兩種模式。 開放主機服務的概念是子系統會為了與其通訊的其他子系統,定義正式的通訊協定 (API)。 發行的語言則延伸了此概念,以其他小組可用以寫入用戶端的形式來發行 API。 在 設計微服務的 API一文中,我們將討論如何使用 OpenAPI 規格 (先前稱為 Swagger) ,以 JSON 或 YAML 格式表示的與 REST API 無關的語言介面描述。

在本章的其餘部分,我們將著重在「出貨」限界內容。

下一步

完成定義域分析之後,下一個步驟是套用策略性 DDD,以更精確地定義領域模型。