軟件系統面向對象的設計思想可謂歷史悠久,20世紀70年代的Smalltalk能夠說是面嚮對象語言的經典,直到今天咱們依然將這門語言視爲面嚮對象語言的基礎。隨着編程語言和技術的發展,各類語言特性層出不窮,面向對象是大部分語言的一個基本特性,像C++、Java、C#這樣的靜態語言,Ruby、Python這樣的動態語言都是面向對象的語言。數據庫
面向對象最初是爲了解決數據隔離問題,隨後的發展中才逐步對封裝、繼承、多態進行使用。編程
過去系統分析和系統設計都是分離的,正如咱們國家「系統分析師」 和「系統設計師」 兩種職稱考試同樣,這樣割裂的結果致使,需求分析的結果沒法直接進行設計編程,而可以進行編程運行的代碼卻扭曲需求,致使客戶運行軟件後才發現不少功能不是本身想要的,並且軟件不能快速跟隨需求變化。後端
DDD則打破了這種隔閡,提出了領域模型概念,統一了分析和設計編程,使得軟件可以更靈活快速跟隨需求變化。設計模式
服務器後端發展三個階段:服務器
UI+DataBase的兩層架構,這種面向數據庫的架構沒有靈活性。微信
UI+Service+DataBase的多層SOA架構,這種服務+表模型的架構易使服務變得囊腫,難於維護拓展,伸縮性能差。數據結構
DDD+SOA微服務的事件驅動的CQRS讀寫分離架構,應付複雜業務邏輯,以聚合模型替代數據表模型,以併發的事件驅動替代串聯的消息驅動。真正實現以業務實體爲核心的靈活拓展。架構
領域驅動設計(Domain-Driven Design)簡稱DDD。併發
DDD是由 Eric Evans 提出的,綜合軟件系統分析和設計的面向對象建模方法。現在已經發展成爲了一種針對大型複雜系統的領域建模與分析方法。
DDD是針對軟件開發領域的一種系統與理論分析方法,是一種方法論。編程語言
領域驅動設計(DDD)的概念源於2004年著名建模專家Eric Evans發表的書籍:《Domain-Driven Design – Tackling Complexity in the Heart of Software》(中文譯名:領域驅動設計—軟件核心複雜性應對之道),池建強在2011年發表的一篇文章《領域驅動設計和實踐》中是這樣形容DDD的:領域驅動設計事實上是針對OOAD的一個擴展和延伸,DDD基於面向對象分析與設計技術,對技術架構進行了分層規劃,同時對每一個類進行了策略和類型的劃分。(OOAD:面向對象的分析和設計)
OOAD經常使用基本的設計原則有七個:
在面向對象中的基本設計原則基礎上,又出現了各類各樣的設計模式,來解決同一種問題。
DDD和已有方法的區別是什麼?
是針對數據庫建模,是關係型數據庫理論的延續,關注數據表和數據表之間的關係,是面向技術建模。
將業務概念和規則轉變爲軟件系統中的類型及其屬性和行爲
合理利用面向對象的封裝、繼承、多態等設計要素
下降或隱藏系統的業務複雜度
提高系統擴展性
傳統的開發是面對數據庫表,先設計表結構,再研究表與表之間的關聯。DDD開發是先理解業務,再理解頁面,再經過頁面來構想代碼中的業務流程,以此來設計數據庫表。
DDD相關概念
現實世界中,領域包含了問題域和解系統。通常認爲軟件是對現實世界的部分模擬。在DDD中,解系統能夠映射爲一個個限界上下文,限界上下文就是軟件對於問題域的一個特定的、有限的解決方案。
一個由顯示邊界限定的特定職責。領域模型便存在於這個邊界以內。在邊界內,每個模型概念,包括它的屬性和操做,都具備特殊的含義。
限界上下文之間的映射關係
合做關係(Partnership):兩個上下文緊密合做的關係,一榮俱榮,一損俱損。
共享內核(Shared Kernel):兩個上下文依賴部分共享的模型。
客戶方-供應方開發(Customer-Supplier Development):上下文之間有組織的上下游依賴。
遵奉者(Conformist):下游上下文只能盲目依賴上游上下文。
防腐層(Anticorruption Layer):一個上下文經過一些適配和轉換與另外一個上下文交互。亦稱適配層。在一個上下文中,有時須要對外部上下文進行訪問,一般會引入防腐層的概念來對外部上下文的訪問進行一次轉義。
開放主機服務(Open Host Service):定義一種協議來讓其餘上下文來對本上下文進行訪問。
發佈語言(Published Language):一般與OHS一塊兒使用,用於定義開放主機的協議。
大泥球(Big Ball of Mud):混雜在一塊兒的上下文關係,邊界不清晰。
另謀他路(SeparateWay):兩個徹底沒有任何聯繫的上下文。
在開發一個項目時,參與的人不少,有開發人員,有測試人員,常常會出現的一個問題就是測試不明白開發的某個字段,某個類名,某個接口是什麼意思,開發與開發之間也會出現相似的問題,而通用語言就是爲了解決這一問題。顧名思義,通用就是說你們都用,你們都用就能保證一致性,好比:userinfo,一看就知道是用戶信息的含義。
實體能夠理解爲傳統項目中的實體類,所承擔的責任基本一致,區別就在於傳統項目是針對數據的,實體就是實體沒有其餘任何做用,而DDD中的實體則是能夠認爲是一個領域,是包含業務的。實體不是由於某個屬性纔是實體,是由於業務需求。
當一個對象用於對事務進行描述而沒有惟一標識時,它被稱做值對象。它具備不變性、相等性和可替換性。
一些重要的領域行爲或操做,能夠歸類爲領域服務。領域服務自己所承載的職責是經過串聯領域對象、資源庫和防腐層等一系列領域內的對象的行爲,對其餘上下文提供交互的接口。
領域事件是對領域內發生的活動進行的建模。領域事件有因有果,可是必須是有價值的緣由纔有果。
聚合是一組相關對象的集合,做爲一個總體被外界訪問,聚合根(Aggregate Root)是這個聚合的根節點。聚合由根實體,值對象和實體組成。核心領域每每都須要用聚合來表達。
從上面所述,咱們能夠得知,DDD不能幫助咱們完成某個功能,不能解決某種業務需求,那麼DDD究竟是什麼,所能作到是什麼呢?DDD分工明確,實體就是實體,領域就是領域,各自負責各自的工做,就像老闆招工人,一通亂找確定須要花費更多的時間和精力,可是分工明確的話,人事部招作人事工做的,開發部招收作開發的,這就能更少的花費時間和精力了。因此DDD是一種解決問題的方法,是一種解決問題的想法,是一種簡化開發流程的設計,因此DDD的實現是先於開發的,是在設計過程當中就實現的。
在業務初期,功能大都很是簡單,普通的 CRUD 就基本能知足要求,此時系統是清晰的。但隨着產品的不斷迭代和演化,業務邏輯變得愈來愈複雜,咱們的系統也愈來愈冗雜。各個模塊之間彼此關聯,甚至到後期連相應的開發者都很難說清模塊的具體功能和意圖究竟是什麼。這就會致使在想要修改一個功能時,要追溯到這個功能須要修改的點就要很長時間,更別提修改帶來的不可預知的影響面。
舉個例子:
訂單服務中提供了查詢、建立訂單相關的接口,也提供了訂單評價、支付的接口。同時訂單表是個大表,包含了很是多字段。咱們在維護和升級時,將會致使牽一髮而動全身,極可能本來咱們只是想改下評價相關的功能,卻影響到了建立訂單的核心流程。雖然咱們能夠經過測試來保證功能的完備性,但當咱們在訂單領域有大量需求同時並行開發時將會出現改動重疊、惡性循環、疲於奔命修改各類問題的局面,並且大量的全量回歸會給測試帶來不可接受的災難。
傳統項目Service層很重,全部邏輯處理基本都放在service層。隨着系統的升級,不斷的重構,業務愈來愈多,愈來愈龐雜,包括數據結構的變化,那麼各個模塊就須要進行修改,本來清晰的系統通過不斷的演化變得複雜、冗餘、耦合度高,後果就很是嚴重。
再舉個例子:
一個商城系統,有購買商品支付,跟蹤郵件等等一些列的流程,這個時候忽然須要加入會員功能,也要生成訂單,生成支付,幾乎與實物商品的訂單一致,惟一不一樣的是會員不能退款,若是咱們從新寫一個支付流程,這無疑是浪費大量的時間和精力去作重複的事情。除此以外若是咱們還有別的辦法,咱們可使用實物訂單的生成訂單和支付方法,就能夠輕鬆的完成會員這個功能,若是哪一天經理忽然說不須要這個功能了,就算修改,對整個系統而言也無傷大雅。
DDD能讓咱們知道如何抽象出限界上下文以及如何去分而治之。
分而治之 : 把複雜的大規模軟件拆分紅若干個子模塊,每個模塊都能獨立運行和解決相關問題。而且分割後各個部分能夠組裝成爲一個總體。
抽象 : 使用抽象可以將問題由大化小,並且問題越小越容易理解,好比說咱們要對接支付,抽象的緯度應該是支付,而不是具體的微信支付仍是支付寶支付。
DDD 的限界上下文能夠完美匹配微服務的要求。在系統複雜以後,咱們都須要用分治來拆解問題。通常有兩種方式,技術維度和業務維度。技術維度是相似 MVC 這樣,業務維度則是指按業務領域來劃分系統。 微服務架構更強調從業務維度去作分治來應對系統複雜度, 而 DDD 也是一樣的着重業務視角。