【實踐篇】領域驅動設計:DDD工程參考架構

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/05/22/17419966.html
-Advertisement-
Play Games

不同團隊落地DDD所採取的應用架構風格可能不同,並沒有統一的、標準的DDD工程架構。即使無法制定通用的、標準的工程應用架構,但為團隊制定一個遵循領域驅動設計思想的參考架構依然有價值。 ...


背景

為什麼要制定參考工程架構

不同團隊落地DDD所採取的應用架構風格可能不同,並沒有統一的、標準的DDD工程架構。有些團隊可能遵循經典的DDD四層架構,或改進的DDD四層架構,有些團隊可能綜合考慮分層架構、整潔架構、六邊形架構等多種架構風格,有些在實踐中可能引入CQRS解決讀模型與寫模型的差異化等等。即使無法制定通用的、標準的工程應用架構,但為團隊制定一個遵循領域驅動設計思想的參考架構依然有價值。基於以下原因:

  • 為團隊實踐DDD的戰術設計提供可以快速開始的工程參考
  • 參考工程大量的命名和結構決策,顯式的體現DDD的相關理念,有利於團隊對DDD的戰術實現達成一致認知
  • 同時,參考架構有助於沉澱團隊對領域驅動設計的一些思考和最佳實踐

參考架構的考量因素

雖然無法制定完全通用的DDD參考架構,但制定某個特定上下文下的參考架構卻具有可行性和實踐價值。針對於上下文的選擇要儘量貼合實際的工程實踐場景並考慮多維度的因素。

本文所述參考工程架構遵循以下原則:

  • 遵循領域驅動設計的本質思想
  • 充分考慮業務系統建設特點
  • 依賴最小化,保持輕量

希望工程參考架構能涵蓋以下範圍

  • 分離業務域與技術域

參考架構要遵循技術和業務隔離的特性,可以參考多種架構風格。業務與技術關註點的分離並不是DDD獨有的特點,在六邊形、整潔架構、洋蔥架構中都遵循了這一重要原則。

  • 多限界上下文場景

大多數團隊基於DDD進行微服務拆分的時候,特別是系統建設初期,對單個微服務應用內的限界上下文的粒度需要權衡。由於團隊組織架構因素及微服務成本問題,單個應用容納的限界上下文一般是多個(理想情況下是1:1)。這些限界上下文隨著後續的逐步迭代有可能會遷移至獨立應用。因此,參考架構將多上下文的應用場景作為重要考量因素。

  • 明確的組件、職責邊界及依賴關係
  • 支持領域報表場景:報表場景在業務系統較為常見,DDD並沒有體現該場景的處理方式。作為工程參考架構,還是希望能夠從實際業務出發,體現對寫模型和報表模型的顯示支持
  • 外部依賴最小化:需要排除不必要的依賴,保持工程架構的輕量化

參考架構剖析

應用的多上下文結構

基於以上原則,參考工程考慮單個應用內多上下文的場景,以期在模塊化和服務粒度及成本間進行權衡折衷。應用架構對多上下文的支持的邏輯示意圖如下所示,在解決方案域對限界上下文進行識別和劃分之後,基於其業務內聚性和關聯性,把多個上下文實現單個工程應用中。單個應用內的多個限界上下文間可能存在交互,交互的形式可以是基於事件驅動,也可以是基於進程內調用。採用事件驅動的方式上下文間的耦合性對低一些,但一般需要引入事件匯流排的支持,額外組件的引入必然會導致複雜性的上升。進程內調用則耦合性會高一些,但從實現角度複雜度會低一些。具體選擇哪種方式開發人員可以基於實際情況進行權衡。

需要再次說明的是,這種應用架構決策是一種多因素的權衡,並沒有與子域與限界上下文1:1的理想化實踐保持一致。

從上圖的邏輯示意圖我們再深入一層,從分層的維度去剖析一下詳細的應用架構的展現形式,如下圖所示:

分層關註點

客戶端

客戶端與應用處於不同的進程,是應用能力的消費端,在實際項目中可能是APP端、PC端、小程式端、公眾號端或三方的業務調用端等等。

接入層

接入層是外部系統與應用內部業務能力的中間層,接入層是應用層對外的門面,是當前應用對外暴露業務能力的入口。該層的組成可能是對外提供的HTTP介面聲明、分散式定時任務調度、消息監聽器、RPC服務等等。其重要職責包括對外部系統的請求進行基礎的參數校驗、入參適配和服務路由(轉發至系一層的應用服務)以及響應數據的適配

業務層:

該層是應用的業務邏輯所在層,整個架構風格採用模塊化單體風格,在該層不同的限界上下文體現為不同的模塊。在每個限界上下文內採用分層架構,獨立劃分為應用層、領域層和網關層

應用層:

協調領域對象、領域服務或外部依賴服務完成業務用例,該層只做能力協調,不處理任何領域邏輯

領域層:

領域層是整個分層的核心,與技術實現無關,主要負責領域模型、領域事件、領域服務定義,以及業務相關外部服務的介面抽象以及倉庫的介面抽象等。

領域層與應用服務的本質區別是:應用層不包含領域邏輯,領域邏輯全部下沉到領域層實現。

網關層:

網關層定位是應用的出口網關,是應用與外部基礎設施交互的防腐層,處理所有技術相關實現

該組件的命名有多種方式,比如有些團隊將其命名為 “rpc”,也有些團隊將其命名為 “infrastructure”,不同的命名體現了團隊對其背後所表達的隱喻的決策選擇。在本文的參考架構中選擇了 網關-Gateway這一命名,決策原因是:限界上下文自身是高內聚的,其與外部的交互需要統一齣口,Gateway所表達的網關的含義恰當的表現了這種統一齣口的理念。如果Facade層是應用的北向網關,是外部系統請求進入內部的入口。則此時的Gateway則表達的是限界上下文的南向網關,是內部應用連接外部的出口。

組件及依賴

從巨集觀的分層我們再深入一層看下每層的組件劃分。如下圖所示:

Start組件:

整個應用的啟動入口、載入應用配置信息等等。

Common組件

提供在不同的限界上下文間復用的領域模型元素的抽象,比如對Command、Query、Event、Entity、ValueObjec通用抽象等。當然,領域模型的通用抽象不是必須在Common組件內以提供復用,也可以作為一個獨立的限界上下文,並以共用內核方式與其它上下文進行共用,或者也可以實現為獨立的jar包組件。

API 組件

RPC類型服務的介面聲明組件,以公司內部使用的JSF為例,該組件是應用對外部系統暴露的JSF API的組件。該組件可以是獨立的工程,當然,有些團隊會將其作為一個Module放入應用工程中。

統一門面組件:Facade

外部客戶端觸達應用系統的入口,也是內部應用服務的統一門面,類似於六邊形架構風格下的適配器。參考架構中基於不同場景劃分為 provider(RPC服務)、task(定時任務)、listener(MQ監聽)、rest(http介面)等幾個子包。外部請求進入系統後,由Facade組件完成入參基本校驗、入參轉換、服務路由以及出參轉換等操作。另外,還可以承擔處理登錄態、鑒權以及日誌等相關能力。

應用服務組件:Application Service

應用服務代表著用例以及系統行為,其通過委托到領域層和基礎設施層(參考架構中的Gateway組件)完成用例的應用邏輯邏輯處理,可以理解為應用服務是領域層的客戶端。該組件典型的職責:

從存儲層載入領域對象、委托領域對象執行領域邏輯、保存領域對象

  • 重要事件通知到外部
  • 出入參轉化適配
  • 事務處理外部
  • 非領域邏輯的服務調用

External API

應用服務的邏輯不僅僅需要協調領域層,有時還需要依賴於外部的三方服務。External API 組件負責對這些外部服務進行介面聲明定義,不做具體實現。

應用服務組件不直接依賴這些外部服務實現,而是依賴其介面抽象。同時,此處的模型定義是基於該限界上下文的語義,是一種對外部模型的適配。

該組件不依賴其它組件,且僅被應用服務組件和Gateway組件依賴。網關組件依賴External API 組件的介面聲明並提供底層技術實現,應用服務組件依賴其介面,並通過IOC方式將具體實現註入完成服務調用。

註意,該組件所依賴的服務不涉及領域邏輯,只是用於支撐應用服務的編排。如果是涉及了領域邏輯,則對外部服務依賴的介面定義需要下沉到Domain層。

Query

Query組件解決領域相關的報表查詢場景,在限界上下文內作為與應用服務對等的組件存在,兩個組件分別負責業務的查詢和命令邏輯。

雖然引入了Query組件對報表場景提供支持,但沒有完全的引入CQRS這一模式。在很多資料中CQRS與DDD同時提及的概率比較高,因為,在DDD下我們解決了複雜的面向領域的寫側模型,但在報表場景下,這種富領域模型有可能並不是最佳選擇。如果讀側和寫側都基於統一的領域模型,一般會導致領域模型的折衷設計。為了滿足查詢側訴求,領域模型不得不引入額外的、領域無關的屬性,由此造成領域模型的污染。

Domain

Domain組件是領域邏輯核心,承擔整個系統領域邏輯的實現,其定義了領域模型、領域服務、領域事件以及倉儲層的抽象。該組件不依賴其它組件(除了通用的領域模型抽象組件Common之外。

上圖所體現的參考架構使用了DDD的戰術設計的經典建模元素,比如聚合、實體、值對象、倉儲、工廠以及領域事件等。在實際落地過程中,這些設計元素的抽象具有一定的挑戰,設計過程中需要經過不斷分析、權衡和重構以完成建模,這正是核心設計所在。

Gateway

網關層承擔整個技術相關性的實現,是內部應用的出口網關。

技術相關性是網關組件區別於其它組件的根本特性。在該組件內要處理技術實現的所有細節,比如與外部服務、中間件、DB的交互等。同時,其與Domain組件以及External API組件的介面抽象協作,共同承擔了系統與外部依賴間(包括外部服務以及應用依賴的中間件、DB等基礎設施)的防腐層職能,負責內部模型到外部模型轉化、外部模型到內部模型轉化以及具體交互。基於網關組件的特性,也非常適合在該層做統一的外部服務數據緩存及降級熔斷處理。

網關層依賴於Domain、Application Service、External API和Query組件,負責上述四個組件定義的介面實現。在Gateway組件通過子包對實現進行隔離:

  • query:查詢服務組件的實現
  • external:External API 組件中依賴外部服務的介面實現
  • repository:倉儲介面的實現

最後

應用架構模式的選擇是系統架構設計的重要維度之一,結構不僅僅是簡單的包結構和命名,其傳達的是一種頂層抽象,背後包含了大量的實踐和知識。制定符合團隊情況的工程參考架構,併在團隊成員間達成共識非常重要。領域驅動設計並沒有統一的、通用的架構,試圖定義標準架構是不切實際的。本文描述的工程架構只是一個參考,實踐過程中應該基於團隊特定情況而有所差異,但原則上都應該遵循業務域與技術域分離的核心理念

作者:京東科技 倪新明

內容來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2022年的程式員節, #大齡程式員去哪兒了#成為了社交媒體上最火的話題之一,程式員的職場成長問題在社會上引起了廣泛關註。 有2位在技術領域摸爬滾打很多年的開發者,35歲後的他們,有70後,有80後,依然在編程開發,依然有離職創業的勇氣,努力實現自己的人生價值。走進他們的故事,你會發現,這個世上沒有 ...
  • # React筆記-Hooks(九) ## Hooks ### 概念 >React Hooks 的意思是 組件儘量寫成純函數 如果需要外部功能和副作用 就用鉤子把外部代碼"鉤"進來 ### 函數組件和類組件區別 >- 函數組件沒有狀態(state) 類組件有 >- 函數組件沒有生命周期 類組件有(掛 ...
  • OpenAI於前幾天發佈了IOS版ChatGPT智能App應用。預示著ChatGPT正式踏入了移動設備領域。 現在可以去AppStore下載這款免費、帶有語音設別功能的ChatGPT應用了。 基於vite4.x+vue3+pinia2模仿chatgpt手機端聊天模板Vue3-MobileGPT。 運 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 在實際的開發工作過程中,積累了一些常見又超級好用的 Javascript 技巧和代碼片段,包括整理的其他大神的 JS 使用技巧,今天篩選了 9 個,以供大家參考。 1、動態載入 JS 文件 在一些特殊的場景下,特別是一些庫和框架的開 ...
  • HTTP 1.1相比HTTP 1.0具有以下優點: 1. 持久連接 :HTTP 1.1引入了持久連接機制,允許多個請求和響應復用同一個TCP連接。這樣可以減少建立和關閉連接的開銷,提高性能和效率。2. 流水線處理 :HTTP 1.1支持流水線處理,即可以同時發送多個請求,不需要等待前一個請求的響應。 ...
  • 參考:[Building a WebGL Carousel with React Three Fiber and GSAP](https://tympanus.net/codrops/2023/04/27/building-a-webgl-carousel-with-react-three-fibe ...
  • ## 一、模式動機 迭代器模式(Iterator Pattern)是一種使用頻率非常高的行為型設計模式,**迭代器**用於**對一個聚合對象進行遍歷**。通過**引入迭代器**可以**將數據的遍歷功能從聚合對象中分離出來**,**聚合對象只負責存儲數據**,而**遍曆數據由迭代器來完成**,簡化了聚 ...
  • 之前總聊微服務,今天換一個話題---低代碼。低代碼這個詞也是最近這幾年很火的概念,尤其是遇到大環境下行,很多大廠和互聯網那個公司也在慢慢在低代碼方向發力,當然,對於傳統項目交付型的軟體公司,低代碼也具有相當大的吸引力。 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...