分析應用程式及識別分解界限

已完成

為了將應用程式移動到微服務架構,Fabrikam 需要評估他們目前的應用程式,並判斷每個微服務的範圍和界限。 針對此評估,他們會使用「領域驅動設計」(DDD) 架構。 讓我們來看看如何將它套用到其應用程式。

注意

此文章不會顯示完整和全方位的領域分析。 我們特意讓範例保持簡短以說明重點。 如需有關 DDD 的詳細資訊,請參閱此課程模組結尾摘要中的<深入了解>一節。

什麼是領域驅動設計?

DDD 是一種系統設計方法,一開始是由 Erik Evans 在 2005 年的著作 Domain-Driven Design: Tackling Complexity in the Heart of Software 中所提出。 這種方法包含三個關鍵元素:

  • 聚焦於核心領域和領域邏輯。
  • 在領域模型上建構設計。
  • 驅動技術小組和商業夥伴間的反覆式共同作業來持續改善系統。

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

Diagram of the steps for domain-driven design.

在 DDD 的策略性階段期間,您會對應業務領域並為領域模型定義限界內容。 在戰術性 DDD 中則會更加精確地定義領域模型。 戰術性模式會在單一限界內容中套用。 在微服務架構中,我們對實體和彙總模式感興趣。 套用這些模式可協助我們識別應用程式中服務的自然界限。 一般而言,微服務應大於或等於彙總,且小於或等於限界內容。

從高層級而言,您可以將此程序分成四個步驟:

  1. 分析業務領域以了解應用程式的功能需求。 此步驟結果是領域的非正式描述,可以精簡成更加正式的一套領域模型。
  2. 定義領域的限界內容。 每個限界內容都會包含領域模型,後者代表較大應用程式的特定子領域。
  3. 在限界內容中,套用戰術性 DDD 模式來定義實體、彙總和領域服務。
  4. 使用來自上一個步驟的結果來識別應用程式中的微服務。

讓我們仔細看看每個步驟所進行的工作。

分析業務領域

DDD 一開始會先將業務領域模型化並建立領域模型。 領域模型是業務領域的抽象模型。 它會抽取並組織領域知識,然後提供共通語言給開發人員和領域專家。

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

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

Diagram of the business domain.

定義限界內容

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

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

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

針對此方法,DDD 的「限界內容」概念就可派上用場。 限界內容只是領域內的界限,系統會在此套用特定的領域模型。 對照先前的圖表,我們可以根據各種功能是否會共用單一領域模型,來將功能分組。

Diagram of the bounded contexts for the drone application.

定義實體、彙總和服務

在戰術性 DDD 中則會更加精確地定義領域模型。 戰術性模式會在單一限界內容中套用。 在微服務架構中,我們對實體和彙總模式感興趣。 套用這些模式可協助我們識別應用程式中服務的自然界限。 一般而言,微服務應大於或等於彙總,且小於或等於限界內容。

有幾個要考慮的戰術 DDD 模式:

  • 實體:實體是具有唯一身分識別且會持續一段時間的物件。 例如,在銀行應用程式中,客戶和帳戶會是實體。
  • 值物件:值物件沒有身分識別。 其屬性的值會定義它,而且是不可變的。 值物件的典型範例包括色彩、日期和時間,以及貨幣值。
  • 彙總:彙總定義了一或多個實體周圍的一致性界限。 彙總的目的是將交易的不變項目模型化。 真實世界中的事物具有複雜的關聯性。 客戶建立訂單、訂單包含產品、產品具有供應商等等。 如果應用程式修改數個相關物件,其如何保證一致性? 我們要如何追蹤不變項目並加以強制執行?
  • 領域和應用程式服務:在 DDD 術語中,服務是指會實作某些邏輯,而不保留任何狀態的物件。 Evans 將領域服務 (會封裝領域邏輯) 和應用程式服務 (會提供技術功能) 兩者加以區分。 應用程式服務通常包含技術功能,例如使用者驗證或傳送 SMS 訊息。 領域服務通常會用以將跨越多個實體的行為模型化。
  • 領域事件:領域事件可在有情況發生時用來通知系統的其他部分。 正如其名,領域事件應代表領域中所發生的事物。 例如,「已將記錄插入資料表中」並不是領域事件。 「已取消遞送」則是領域事件。 領域事件在微服務架構中特別具有關聯性。 因為微服務是分散式的且不會共用資料存放區,因此領域事件可提供方式讓微服務相互協調。

Diagram of the drone domain model.

在他們的系統中,Fabrikam 開發小組已識別出下列實體:

  • 遞送
  • 套件
  • 無人機
  • 客戶​​
  • 確認
  • 通知
  • 標記

前四個實體 (遞送、包裹、無人機和帳戶),全都是代表交易一致性界限的彙總。 「確認」和「通知」是「遞送」的子實體。 「標籤」則是「包裹」的子實體。

在此設計中的值物件包括 Location、ETA、PackageWeight 和 PackageSize。

有兩個領域事件:

  • 無人機在飛行時,「無人機」實體會傳送描述無人機位置和狀態 (例如飛行中、已著陸) 的 DroneStatus 事件。
  • 每當遞送階段有所變更時,「遞送」實體就會傳送 DeliveryTracking 事件。 這些事件包括 DeliveryCreated、DeliveryRescheduled、DeliveryHeadedToDropoff 和 DeliveryCompleted。

請注意,這些事件會描述領域模型中有意義的事物。 它們會描述關於領域的內容,且不會繫結到特定的程式設計語言建構。

開發小組還識別出另一個功能領域,但無法明確歸類為目前為止所描述的任何實體。 系統的某些部分必須協調排程或更新遞送時所涉及的所有步驟。 開發小組已將兩個領域服務新增到設計中。 「排程器」會協調步驟。 「監督員」會監視每個步驟的狀態,以偵測是否有任何步驟失敗或逾時。

識別微服務

而現在,我們已準備好從領域模型階段進入應用程式設計階段。 以下是您可以用來從領域模型衍生微服務的方法。

  1. 從限定內容開始。 一般而言,微服務中的功能不應跨越一個以上的限定內容。 根據定義,限界內容會標示特定領域模型的界限。 如果您的某個微服務會將不同的領域模型混合在一起,這表示您可能需要重新琢磨您的領域分析。
  2. 接著,查看領域模型中的彙總。 彙總通常很適合作為微服務。 設計良好的彙總會顯示出擁有良好設計之微服務的許多特性:
    • 彙總是衍生自商務需求,而非出於技術方面的顧慮 (例如資料存取或傳訊)。
    • 彙總應該要高度銜接各項功能。
    • 彙總是持續性的界限。
    • 彙總應該是鬆散耦合的。
  3. 領域服務通常很適合作為微服務。 領域服務是跨多個彙總的無狀態作業。 典型的範例是牽涉到數個微服務的工作流程。 稍後,我們會在無人機遞送應用程式中看到領域服務的範例。
  4. 最後,請考慮非功能性需求。 審視各個因素,例如小組規模、資料類型、技術、延展性需求、可用性需求和安全性需求。 這些因素可能會讓您進一步將微服務分解成兩個 (或以上) 較小服務,或反過來將數個微服務合併成一個。

最重要的是,請務必要務實,並記得領域驅動設計是需要不斷反覆的程序。 當您猶豫不定時,請從更粗略的微服務來開始著手。 將微服務分割成兩個較小的服務,會比重構數個現有微服務上的功能來得簡單。

Diagram of the microservices.

將領域驅動設計套用至無人機應用程式

對於 Fabrikam 的應用程式,這些服務全都位於其現有的整合型應用程式中。 識別他們可以將其應用程式分解成微服務的位置之後,他們將會從封裝服務開始。

封裝服務目前有一個專門的開發小組,其已呈現與擴充性相關的效能問題,且是開始分解應用程式的絕佳候選項目。