領域驅動設計(Domain-driven design,DDD)是一種為復雜需求開發軟件的方法,它將軟件的實現與不斷發展的核心業務概念模型緊密地結合在一起。
領域是一個知識的范疇。它指的是我們的軟件所要模擬的業務知識。領域驅動設計的中心是領域模型,它對一個領域的流程和規則有著深刻的理解。洋蔥架構實現了這一概念,并極大地改善了代碼的品質,降低了復雜性,并且支持不斷發展的企業系統。
為什么要用洋蔥架構?
領域實體是核心和中心部分。洋蔥架構是建立在一個領域模型上的,其中各層是通過接口連接的。其背后的思想是,在領域實體和業務規則構成架構的核心部分時,盡可能將外部依賴性保持在外。
它提供了靈活、可持續和可移植的架構。
各層之間沒有緊密的耦合,并且有關注點的分離。
由于所有的代碼都依賴于更深的層或者中心,所以提供了更好的可維護性。
提高了整體代碼的可測試性,因為單元測試可以為單獨的層創建,而不會影響到其他的模塊。
框架/技術可以很容易地改變而不影響核心領域。例如,RabbitMQ 可以被 ActiveMQ 取代,SQL 可以被 MongoDB 取代。
原則
洋蔥架構是由多個同心層構成,它們相互連接,并朝向代表領域的核心。它是基于控制反轉(Inversion of Control,IoC)的原則。該架構并不關注底層技術或框架,而是關注實際的領域模型。它是基于以下原則:
依賴性
圓圈代表不同的責任層。一般來說,我們潛入得越深,就越接近于領域和業務規則。外圈代表機制,內圈代表核心領域邏輯。外層依賴于內層,而內層則對外圈一無所知。通常情況下,屬于外圈的類、方法、變量和源代碼依賴于內圈,但是反過來也一樣。
數據格式/結構可能因層而異。外層的數據格式不應該被內層使用。例如,API 中使用的數據格式可以與 DB 中用于持久化的數據格式不同。數據流可以使用數據傳輸對象。每當數據跨層/跨界時,它應該以方便該層的形式出現。例如,API 可以有 DTO,DB 層可以有 Entity Objects,這取決于存儲在數據庫中的對象與領域模型的不同。
數據封裝
每個層/圈封裝或隱藏內部的實現細節,并向外層公開接口。所有的層也需要提供便于內層消費的信息。其目的是最小化層與層之間的耦合,最大化跨層垂直切面內的耦合 。我們在較深的層定義抽象接口,并在最外層提供其具體實現。這樣可以確保我們專注于領域模型,而不必過多地擔心實現細節。我們還可以使用依賴性注入框架,比如 Spring,在運行時將接口與實現連接起來。例如,領域中使用的存儲庫和應用服務中使用的外部服務在基礎設施層實現。
洋蔥架構中的數據封裝
關注點的分離
應用被分為若干層,每一層都有一組職責,并解決不同的關注點。每一層都作為應用中的模塊/包/命名空間。
耦合性
低耦合性,可以使一個模塊與另一個模塊交互,而不需要關注另一個模塊的內部。所有的內部層都不需要關注外部層的內部實現。
洋蔥架構層
讓我們通過一個創建訂單的用例來了解架構的不同層和它們的職責。當收到一個創建訂單的請求時,我們會對這個訂單進行驗證,將這個訂單保存在數據庫中,更新所有訂單項目的庫存,借記訂單金額,最后向客戶發送訂單完成的通知。
說明各層之間的依賴關系的包圖
領域模型/實體
領域實體是領域驅動設計的基本構件,它們被用來在代碼中為通用語言的概念建模。實體是在問題域中具有唯一身份的領域概念。領域實體封裝了屬性和實體行為。它應該是獨立于數據庫或網絡 API 等特定技術的。例如,在訂單領域,訂單是一個實體,并具有像 OrderId、Address、UserInfo、OrderItems、PricingInfo 這樣的屬性以及像 AddOrderItems、GetPricingInfo、ValidateOrder 這樣的行為。
訂單實體類
領域服務
領域服務負責保持領域邏輯和業務規則。所有的業務邏輯應該作為領域服務的一部分來實現。領域服務由應用服務協調,以服務于業務用例。它們不是典型的 CRUD 服務,通常是獨立的服務。領域服務負責復雜的業務規則,如在處理訂單時計算價格和稅收信息,保存和更新訂單的訂單庫接口,更新購買物品信息的庫存接口等。
它包含了對其目標非常關鍵的算法,并且將用例作為應用的核心來實現。
應用服務
應用服務也被稱為“用例”,是只負責協調請求步驟的服務,不應該有任何業務邏輯。應用服務與其他服務交互,以滿足客戶的請求。讓我們考慮一下用例,用一個物品清單創建一個訂單。我們首先需要計算價格,包括稅收計算/折扣等,保存訂單項目并向客戶發送訂單確認通知。定價計算應該是領域服務的一部分,但涉及定價計算、檢查可用性、保存訂單和通知用戶的協調工作應該是應用服務的一部分。應用服務只能由基礎設施服務調用。
基礎設施服務
基礎設施服務也被稱為基礎設施適配器,是洋蔥架構的最外層。這些服務負責與外部世界交互,不解決任何領域的問題。這些服務只是與外部資源通信,沒有任何邏輯。例如:外部通知服務、GRPC 服務器端點、Kafka 事件流適配器、數據庫適配器。
可觀察性服務
可觀察性服務負責監控應用。這些服務有助于執行以下任務:
數據收集(指標、日志、痕跡):主要使用庫/側線來收集代碼執行期間的各種數據。
數據存儲:使用能夠集中存儲所收集的數據的工具(分類、索引等)。
可視化:使用允許你對收集的數據進行可視化的工具。
一些例子包括 Splunk、ELK、Grafana、Graphite、Datadog。
測試策略
洋蔥架構的不同層有不同的職責,相應地也有不同的測試策略。測試金字塔是一個很好的框架,它規定了不同類型的測試。屬于領域模型、領域服務和應用服務的業務規則應通過單元測試進行測試。當我們移動到外層時,在基礎設施服務中進行集成測試更有意義。對于我們的應用,端到端測試和 BDD 是最合適的測試策略。
針對不同層的測試策略
微服務
當孤立地看待每個微服務時,洋蔥架構也適用于微服務。每個微服務都有自己的模型、自己的用例,并定義了自己的外部接口,用于檢索或修改數據。這些接口可以用一個適配器來實現,該適配器通過公開 HTTP Rest、GRPC、Thrift Endpoints 等連接到另一個微服務。它很適合微服務,在微服務中,數據訪問層不僅包括數據庫,還包括例如一個 http 客戶端,以從另一個微服務,甚至從外部系統獲取數據。
應用結構和層數
應用結構和層,包括層如何映射到模塊以及它們之間的依賴關系。它還描述了對不同層使用什么樣的測試策略
模塊化與打包
有兩種方法來組織應用的源代碼:
要么,我們可以將所有的包放在一個模塊/項目中,要么將應用分為不同的模塊/項目,每個模塊/項目負責洋蔥架構中的一個層。
這在很大程度上取決于應用的復雜性和項目的規模,將源代碼分為多個模塊。在微服務架構中,模塊化可能有意義,也可能沒有意義,這取決于復雜性和用例。
框架、客戶端和驅動
基礎設施層由網絡或服務器的框架、數據庫的客戶端、隊列或外部服務組成。它負責配置和縫合所有的外部服務和框架。洋蔥架構提供了解耦功能,因此在任何時候交換技術都會變得更容易。
我們需要每個層嗎?
將我們的應用分層組織有助于實現關注點的分離。但我們需要所有的層嗎?也許需要,也許不需要。這取決于用例和應用的復雜性。根據應用的需要,也可以創建更多的抽象層。例如,對于沒有很多業務邏輯的小型應用,擁有領域服務可能沒有意義。無論哪一層,依賴關系都應該是從外層到內層。
總結
洋蔥架構在開始時可能似乎有些困難,但是在業界已經得到了普遍的認可。這是一種讓軟件易于演進的強有力架構。通過把應用劃分為幾層,可以使系統更加易于測試、維護和移植。它有助于在舊框架過時時輕松采用新框架/技術。與其他架構風格類似,如六邊形、分層、簡潔的架構等,它為常見問題提供了一個解決方案。
審核編輯:劉清
-
適配器
+關注
關注
8文章
1965瀏覽量
68115 -
SQL
+關注
關注
1文章
770瀏覽量
44190 -
RBAC
+關注
關注
0文章
44瀏覽量
9975 -
mongodb
+關注
關注
0文章
22瀏覽量
372
原文標題:詳解DDD“洋蔥架構”
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論