手機版
你好,游客 登錄 注冊
背景:
閱讀新聞

驅動領域DDD的微服務設計和開發實戰

[日期:2020-01-03] 來源:cnblogs.com/burningmyself  作者:yfb [字體: ]

你是否還在為微服務應該拆多小而爭論不休?到底如何才能設計出收放自如的微服務?怎樣才能保證業務領域模型與代碼模型的一致性?或許本文能幫你找到答案。 本文是基于 DDD 的微服務設計和開發實戰篇,通過借鑒領域驅動設計思想,指導微服務項目團隊進行設計和開發(理論篇詳見《當中臺遇上 DDD,我們該如何設計微服務?》)。本文包括三部分內容:第一部分講述領域驅動設計基本知識,包括:分層架構、服務視圖、數據視圖和領域事件發布和訂閱等;第二部分講述微服務設計方法、過程、模板、代碼目錄、設計原則等內容;最后部分以一個項目為例講述基于 DDD 的微服務設計過程。

目標¶

本文采用 DDD(領域驅動設計)作為微服務設計指導思想,通過事件風暴建立領域模型,合理劃分領域邏輯和物理邊界,建立領域對象及服務矩陣和服務架構圖,定義符合 DDD 分層架構思想的代碼結構模型,保證業務模型與代碼模型的一致性。通過上述設計思想、方法和過程,指導團隊按照 DDD 設計思想完成微服務設計和開發。 通過領域模型和 DDD 的分層思想,屏蔽外部變化對領域邏輯的影響,確保交付的軟件產品是邊界清晰的微服務,而不是內部邊界依然混亂的小單體。在需求和設計變化時,可以輕松的完成微服務的開發、拆分和組合,確保微服務不易受外部變化的影響,并穩定運行。

適用范圍¶

本文適用于按照 DDD 設計方法進行微服務設計和開發的項目及相關人員。

DDD 分層架構視圖¶

DDD 分層架構包括:展現層、應用層、領域層和基礎層。

DDD 分層架構各層職能如下:

  • 展現層
    展現層負責向用戶顯示信息和解釋用戶指令。
  • 應用層
    應用層是很薄的一層,主要面向用戶用例操作,協調和指揮領域對象來完成業務邏輯。應用層也是與其他系統的應用層進行交互的必要渠道。應用層服務盡量簡單,它不包含業務規則或知識,只為下一層的領域對象協調任務,使它們互相協作。應用層還可進行安全認證、權限校驗、分布式和持久化事務控制或向外部應用發送基于事件的消息等。
  • 領域層
    領域層是軟件的核心所在,它實現全部業務邏輯并且通過各種校驗手段保證業務正確性。它包含業務所涉及的領域對象(實體、值對象)、領域服務以及它們之間的關系。它負責表達業務概念、業務狀態以及業務規則,具體表現形式就是領域模型。
  • 基礎層
    基礎層為各層提供通用的技術能力,包括:為應用層傳遞消息、提供 API 管理,為領域層提供數據庫持久化機制等。它還能通過技術框架來支持各層之間的交互。

服務視圖¶

微服務內的服務視圖

微服務內有 Facade 接口、應用服務、領域服務和基礎服務,各層服務協同配合,為外部提供服務。

1、接口服務¶

接口服務位于用戶接口層,用于處理用戶發送的 Restful 請求和解析用戶輸入的配置文件等,并將信息傳遞給應用層。

2、應用服務¶

應用服務位于應用層。用來表述應用和用戶行為,負責服務的組合、編排和轉發,負責處理業務用例的執行順序以及結果的拼裝。 應用層的服務包括應用服務和領域事件相關服務。 應用服務可對微服務內的領域服務以及微服務外的應用服務進行組合和編排,或者對基礎層如文件、緩存等數據直接操作形成應用服務,對外提供粗粒度的服務。 領域事件服務包括兩類:領域事件的發布和訂閱。通過事件總線和消息隊列實現異步數據傳輸,實現微服務之間的解耦。

3、領域服務¶

領域服務位于領域層,為完成領域中跨實體或值對象的操作轉換而封裝的服務,領域服務以與實體和值對象相同的方式參與實施過程。 領域服務對同一個實體的一個或多個方法進行組合和封裝,或對多個不同實體的操作進行組合或編排,對外暴露成領域服務。領域服務封裝了核心的業務邏輯。實體自身的行為在實體類內部實現,向上封裝成領域服務暴露。 為隱藏領域層的業務邏輯實現,所有領域方法和服務等均須通過領域服務對外暴露。 為實現微服務內聚合之間的解耦,原則上禁止跨聚合的領域服務調用和跨聚合的數據相互關聯。

4、基礎服務¶

基礎服務位于基礎層。為各層提供資源服務(如數據庫、緩存等),實現各層的解耦,降低外部資源變化對業務邏輯的影響。 基礎服務主要為倉儲服務,通過依賴反轉的方式為各層提供基礎資源服務,領域服務和應用服務調用倉儲服務接口,利用倉儲實現持久化數據對象或直接訪問基礎資源。

微服務外的服務視圖¶

1. 前端應用與微服務¶

微服務中的應用服務通過用戶接口層組裝和數據轉換后,發布在 API 網關,為前端應用提供數據展示服務。

2. 微服務與外部應用¶

跨微服務數據處理時,對實時性要求高的場景,可選擇直接調用應用服務的方式(新增和修改類型操作需關注事務一致性)。對實時性要求不高的場景,可選擇異步化的領域事件驅動機制(最終數據一致性)。

數據視圖¶

DDD 分層架構中數據對象轉換的過程如下圖。

數據視圖應用服務通過數據傳輸對象(DTO)完成外部數據交換。領域層通過領域對象(DO)作為領域實體和值對象的數據和行為載體?;A層利用持久化對象(PO)完成數據庫的交換。 DTO 與 VO 通過 Restful 協議實現 JSON 格式和對象轉換。 前端應用與應用層之間 DTO 與 DO 的轉換發生在用戶接口層。如微服務內應用服務需調用外部微服務的應用服務,則 DTO 的組裝和 DTO 與 DO 的轉換發生在應用層。 領域層 DO 與 PO 的轉換發生在基礎層。

領域事件和事件總線¶

領域事件是領域模型中非常重要的部分,用來表示領域中發生的事件。一個領域事件將導致進一步的業務操作,有助于形成完整的業務閉環。領域事件主要用于解耦微服務,各個微服務之間不再是強一致性,而是基于事件的最終一致性。

微服務內的領域事件¶

微服務內的領域事件可以通過事件總線或利用應用服務實現不同聚合之間的業務協同。當微服務內發生領域事件時,由于大部分事件的集成發生在同一個線程內,不一定需要引入消息中間件。但一個事件如果同時更新多個聚合數據,按照 DDD“一個事務只更新一個聚合根”的原則,可以考慮引入消息中間件,通過異步化的方式,對微服務內不同的聚合根采用不同的事務。

微服務之間的領域事件¶

微服務之間的數據交互方式通常有兩種:應用服務調用和領域事件驅動機制。 領域事件驅動機制更多的用于不同微服務之間的集成,實現微服務之間的解耦。事件庫(表)可以用于微服務之間的數據對賬,在應用、網絡等出現問題后,可以實現源和目的端的數據比對,在數據暫時不一致的情況下仍可根據這些數據完成后續業務處理流程,保證微服務之間數據的最終一致性。 應用服務調用方式通常應用于實時性要求高的業務場景,但一旦涉及到跨微服務的數據修改,將會增加分布式事務控制成本,影響系統性能,微服務之間的耦合度也會變高。

事件總線¶

事件總線位于基礎層,為應用層和領域層服務提供事件消息接收和分發等服務。其大致流程如下: 1、服務觸發并發布事件。 2、事件總線事件分發。

  • 如果是微服務內的訂閱者(微服務內的其它聚合),則直接分發到指定訂閱者。
  • 如果是微服務外的訂閱者,則事件消息先保存到事件庫(表)并異步發送到消息中間件。
  • 如果同時存在微服務內和外訂閱者,則分發到內部訂閱者,并將事件消息保存到事件庫(表)并異步發送到消息中間件。為了保證事務的一致性,事件表可以共享業務數據庫。也可以采用多個微服務共享事件庫的方式。當業務操作和事件發布操作跨數據庫時,須保證業務操作和事件發布操作數據的強一致性。

事件數據持久化¶

事件數據的持久化存儲可以有兩種方案,在項目實施過程中根據具體場景選擇最佳方案。

  • 事件數據保存到微服務所在業務數據庫的事件表中,利用本地事務保證業務操作和事件發布操作的強一致性。
  • 事件數據保存到多個微服務共享的事件庫中。需要注意的一點是:這時業務操作和事件發布操作會跨數據庫操作,須保證事務的強一致性(如分布式事務機制)。

事件數據的持久化可以保證數據的完整性,基于這些數據可以完成跨微服務數據的一致性比對。

微服務設計方法¶

事件風暴¶

本階段主要完成領域模型設計。 基于 DDD 的微服務設計通常采用事件風暴方法。通過事件風暴完成領域模型設計,劃分出微服務邏輯邊界和物理邊界,定義領域模型中的領域對象,指導微服務設計和開發。事件風暴通常包括產品愿景、場景分析、領域建模、微服務設計和拆分等過程。本文不對事件風暴詳細方法做深入描述,如感興趣可查閱相關資料。

  • 1、產品愿景

產品愿景是對產品的頂層價值設計,對產品目標用戶、核心價值、差異化競爭點等信息達成一致,避免產品偏離方向。建議參與角色:業務需求方、產品經理和開發組長。

  • 2、場景分析

場景分析是從用戶視角出發,探索業務領域中的典型場景,產出領域中需要支撐的場景分類、用例操作以及不同子域之間的依賴關系,用以支撐領域建模。 建議參與角色:產品經理、需求分析人員、架構師、開發組長和測試組長。

  • 3、領域建模

領域建模是通過對業務和問題域進行分析,建立領域模型,向上通過限界上下文指導微服務邊界設計,向下通過聚合指???實體的對象設計。 建議參與角色:領域專家、產品經理、需求分析人員、架構師、開發組長和測試組長。

  • 4、微服務拆分和設計

結合業務限界上下文與技術因素,對服務的粒度、分層、邊界劃分、依賴關系和集成關系進行梳理,完成微服務拆分和設計。 微服務設計應綜合考慮業務職責單一、敏態與穩態業務分離、非功能性需求(如彈性伸縮要求、安全性等要求)、團隊組織和溝通效率、軟件包大小以及技術異構等因素。 建議參與角色:產品經理、需求分析人員、架構師、開發組長和測試組長。

領域對象及服務矩陣和代碼模型設計¶

本階段完成領域對象及服務矩陣文檔以及微服務代碼模型設計。

  • 1、領域對象及服務矩陣

根據事件風暴過程領域對象和關系,對產出的限界上下文、聚合、實體、值對象、倉儲、事件、應用服務、領域服務等領域對象以及各對象之間的依賴關系進行梳理,確定各對象在分層架構中的位置和依賴關系,建立領域對象分層架構視圖,為每個領域對象建立與代碼模型對象的一一映射。 建議參與角色:架構師和開發組長。

  • 2、微服務代碼模型

根據領域對象在 DDD 分層架構中所在的層、領域類型、與代碼對象的映射關系,定義領域對象在微服務代碼模型中的包、類和方法名稱等,設計微服務工程的代碼層級和代碼結構,明確各層間的調用關系。 建議參與角色:架構師和開發組長。

領域對象及服務矩陣樣例說明¶

領域對象及服務矩陣主要用來記錄事件風暴和微服務設計過程中產出的領域對象屬性,如:各領域對象在 DDD 分層架構中的位置、屬性、依賴關系以及與代碼對象的映射關系等。通過建立領域對象與代碼對象的映射關系,可指導軟件開發人員準確無誤的按照設計文檔完成微服務開發。 以下為領域對象及服務矩陣樣例(部分數據,僅供參考)。

各欄說明如下: * 層:

定義領域對象位于 DDD 分層架構中的哪一層。如:接口層、應用層、領域層以及基礎層等。

  • 聚合:

在事件風暴過程中將關聯緊密的實體和值對象等組合形成聚合。本欄說明聚合名稱。 * 領域對象名稱:

領域模型中領域對象的具體名稱。如:“請假審批已通過”是類型為“事件”的領域對象;“請假單”是領域類型為“實體”的領域對象。

  • 領域類型:

在領域模型中根據 DDD 知識域定義的領域對象的類型,如:限界上下文、聚合、聚合根(實體)、實體、值對象、事件、命令、應用服務、領域服務和倉儲服務等。 依賴對象名稱:根據業務對象依賴或分層調用依賴關系建立的領域對象的依賴關系(如服務調用依賴、關聯對象聚合等)。本欄說明領域對象需依賴的其他領域對象,如上層服務在組合和編排過程中對下層服務的調用依賴、實體之間或者實體與值對象在聚合內的依賴等。

  • 包名:

代碼模型中的包名,本欄說明領域對象所在的軟件包。

  • 類名:

代碼模型中的類名,本欄說明領域對象的類名。

  • 方法名:

代碼模型中的方法名,本欄說明領域對象實現或操作的方法名。

微服務代碼結構模型¶

微服務代碼模型最終結果來源于領域對象及服務矩陣。在代碼模型設計時須建立領域對象和代碼對象的一一映射,保證業務模型與代碼模型的一致性,即使不熟悉業務的開發人員或者不熟悉代碼的業務人員也可以很快定位到代碼位置。

微服務代碼總目錄¶

基于 DDD 的代碼模型包括 interfaces、application、domain 和 infrastructure 四個目錄。

  • Interfaces(用戶接口層):

本目錄主要存放用戶接口層代碼。前端應用通過本層向應用服務獲取展現所需的數據。本層主要用于處理用戶發送的 Restful 請求和解析用戶輸入的配置文件等,并將信息傳遞給 Application 層。主要代碼形態是數據組裝以及 Facade 接口等。

  • Application(應用層):

本目錄主要存放應用層代碼。應用服務代碼基于微服務內的領域服務或微服務外的應用服務完成服務編排和組合。為用戶接口層提供各種應用數據展現支持。主要代碼形態是應用服務和領域事件等。

  • Domain(領域層):

本目錄主要存放領域層代碼。本層代碼主要實現核心領域邏輯,其主要代碼形態是實體類方法和領域服務等。

  • Infrastructure(基礎層):

本目錄存放基礎層代碼,為其它各層提供通用技術能力、三方軟件包、配置和基礎資源服務等。

用戶接口層代碼模型¶

用戶接口層代碼模型目錄包括:assembler、dto 和 facade。

  • Assembler:實現 DTO 與領域對象之間的相互轉換和數據交換。理論上 Assembler 總是與 DTO 一同被使用。

  • Dto:數據傳輸的載體,內部不存在任何業務邏輯,通過 DTO 把內部的領域對象與外界隔離。

  • Facade:提供較粗粒度的調用接口,將用戶請求委派給一個或多個應用服務進行處理。

應用層代碼模型¶

應用層代碼模型目錄包括:event 和 service。

  • Event(事件):事件目錄包括兩個子目錄:publish 和 subscribe。publish 目錄主要存放微服務內領域事件發布相關代碼。subscribe 目錄主要存放微服務內聚合之間或外部微服務領域事件訂閱處理相關代碼。為了實現領域事件的統一管理,微服務內所有領域事件(包括應用層和領域層事件)的發布和訂閱處理都統一放在應用層。

  • Service(應用服務):這里的服務是應用服務。應用服務對多個領域服務或外部應用服務進行封裝、編排和組合,對外提供粗粒度的服務。

領域層代碼模型¶

微服務領域層包括一個或多個聚合代碼包。標準的聚合代碼模型包括:entity、repository 和 service 三個子目錄。

  • Aggregate(聚合):聚合代碼包的根目錄,實際項目中以實際業務屬性的名稱來命名。聚合定義了領域對象之間的關系和邊界,實現領域模型的內聚。
  • Entity(實體):存放實體(含聚合根、實體和值對象)相關代碼。同一實體所有相關的代碼(含對同一實體類多個對象操作的方法,如對多個對象的 count 等)都放在一個實體類中。
  • Service(領域服務):存放對多個不同實體對象操作的領域服務代碼。這部分代碼以領域服務的形式存在,在設計時一個領域服務對應一個類。
  • Repository(倉儲):存放聚合對應的查詢或持久化領域對象的代碼,通常包括倉儲接口和倉儲實現方法。為了方便聚合的拆分和組合,我們設定一個原則:一個聚合對應一個倉儲。
  • 特別說明:按照 DDD 分層原則,倉儲實現本應屬于基礎層代碼,但為了微服務代碼拆分和重組的便利性,我們把聚合的倉儲實現代碼放到了領域層對應的聚合代碼包內。如果需求或者設計發生變化導致聚合需要拆分或重新組合時,我們可以聚合代碼包為單位,輕松實現微服務聚合的拆分和組合。

基礎層代碼模型¶

基礎層代碼模型包括:config 和 util 兩個子目錄。

  • Config:主要存放配置相關代碼。
  • Util:主要存放平臺、開發框架、消息、數據庫、緩存、文件、總線、網關、第三方類庫、通用算法等基礎代碼,可為不同的資源類別建立不同的子目錄。

微服務總目錄結構¶

微服務總目錄結構如下:

微服務設計原則¶

微服務設計原則中如高內聚低耦合、復用、單一職責等原則在此就不贅述了,這里主要強調以下幾條:

  • 第一條:“要領域驅動設計,而不是數據驅動設計,也不是界面驅動設計”。

微服務設計首先應建立領域模型,確定邏輯和物理邊界后,然后才進行微服務邊界拆分,而不是一上來就定義數據庫表結構,也不是界面需要什么,就去調整領域邏輯代碼。 領域模型和領域服務應具有高度通用性,通過接口層和應用層屏蔽外部變化對業務邏輯的影響,保證核心業務功能的穩定性。

  • 第二條:“要邊界清晰的微服務,而不是泥球小單體”。

微服務完成開發后其功能和代碼也不是一成不變的。隨著需求或設計變化,微服務內的代碼也會分分合合。邏輯邊界清晰的微服務,可快速實現微服務代碼的拆分和組合。DDD 思想中的邏輯邊界和分層設計也是為微服務各種可能的分分合合做準備的。 微服務內聚合與聚合之間的領域服務以及數據原則上禁止相互產生依賴。如有必要可通過上層的應用服務編排或者事件驅動機制實現聚合之間的解耦,以利于聚合之間的組合和拆分。

  • 第三條:“要職能清晰的分層,而不是什么都放的大籮筐”。

分層架構中各層職能定位清晰,且都只能與其下方的層發生依賴,也就是說只能從外層調用內層服務,內層服務通過封裝、組合或編排對外逐層暴露,服務粒度由細到粗。 應用層負責服務的編排和組合,領域層負責領域業務邏輯的實現,基礎層為各層提供資源服務。

  • 第四條:“要做自己能 hold 住的微服務,而不是過度拆分的微服務”

微服務的過度拆分必然會帶來軟件維護成本的上升,如:集成成本、運維成本以及監控和定位問題的成本。企業轉型過程中很難短時間內提升這些能力,如果項目團隊不具備這些能力,將很難 hold 住這些過細的微服務。而如果我們在微服務設計之初就已經定義好了微服務內的邏輯邊界,項目初期我們可以盡可能少的拆分出過細的微服務,隨著技術的積累和時間的推移,當我們具有這些能力后,由于微服務內有清晰的邏輯邊界,這時就可以隨時根據需要輕松的拆分或組合出新的微服務。

不同場景的微服務設計¶

微服務的設計先從領域建模開始,領域模型是微服務設計的核心,微服務是領域建模的結果。在微服務設計之前,請先判斷你的業務是否聚焦在領域和領域邏輯。 實際在做系統設計時我們可能面臨各種不同的情形,如從傳統單體拆分為多個微服務,也可能是一個全新領域的微服務設計(如創業中的應用),抑或是將一個單體中面臨問題或性能瓶頸的模塊拆分為微服務而其余功能仍為單體的情況。 下面分幾類不同場景說明如何進行微服務和領域模型設計。

新建系統的微服務設計¶

新建系統會遇到復雜和簡單領域兩種場景,兩者的領域建模過程也會有所差別。

1、簡單領域的建模¶

對于簡單的業務領域,一個領域可能就是一個小的子域。領域建模過程相對簡單,根據事件風暴可以分解出事件、命令、實體、聚合和限界上下文等,根據領域模型和微服務拆分原則設計出微服務即可。

2、復雜領域的建模¶

對于復雜的業務領域,領域可能還需要拆分為子域,甚至子域還會進一步拆分,如:保險領域可以拆分為承保、理賠、收付費和再保等子域,承保子域還可以再拆分為投保、保單管理等子子域。對于這種復雜的領域模型,是無法通過一個事件風暴完成領域建模的,即使能完成,其工程量也是非常浩大,效果也不一定好。 對于這種復雜的領域,我們可以分三階段來完成領域模型和微服務設計。

  • 拆分子域建立領域模型:根據業務特點考慮流程節點或功能模塊等邊界因素(微服務最終的拆分結果很多時候跟這些邊界因素有一定的相關性),按領域逐級分解為大小合適的子域,針對子域進行事件風暴,記錄領域對象、聚合和限界上下文,初步確定各級子域的領域模型。

  • 領域模型微調:梳理領域內所有子域的領域模型,對各子域模型進行微調,這個過程重點考慮不同限界上下文內聚合的重新組合,同步需要考慮子域、限界上下文以及聚合之間的邊界、服務以及事件之間的依賴關系,確定最終的領域模型。

  • 微服務設計和拆分:根據領域模型的限界上下文和微服務的拆分原則,完成微服務的拆分和設計。

單體遺留系統的微服務設計¶

如果一個單體遺留系統,只是將面臨問題或性能瓶頸的模塊拆分為微服務,而其余功能仍為單體。我們只需要將這些特定功能領域理解為一個簡單的子領域,按照簡單領域建模方式進行領域模型的設計即可。但在新微服務設計中需要考慮新老系統之間的服務協議,必要時引入防腐層。

特別說明¶

雖然有些業務領域在事件風暴后發現無法建立領域模型,如數據處理或分析類場景,但本文所述的分層架構模型、服務之間規約和代碼目錄結構在微服務設計和開發中仍然是通用的。

基于 DDD 的微服務設計和開發實例¶

為了更好的理解 DDD 的設計思想和過程,我們用一個場景簡單但基本涵蓋 DDD 設計思想的項目來說明微服務設計和開發過程。

項目基本信息¶

項目主要目標是實現在線請假和考勤管理?;竟δ馨ǎ赫埣?、考勤以及人員管理等。

  • 請假:請假人填寫請假單提交審批,根據請假人身份和請假天數進行校驗,根據審批規則逐級遞交審批,核批通過則完成審批。
  • 考勤:根據考勤規則,剔除請假數據后,對員工考勤數據進行校驗,輸出考勤統計表。
  • 人員管理:維護人員基本信息和上下級關系。 ......

設計和實施步驟¶

步驟一:事件風暴¶

由于項目目標基本明確,我們在事件風暴過程中裁剪了產品愿景,直接從用戶旅程和場景分析開始。

  • 1、場景分析:場景分析是一個發散的過程。根據不同角色的旅程和場景分析,盡可能全面的梳理??前端操作到后端業務邏輯發生的所有操作、命令、領域事件以及外部依賴關系等信息(如下圖),如:請假人員會執行創建請假信息操作命令,審批人員會執行審批操作,請假審批通過后會產生領域事件,通知郵件系統反饋請假人員結果,并將請假數據發送到考勤以便核銷等。在記錄這些領域對象的同時,我們也會標記各對象在 DDD 中的層和對象類型等屬性,如:應用服務、領域服務、事件和命令等類型。

  • 2、領域建模:領域建模是一個收斂的過程。這個收斂過程分三步:第一步根據場景分析中的操作集合定義領域實體;第二步根據領域實體業務關聯性,定義聚合;第三步根據業務及語義邊界等因素,定義限界上下文。

    • 定義領域實體:在場景分析過程中梳理完操作、命令、領域事件以及外部依賴關系等領域對象后。分析這些操作應由什么實體發起或產生,從而定義領域實體對象,并將這些操作與實體進行關聯。 在請假場景中,經分析需要有請假單實體對象,請假單實體有創建請假信息以及修改請假信息等操作。
    • 定義聚合:將業務緊密相關的實體進行組合形成聚合,同時確定聚合中的聚合根、值對象和實體。經分析項目最終形成三個聚合:人員管理、請假和考勤。在請假聚合中有請假單、審批軌跡、審批規則等實體,其中請假單是聚合根,審批軌跡是請假單的值對象,審批規則是輔助實體。
    • 聚合內須保證業務操作的事務性,高度內聚的實體對象可自包含完成本領域功能。聚合是可拆分為微服務的最小單元。在同一限界上下文內多個聚合可以組合為一個微服務。如有必要,也可以將某一個聚合獨立為微服務。
    • 定義限界上下文:根據領域及語義邊界等因素確定限界上下文,將同一個語義環境下的一個或者多個聚合放在一個限界上下文內。由于人員管理與請假聚合兩者業務關聯緊密,共同完成人員請假功能,兩者一起構成請假限界上下文,考勤聚合則單獨形成考勤限界上下文。
  • 3、微服務設計和拆分:理論上一個限界上下文可以設計為一個微服務,但還需要綜合考慮多種外部因素,如:職責單一性、性能差異、版本發布頻率、團隊溝通效率和技術異構等要素。

由于本項目微服務設計受技術以及團隊等因素影響相對較小,主要考慮職責單一性,因此根據限界上下文直接拆分為請假和考勤兩個微服務。其中請假微服務包含人員和請假兩個聚合,考勤微服務只包含考勤聚合。

步驟二、領域對象及服務矩陣¶

將事件風暴中產出的領域對象按照各自所在的微服務進行分類,定義每個領域對象在微服務中的層、領域類型和依賴的領域對象等。

這個步驟最關鍵的工作是確定實體、方法、服務等領域對象在微服務分層架構中的位置以及各對象之間的依賴關系,形成服務矩陣(如下表)。這個過程也將在事件風暴數據的基礎上,進一步細化領域對象以及它們之間關系,并補充事件風暴中可能遺漏的細節。

確定完各領域對象的屬性后,按照代碼模型設計各個領域對象在代碼模型中的代碼對象(包括代碼對象所在的:包名、類名和方法名),建立領域對象與代碼對象的一一映射關系。根據這種映射關系,相關人員可快速定位到業務邏輯所在的代碼位置。

步驟三:領域模型及服務架構¶

根據領域模型中領域對象屬性以及服務矩陣,畫出領域對象及服務架構視圖(如下圖)。這個視圖可以作為標準的 DDD 分層領域服務架構視圖模型,應用在不同的領域模型中。這個模型可以清晰的體現微服務內實體、聚合之間的關系,各層服務之間的依賴關系以及應用層服務組合和編排的關系,微服務之間的服務調用以及事件驅動的前后處理邏輯關系。 在這個階段,前端的設計也可以同步進行,在這里我們用到了微前端的設計理念,為請假和考勤微服務分別設計了請假和考勤微前端,基于微前端和微服務,形成從前端到后端的業務邏輯自包含組件。兩個微前端之上有一個集成主頁面,可根據頁面流動態加載請假和考勤的微前端頁面。

步驟四:代碼模型設計¶

根據 DDD 的代碼結構模型和各領域對象在所在的包、類和方法,定義出請假微服務的代碼結構模型。應用層代碼結構包括:應用服務以及事件發布相關代碼(如下圖)。

領域層代碼結構包括一個或多個聚合的實體類以及領域服務相關代碼(如下圖)。在本項目中請假微服務領域層包含了請假和人員兩個聚合。

領域模型中的一個聚合對應一個聚合代碼包,如:人員和請假領域邏輯代碼都放在各自的聚合代碼包中,如隨著業務發展,人員管理功能需要從請假微服務中拆分出來,我們只需要將人員聚合代碼包稍加改造并獨立部署即可快速發布為人員管理微服務。

步驟五:詳細設計¶

在完成領域模型和代碼模型設計后,我們就可以開始詳細設計了,詳細設計主要結合具體的業務功能來開展,主要工作包括:系統界面、數據庫表以及字段、服務參數規約及功能等。

步驟六:代碼開發¶

軟件開發人員只需要按照設計文檔和功能要求,找到業務功能對應的代碼位置,完成代碼開發和服務編排即可。

步驟七:測試和發布¶

完成代碼開發后,由開發人員編寫單元測試用例,基于擋板模擬依賴對象完成跨服務的測試。單元測試完成后,在團隊內可進一步完成微服務與相應微前端的集成和測試,形成請假和考勤兩個業務組件。前端主頁面完成請假和考勤微前端頁面集成和頁面流及組件基礎數據配置,主頁面可以按照頁面流程動態加載請假和考勤微前端頁面。最終部署的軟件包包括:請假和考勤兩個微服務,請假和考勤兩個微前端,一個主頁面共計五個。這五個部署包獨立開發、獨立運行和獨立部署。

技術組件說明¶

主頁面和微前端采用:Vue(前端框架),ElementUI(UI 框架 -PC),VUX(UI 框架 - 移動端) 和 MPVUE(UI 框架 - 小程序) 等。微服務開發采用:Spring Cloud、Kafka、Redis 等。數據庫采用:PostgreSQL。

附錄一:DDD 名詞和術語¶

      • Event Storming(事件風暴):事件風暴是一項團隊活動,旨在通過領域事件識別出聚合根,進而劃分微服務的限界上下文。在活動中,團隊先通過頭腦風暴的形式羅列出領域中所有的領域事件,整合之后形成最終的領域事件集合,然后對于每一個事件,標注出導致該事件的命令(Command),再然后為每個事件標注出命令發起方的角色,命令可以是用戶發起,也可以是第三方系統調用或者是定時器觸發等。最后對事件進行分類整理出聚合根以及限界上下文。
      • Entity(實體):每個實體是唯一的,并且可以相當長的一段時間內持續地變化。我們可以對實體做多次修改,故一個實體對象可能和它先前的狀態大不相同。但是,由于它們擁有相同的身份標識,他們依然是同一個實體。例如一件商品在電商商品上下文中是一個實體,通過商品中臺唯一的商品 id 來標示這個實體。
      • ValueObject(值對象):值對象用于度量和描述事物,當你只關心某個對象的屬性時,該對象便可作為一個值對象。實體與值對象的區別在于唯一的身份標識和可變性。當一個對象用于描述一個事物,但是又沒有唯一標示,那么它就是一個值對象。例如商品中的商品類別,類別就沒有一個唯一標識,通過圖書、服裝等這些值就能明確表示這個商品類別。
      • Aggregate(聚合):聚合是實體的升級,是由一組與生俱來就密切相關實體和值對象組合而成的,整個組合的最上層實體就是聚合。
      • Bounded Context(限界上下文):用來封裝通用語言和領域對象,為領域提供上下文語境,保證在領域之內的一些術語、業務相關對象等(通用語言)有一個確切的含義,沒有二義性。使團隊所有成員能夠明確地知道什么必須保持一致,什么必須獨立開發。
linux
相關資訊       微服務  驅動領域DDD 
本文評論   查看全部評論 (0)
表情: 表情 姓名: 字數

       

評論聲明
  • 尊重網上道德,遵守中華人民共和國的各項有關法律法規
  • 承擔一切因您的行為而直接或間接導致的民事或刑事法律責任
  • 本站管理人員有權保留或刪除其管轄留言中的任意內容
  • 本站有權在網站內轉載或引用您的評論
  • 參與本評論即表明您已經閱讀并接受上述條款
彩票投注骗局