為什麼要進行領域驅動設計? - Vincent
儘管DDD的理念已經存在了10多年,但最近幾天卻獲得了很多人的青睞。我認為這主要是因為人們開始注意到DDD所提出的思想與某類複雜性之間的關聯,這類複雜性在涉及多個團隊的大規模組織所構建的軟體中反覆出現。
儘管所有的現代架構原則和實踐都為常見的問題提供瞭解決方案,但這種出現在軟體開發核心的特殊型別的複雜性通常沒有得到解決,而DDD承諾為其提供一個解決方案。
本部落格的目的是強調DDD所解決的問題型別,並對該解決方案進行高層次的介紹。
柏拉圖主義
讓我們先來探討一些哲學思想,我相信這些思想會幫助你獲得一些關於DDD的新觀點。
柏拉圖認為,存在這樣一個抽象物體的領域,它是非物理的(存在於空間和時間之外)和非精神的,抽象物體可以透過推理被感知。
舉例來說。當我們談論一個完美的圓時,我們感知到了完美的圓的概念(它只作為一個抽象的物件存在),儘管在物理世界中產生一個完美的圓的任何嘗試在某種程度上都是不完美的。
心理或認知世界是我們看到/感覺到物理世界中的物體,並發現柏拉圖世界中抽象形式的存在的地方,也就是說,完美圓的概念是被發現的,而不是被發明的!
同樣,當我們看一個軟體模型或架構時,我們只是看到了一個真實系統的投影,你可以把真實系統稱為領域(類似於抽象物件)。
領域與軟體模型
在軟體中構造的模型來表示特定問題或領域稱為“領域模型”,您可能已經猜到了
立即想到的問題是,“領域模型應該有多準確?”
這與“一個圓應該有多完美?”是同一個問題,這兩個問題的答案都只是“對解決特定問題有用的程度”。
是的,沒有辦法對領域進行一對一的精確建模,但您始終可以以一種對解決手頭問題有用的方式進行建模。
讓我們以模擬我們自己的地球為例。
上圖模型與真實
右邊是地球的真實圖片,雖然真實但用處不大。
但左邊是地球的投影(或模型),稱為墨卡托地圖,儘管尺寸變形(看看綠地和非洲的尺寸),但它在確定任意兩個位置的方向時非常有用。
關鍵是,墨卡托地圖是為特定目的建模的——有一個有用的投影,可以幫助水手確定方向!
本質上,準確性不是很重要,但實用性很重要!
有效建模的工具
既然我們同意對領域/問題建模的重要性,那麼我們可以使用哪些工具來進行有效建模?
建模工具可以大致分為原則、模式和哲學。
設計原則
有大量的設計原則可以指導您在結構和行為方面對軟體進行建模。
例如, GRASP原則讓您思考並考慮如何將職責分配給各個類,從而在低耦合的情況下實現高內聚。
另一方面,SOLID原則與 GRASP 並沒有太大區別,它試圖為您提供一個指南來實現——再一次,高內聚和低耦合——事實上,我認為 SOLID 只是翻新的 GRASP :-)
除了掌握和紮實之外,還有其他通用的物件導向和函數語言程式設計原則。
本質上,所有原則都可以幫助您以高度內聚和鬆散耦合的方式對軟體進行建模——它們是精心設計的軟體的構建塊。
設計模式
到處都有模式,模式是一種重複的東西。
"房子 "是一種模式,"村莊"、"城鎮"、"大學 "等也是如此。
有一些特徵在城鎮之間是共同的。例如,每個鎮通常會有一個鎮中心,一個公園,一個購物中心,咖啡館,圖書館,火車站等。
當城鎮的所有設施都被放置在正確/最佳的位置時,城鎮就會變得活潑而和諧。沒有人希望在鎮外5公里處有一個公園,也沒有人希望在公園內有一個公共汽車的車站!
你看到了嗎?在我們周圍有一些固有的模式在重複。每個模式都存在於一個上下文中,併為上下文增加一些價值。
如果你用一種正式的語言來捕捉模式的本質,並給它一個名字,那麼在交流過程中分享它就會變得非常有用,也可以複製它。
這正是 Christopher Alexander 在他的著作《The Timeless way of Building 》和《The Pattern Language》中所描述的。他在他的書中列出了大約 250 種模式,您可以開始使用這些模式進行交流和複製。
Christopher Alexander 對四位作者的幫派產生了直接影響,他們提出了一本名為設計模式的非常受歡迎的書——就像模式語言一樣,它是軟體中大約 23 種常見模式的目錄。
現在,您可能會問這個問題:“原則和模式之間有什麼區別?” 答案是模式源於原則。
原則是軟體模型的基本構建塊,而模式是原則之上的一層,有助於捕獲、記錄和共享常見問題的解決方案。
如您所見,在設計模式之上還有更高階別的架構模式,例如 CQRS 和事件溯源。
設計理念
好的,現在我們知道我們有原則和模式來幫助我們對軟體進行建模,還有其他基本的設計哲學,但這些哲學中的大多數想法都可以概括為“抽象思考”
當您開始在抽象中思考並在您的程式碼中建立抽象時,無論是自上而下還是自下而上,您最終都會產生一個好的軟體設計。
這裡有一些談論軟體開發哲學的書,這些書對我如何建模/構建軟體產生了很大的影響。
現有工具的侷限性
好的,現在我們終於可以談談 DDD 了:-)
快速回顧!我們現在同意,我們有很多原則、模式和哲學,使我們能夠建立良好的軟體模型。
但不幸的是,如果您在處理複雜領域的大型組織中工作,上面列出的原則、模式和哲學仍然存在未解決的問題。
讓我們看看仍然需要注意的一些問題。
斷裂模型
如果兩個工程師或團隊被分配使用所有已知原則和模式對域建模的任務,他們將提出兩個完全不同的模型!— 不僅模型在結構上不同,而且在語言上也不同!
這表明一個大問題,團隊之間沒有共同點來在某種程度上統一模型。
破碎的語言
上述“脫節模型”問題的原因之一是用於建模的語言。組織中的每個人都可以在他們的模型中自由使用任何名稱和術語。
這不僅會導致模型難以理解,還會產生歧義和誤解。
例如,如果您正在構建一個支付系統,您將客戶向其帳戶新增新簽帳金融卡/信用卡的過程稱為什麼? 有人可能稱之為“卡連結”,有人可能稱之為“卡授權”,還有人可能稱之為“新增新卡”。
隨著組織構建越來越多的系統而沒有就通用詞彙達成一致,您會看到人們使用不同的詞來描述相同或有時完全相反的概念,從而導致很多混淆和誤解。
分裂的人
支離破碎的語言不僅是技術團隊之間的問題,它還會導致整個組織中出現更多分歧。
領域專家可能有自己的語言來表達某些領域概念,領域專家使用的詞彙可能與技術專家使用的詞彙完全不同。
這種分歧在大型組織中非常普遍,您可以在來自兩個不同群體(技術與領域)的人們試圖定義問題或探索問題的可能解決方案的任何討論中注意到它。
如果一個人不能清晰地表達出每個人都同意的問題的定義,那麼表達解決方案就更難了!
DDD(關鍵概念)
領域驅動設計 (DDD) 是一種透過將實現與核心業務概念的演化模型深度聯絡起來來開發滿足複雜需求的軟體的方法。
DDD 提供了很多原則和實踐來解決複雜的問題,我只會嘗試向您簡要介紹關鍵概念。
無處不在的語言UL
DDD 中出現的最重要的概念之一是無處不在的語言,它試圖解決上面列出的主要問題,即斷裂的語言、脫節的模型和脫節的人。
無處不在的語言UL是在開發人員和組織的其他成員之間構建通用共享語言的實踐。領域專家和開發人員在任何討論中使用的相同語言和術語應該與軟體模型中使用的術語相同。
這讓人能夠毫不含糊地清晰地表達問題和解決方案,這是一種強大的力量!
語言本身作為一種協作努力而發展,這需要有意識的紀律來保持語言的更新和管理。
整個想法聽起來很簡單,但這正是大多陣列織所缺少的元素。一旦你將一種無處不在的語言作為領域的官方語言,你將開始在程式碼、討論、測試、文件、需求等中看到它的一致使用。
限界上下文
無處不在的語言的想法應該立即讓你質疑在一個大型組織中使用一種全域性語言是否現實?
答案是不!我們甚至不應該嘗試擁有這樣一種統一的全域性語言,它實際上會對我們不利並造成更多混亂!
假設您有兩個團隊,一個負責支付系統,另一個負責訂單管理系統。這兩個系統雖然可能會相互作用,但專注於解決截然不同的問題。
試圖在兩個團隊之間共享一種共同的語言意味著每個團隊都會試圖將語言彎曲到非常具體的問題空間,從而完全汙染語言。
每個單詞/語句在特定上下文中都有意義。同樣,每個領域概念在(有界的)上下文中都有含義。
有界上下文只是應用特定通用語言或領域模型的領域內的邊界。
例如,如下所示,您在組織中有兩個限界上下文(或子域):Billing和市場營銷。
兩個子域處理完全不同型別的問題,但是,在兩個模型中都需要引用Customer.
儘管客戶Customer的ID標識在Billing營銷系統和營銷系統之間是相同的,但Customer兩者的定義不同Billing系統對賬單地址和未結髮票感興趣,而營銷系統對客戶的聯絡方式及其最近的訂單感興趣。
當我在計費範圍內提到“客戶”時,很明顯我指的是包含計費相關資訊的客戶表示。另一方面,如果我Customer在市場營銷有界上下文中引用,很明顯我指的是包含聯絡方式及其最近訂單的客戶表示。因此,組織中的歧義較少。
這意味著每個子域(限界上下文)都有自己的通用語言和軟體模型。
DDD原則
到目前為止,我們涉及的所有 DDD 概念都是高階實踐和約定,主要有助於解決組織問題。
還有一些原則可以幫助您在程式碼中有效地為您的領域建模。我不會涉及所有原則,但會涉及以下幾個關鍵原則。
實體、值物件和聚合
讓我們看看下面顯示的訂單管理系統示例。
Money是一個價值物件。在DDD社群,價值物件的形式定義是。"一個描述某些特徵或屬性的物件,但不帶有身份概念"。
非常直接的是,Money不需要有一個唯一的識別符號。20美元的金額可以由Money類的任何例項表示,其價值和貨幣設定為20&$。
另一方面,在你的模型中,有一些類需要一個唯一的識別符號。這些類被稱為實體。
實體的正式定義是。"一個物件從根本上說不是由它的屬性來定義的,而是由一條連續性和身份的線來定義的"。
所有的實體都會有一個非常明確的生命週期,也就是說,它在一個特定的時間點上出現,並經歷各種轉變,最終可能以終止的狀態結束。
在上面的例子中,當客戶下訂單時,訂單就會出現,它必須有一個唯一的識別符號,以便於客戶跟蹤和修改。它經歷了各種狀態,如待定、已發貨等。最終可能以完成或取消的狀態結束。
然而,一個訂單的LineItem雖然也是一個實體,但因為我們需要識別確切的行專案,所以不需要在 "訂單 "的上下文中是唯一可識別的。
這意味著所有需要訪問LineItem的用例都應該首先訪問訂單,然後訪問訂單中的行專案--這意味著LineItem只需要在訂單中擁有一個唯一的識別符號。
正如你在這個例子中所看到的,訂單實體(有一個全域性唯一的識別符號),LineItem實體(有一個本地唯一的識別符號)和Money值物件共同構成了一個叫做Aggregate的物件群。
另外,注意到訂單有一個特殊的性質,它是唯一可識別的實體,這使得它本質上成為外界與訂單相關的任何事物互動的第一個接觸點。因此,秩序雖然是一個實體,但也被稱為聚合根!
正式定義:"一組相關的物件,在資料變化時被視為一個單位。外部引用被限制在聚合AGGREGATE的一個成員上,指定為根。一套一致性規則適用於AGGREGATE的邊界內。"
Repository庫
儲存庫是一種抽象,使系統能夠儲存、檢索和搜尋物件的集合(通常是聚合根)。
我見過一些案例,人們使用儲存庫幾乎就像使用資料訪問層一樣,但它遠不止於此,儲存庫應該總是被視為 "聚合根的儲存庫"
例如。"訂單庫"、"客戶庫 "等。
這允許你在你的領域程式碼中像其他領域概念一樣使用資源庫介面,儲存聚合根只是資源庫的額外責任,這是一個實現細節。
但這裡的關鍵是,在你的領域模型中,儲存庫應該被視為一流的公民,而不是儲存的抽象概念!
概括
- DDD 是一種文化轉變,是一種思維方式。
- 這不是一種技術,而是一種實踐。
- 它需要整個組織的協作努力才能充分獲得收益
- 您的程式碼中的軟體模型與您的測試一起成為您所在領域的真實來源
- 獨特的語言是關鍵!
相關文章
- 什麼是領域驅動設計(DDD)?- mathias
- 不容錯過!什麼是領域驅動設計?為什麼落地這麼難?
- 什麼是DDD領域驅動設計的戰略設計?
- 什麼是DDD領域驅動設計的戰術設計?
- DDD領域驅動設計:領域事件事件
- MasaFramework -- 領域驅動設計Framework
- 領域驅動設計示例
- 理解領域驅動設計
- 領域驅動設計中的聚合是什麼? - James Hickey
- 什麼是DDD領域驅動設計的統一語言?
- 戲說領域驅動設計(廿五)——領域事件事件
- 領域驅動設計戰術模式--領域事件模式事件
- 領域驅動設計核心概念
- 領域驅動設計簡介
- DDD領域驅動設計pdf
- 再談領域驅動設計
- 實現領域驅動設計
- 戲說領域驅動設計(廿一)——領域服務
- 領域驅動設計戰術模式--領域服務模式
- DDD-領域驅動設計示例
- 微服務領域驅動設計 - semaphoreci微服務
- 淺談DDD(領域驅動設計)
- 淺談 DDD 領域驅動設計
- 何時使用領域驅動設計
- DDD領域驅動設計:倉儲
- JavaScript中的領域驅動設計JavaScript
- 前端開發-領域驅動設計前端
- 戲說領域驅動設計(五)——子域
- 問題驅動設計與領域驅動設計的區別 - abdullin
- 為什麼以及如何要進行架構設計權衡?架構
- 最常見領域驅動設計錯誤
- 領域驅動設計整合與架構架構
- dayatang/dddlib:DDD領域驅動設計庫
- 戲說領域驅動設計(廿二)——聚合
- 戲說領域驅動設計(三)——困境
- DDD-領域驅動設計簡談
- 領域驅動設計(DDD)入門&概要
- 戲說領域驅動設計(二)——修身