領域驅動設計簡介

easonChen發表於2021-08-25

今天的企業應用程式無疑是複雜的,需要依靠一些專門技術(永續性,AJAX,Web服務等)來完成他們的工作。作為開發人員,我們傾向於關注這些技術細節,這是可以理解的。但事實是,一個不能解決業務需求的系統對任何人都沒用,無論它看起來多麼漂亮或者如何很好地構建其基礎設施。

領域驅動設計(DDD)的理念- 首先由Eric Evans在他的同名書中描述 - 是關於將我們的注意力放在應用程式的核心,關注業務領域固有的複雜性本身。我們還將核心域(業務獨有)與支援子域(通常是通用的,如錢或時間)區分開來,並將更多的設計工作放在核心上。

領域驅動設計包含一組用於從領域模型構建企業應用程式的模式。在您的軟體生涯中,您可能已經遇到過許多這些想法,特別是如果您是OO語言的經驗豐富的開發人員。但將它們一起應用將允許您構建真正滿足業務需求的系統。

在本文中,我將介紹DDD的一些主要模式,瞭解一些新手似乎很難解決的問題,並突出顯示一些工具和資源,以幫助您在工作中應用DDD。

程式碼和模型......

使用DDD,我們希望建立問題域的模型,永續性,使用者介面和訊息傳遞的東西可以在以後再建立,這是需要理解的業務領域,因為正在構建的系統中,可以區分公司的業務、核心競爭力以及競爭對手情況。(如果不是這樣,那麼考慮購買一個包裝好的產品)。

根據模型,我們不是指一個或一組圖表; 確實,圖表很有用,但它們不是模型,只是模型的不同檢視(參見圖)。模型是我們選擇在軟體中實現的一組概念,用程式碼表示,以及用於構建交付系統的任何其他軟體工件。換句話說,程式碼就是模型。文字編輯器提供了一種使用此模型的方法,儘管現代工具也提供了大量其他視覺化(UML類圖,實體關係圖,Spring beandocs,Struts / JSF流等)。

領域驅動設計簡介

圖1:模型與模型的檢視

這是DDD模式中的第一個概念:模型驅動設計。這意味著能夠將模型中的概念對映到設計/程式碼的概念(理想情況下),模型的變化意味著程式碼的變化; 更改程式碼意味著模型已更改。DDD並沒有強制要求您使用物件導向來構建領域 - 例如,我們可以使用規則引擎構建模型 - 但是考慮到主要的企業程式語言是基於OO的,大多數模型本質上都是OO。畢竟,OO基於建模範例。模型的概念將表示為類和介面,作為類成員的職責。

談談語言

現在讓我們看一下領域驅動設計的另一個基本原則。回顧一下:我們想要捕獲一個問題域的域模型,並且我們將在程式碼/軟體工件中表達成某種理解。為了幫助我們做到這一點,DDD提倡領域專家和開發人員有意識地使用模型中的概念進行溝通。因此,領域專家不會根據螢幕上的欄位或選單項來描述新的使用者故事需求,而是討論領域物件所需的基礎屬性或行為。類似地,開發人員不會討論資料庫表中的資料列以及新欄位型別。

DDD嚴格要求我們開發出一種無處不在的語言。如果一個想法不能輕易地明確表達,那麼它實際上在暗示背後有一個概念,這個概念在領域模型中缺失了,並且團隊需要共同努力找出缺失的概念是什麼。一旦建立了這個,那麼資料庫表中的螢幕或資料表列上的新欄位等結果就自然產生。

像DDD一樣,這種發現無處不在的語言的想法並不是一個新想法:XPers稱之為“名稱系統”,多年來DBA將資料字典放在一起。但無處不在的語言一個令人回味的術語,可以出售給商業和技術人員。現在,“整個團隊”敏捷實踐正在成為主流,這也很有意義。

模型和上下文......

每當我們討論模型時,它總是在某種情況下(某種背景條件下),通常可以從使用該系統的終端使用者的使用情況來推斷出這個上下文背景。比如,我們有一個部署到交易員前臺的交易系統,或超市收銀員使用的銷售點系統,這些使用者以特定方式與模型的概念相關,並且模型的術語對這些使用者有意義,但不一定對該上下文之外的任何其他人有意義。DDD稱之為有界上下文(BC)。每個域模型都只存在於一個BC中,BC只包含一個域模型。

我必須承認,當我第一次讀到關於BC時,我看不出重點:如果BC與領域模型一樣,為什麼要引入一個新術語?如果只有終端使用者與BC進行了互動,那麼也許就不需要這個術語了。然而,不同的系統(BC)也相互互動,傳送檔案,傳遞訊息,呼叫API等。如果我們知道有兩個BC相互互動,那麼我們知道我們必須注意進行概念之間進行轉換:此域和其他域之間。

在模型周圍設定明確的邊界也意味著我們可以開始討論這些BC之間的關係。實際上,DDD確定了BC之間的一整套關係,因此當我們需要將不同的BC連結在一起時,我們可以合理地確定應該做什麼:

  • 已釋出的語言published language:互動式BC是就共同的語言(例如企業服務匯流排上的一堆XML模式)達成一致,通過它們可以相互互動;
  • 開放主機服務open host service:BC指定任何其他BC可以使用其服務的協議(例如RESTful Web服務);
  • 共享核心shared kernel:兩個BC使用一個共同的程式碼核心(例如一個庫)作為一個共同的通用語言,但是否則以他們自己的特定方式執行其他的東西;
  • 釋出/訂閱customer/supplier:一個BC使用另一個BC的服務,並且是另一個BC的利益相關者(客戶端)。因此,它可以影響該BC提供的服務;
  • 跟從者conformist:一個BC使用另一個BC的服務,但不是其他BC的利益相關者。因此,它使用“原樣”(符合)BC提供的協議或API;
  • 防腐敗層anti-corruption layer:一個BC使用另一個服務而不是利益相關者,但旨在通過引入一組介面卡 - 反腐敗層來最小化BC所依賴的變化所帶來的影響。

你可以看到,在這個列表中,兩個BC之間的合作耦合水平是逐漸降低(見圖2)。使用已釋出的語言,我們從BC建立一個可以與之互動的共同標準,我們不擁有這種語言,而是擁有它們所在的企業(甚至可能是行業標準)。有了開放主機服務,我們仍然做得很好; BC提供其作為任何其他BC呼叫的執行時服務的功能,但隨著服務的發展(可能)將保持向後相容性。

領域驅動設計簡介

圖2:有界上下文關係的譜

然而,當我們走向跟從模式時,我們只是一起呼叫和被呼叫; 一個BC明顯屈服於另一個。如果我們必須與購買megabucks的總分類帳系統整合,那可能就是我們所處的情況。如果我們使用反腐敗層, 那麼我們通常會與遺留系統整合,但是額外的層將我們儘可能地隔離開來。當然,這需要花錢來實施,但它降低了依賴風險。反腐敗層也比重新實現遺留系統便宜很多,這最多會分散我們對核心域的注意力,最壞的情況是以失敗告終。

DDD建議我們制定一個BC圖來識別我們的BC以及我們依賴或依賴的BC,以確定這些依賴關係的性質。圖3顯示了我過去5年左右一直在研究的系統的上下文對映。

領域驅動設計簡介

圖3:上下文對映示例

所有這些關於有界上下文圖和BC的討論有時被稱為戰略性DDD,並且是有充分的理由的。畢竟,當你想到它時,弄清楚BC之間的關係是非常具有戰略重要的:我的系統將依賴哪些上游系統,我是否容易與它們整合,我是否有利用它們,我相信它們嗎?下游也是如此:哪些系統將使用我的服務,如何將我的功能作為服務公開,他們是否會對我有利?誤解了這一點,您的應用程式可能很容易失敗。

層和六邊形

現在讓我們轉向內部並考慮我們自己的BC(系統)的架構。從根本上說,DDD只關心領域層,實際上它並沒有很多關於其他層的說法:比如表現層,應用程式層或基礎架構層(或持久層)。但它確實期望它們存在。這是分層架構模式(圖4)。

領域驅動設計簡介

圖4:分層架構

當然,我們多年來一直在構建多層系統,但這並不意味著我們必須擅長它。確實,過去的一些主流技術 - 例如EJB 2,對,我說的是它! - 對領域模型可以作為有意義的層存在的想法產生了積極的影響。所有的業務邏輯似乎滲透到應用層或(更糟糕的)表現層,留下一組貧血的領域物件作為資料持有者的空殼(DTO或VO),這不是DDD的意思。

因此,要絕對清楚,應用程式層中不應存在任何領域邏輯。相反,應用程式層負責事務管理和安全性等事務。在某些架構中,它還可能負責確保從基礎結構/持久層中檢索的領域物件在與之互動之前已正確初始化(儘管我更喜歡基礎結構層執行此操作)。

如果表現層有單獨的儲存空間中(比如手機終端),應用層也充當表現層和領域層之間的中介。表現層通常處理領域物件或其他物件(資料傳輸物件或DTO)的可序列化表示,通常每個“檢視”一個。如果這些被修改,則表示層將對應用程式層的任何更改傳送回去,而應用程式層確定已修改的領域物件,並從持久層載入它們,然後轉發對這些領域物件的更改。

分層架構的一個缺點是:它從表現層一直到基礎結構層的依賴性是線性的。但是,我們可能希望在表現層和基礎結構層中支援不同的實現。如果我們想測試我們的應用程式肯定是這樣的:

  • 例如,FitNesse等工具允許我們從終端使用者的角度驗證我們系統的行為。但是這些工具通常不會通過表示層,而是直接返回到下一層,即應用層。所以從某種意義上說,FitNesse就是另一種觀察者。
  • 同樣,我們可能有多個永續性實現。我們的生產實現可能使用RDBMS或類似技術,但是對於測試和原型設計,我們可能有一個輕量級實現(甚至可能在記憶體中),因此我們可以模擬永續性。

我們可能還想區分“內部”和“外部”層之間的互動,其中內部我指的是兩個層完全在我們的系統(或BC)內的互動,而外部互動跨越BC。

因此,不要將我們的應用程式視為一組圖層,另一種方法是將其視為六邊形,如圖5所示。我們的終端使用者使用的是檢視器以及FitNesse測試使用內部客戶端API(或埠),而來自其他BC的呼叫(例如,RESTful用於開放主機互動,或來自ESB介面卡的呼叫用於已釋出的語言互動)命中外部客戶端埠。對於後端基礎架構層,我們可以看到用於替代物件儲存實現的永續性埠,此外,領域層中的物件可以通過外部服務埠呼叫其他BC。

領域驅動設計簡介

圖5:六邊形結構

領域驅動設計簡介之二

相關文章