【實踐篇】領域驅動設計: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
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...