做爲一個領域驅動設計的實踐者,我切實感覺到了領域驅動這種方法論給軟件工程帶來的好處,同時也感覺到了實踐領域驅動的困難,這種困難體如今工程實踐的方方面面,例如什麼是領域驅動的最佳設計?如何把書本上的設計靈活的應用在本身的項目上?如何跟團隊成員普及領域驅動,讓你們也能明白這種開發思想?我深知理解並應用領域驅動設計的道路是曲折的,而讓廣大開發者的思想轉變則是這條路上的第一步。數據庫
世面上有關領域驅動設計的書籍很少,可是也不利於入門,尤爲是對剛入門的開發者,要理解領域驅動的思想更是難上加難。
本文嘗試從領域驅動設計解決問題的目的出發,試圖經過簡單的描述來講明領域驅動設計這門方法論的思想。redux
做爲一個軟件開發者,多數人覺得本身的職責就是編寫代碼,然而軟件開發不是工廠流水線,若是全部的軟件開發者不停的開發新功能而不關心設計,那麼軟件開發過程將變得愈來愈複雜,關於這一點你們應該都有不一樣程度的感覺。架構
軟件開發工程師的工做是經過軟件來解決問題,編寫代碼只是其中的一部分工做,設計和交流一樣重要。而領域驅動設計就是一個讓軟件開發工程師交流和共享領域知識的途徑。app
做爲一個問題的解決者,可否理解和認識問題的來龍去脈相當重要。很明顯,若是你只看到了問題的表面,或者對事實有曲解,你顯然不會找到一個有效的解決方案。對於開發者,若是你編寫的代碼只是你的理解,而不是領域專家的理解,你如何保證上線產品的質量?dom
那麼如何保證開發者編寫的代碼就是領域專家的想法?最簡單的辦法就是讓領域專家來編寫代碼,可是這種方案可遇不可求,還有沒有別的辦法?
若是領域專家,開發團隊以及代碼可以共享一個模型,這將有效減小不一樣利益相關者的溝通及交流而且會確保全部人都在解決同一個問題。
這個想法要求開發者可以把代碼設計爲一個反映業務的模型,而這正是領域驅動設計的核心思想。數據庫設計
爲了在領域專家和開發者之間創建一個共享模型,收集需求並理解業務是第一步。收集需求和理解業務的方式多種多樣,而事件風暴常常被用來達到這一目的。業務邏輯能夠看作是一系列狀態的轉換過程,而這些過程轉換又被稱爲業務事件。好比「訂單已提交」就是一個領域事件,若是把這個領域事件看作是訂單業務的開始,經過梳理」訂單已支付」以及」訂單已出庫」等後續的領域事件,就能夠理解整個訂單業務。此時對領域業務的理解被稱爲「問題域」。微服務
經過事件風暴,開發團隊和領域專家已經對整個」問題域」有了理解,可是如今着手解決「問題域」還有點早。當咱們在面對一個大的問題時,天然而然會想到先將大的問題劃分紅若干個小問題,而後再考慮各個擊破。通過上一個步驟,咱們對問題領域已經有了很清楚的認識,接下來的一步就是把問題域劃分爲若干個小的問題域。咱們有一個網上商城的問題域,能不能把它分割爲小的問題域?
答案確定的,咱們把網上商城的問題分爲:「訂單」,「銷售」,「市場」,「財務」,「採購」等若干個小問題域,再針對小的問題域分而治之。小的問題域在領域驅動設計中被稱爲「問題子域」。spa
理解了問題域並劃分爲問題子域並不意味着你就能建立出一個好的方案,你沒法針對問題子域的全部信息設計出一個解決方案,你的解決方案只會專一於那些有助於解決該問題子域的信息,對於不相關的信息則會人爲的屏蔽掉。
爲何叫限界?
在現實世界中,領域的邊界很模糊,可是要設計一個好的解決方案,咱們須要對問題子域加上一個邊界,將不重要的信息排除在邊界外。讓解決方案專心解決重點問題。
爲何叫上下文?
每一個上下文都表明着該解決方案的專業知識。在同一個上下文裏,咱們共享統一的語言和一致的設計。
經過界限上下文人爲將問題子域限制在有限的界限內,你才能夠着手建立解決方案。設計
團隊之間共享的術語和詞彙被稱爲統一語言。統一語言用來定義業務領域的共享模型,固然能夠用在項目的任何地方,包括需求分析和設計,最重要的是統一語言還須要出如今代碼中。另外,統一語言在不一樣的界限上下文中每每不可以通用,例如在「認證上下文」中提到「用戶」,在「機票訂單上下文」中叫作「乘客」。orm
有了界限上下文,讓解決方案聚焦在最有用的信息裏,你才能夠着手創建共享模型。
如何才能創建一個不錯的共享模型呢?
使用可視化的圖示彷佛是一個不錯的想法,但實際上畫出一個可以表達全部領域知識的圖示並非一個簡單的工做;若是你有數據庫開發相關的經驗,你可能會想到經過表和主外鍵來表達領域知識,若是你有這樣的想法那你就錯了,在領域驅動設計中講究經過領域邏輯來驅動設計和開發工做,而不是經過數據庫模型來驅動開發。
在領域驅動設計中這一步叫作」領域建模「,你應該用代碼創建一個反映領域知識的模型,這個模型跟領域專家口中的領域知識是一致的。領域模型是提供業務能力的核心部件,也是整個應用程序提供業務能力的核心。
對於開發者而言領域建模相當重要,也是最考驗開發者功底的一個環節。一方面開發者須要抽象出一個可以跟領域專家口中一致的模型,另外一方面開發者還須要經過代碼將這個模型表達出來。你須要恰如其分的使用一些面向對象的技巧把領域知識抽象到一個代碼模型中,在這個過程當中你須要瞭解」值對象」,」實體「,」聚合根「等概念,在此再也不細說。
在領域建模以及以前的步驟中,咱們都沒有說起數據庫,由於領域驅動設計的核心是用代碼創建一個共享模型,而數據庫設計根本就不是領域驅動設計關心的內容。
可是終究咱們仍是要把領域模型的狀態持久化到數據庫中,有沒有辦法在不關心數據庫結構的狀況下將已經創建好的領域模型持久化?主流ORM的Code First剛好匹配咱們如今的處境,已經有一點爲領域驅動設計而生的味道了。
可是即使是ORM的Code First也會對領域模型有侵入,你可能須要根據不一樣的ORM爲模型加上一些註解或者配置之類的代碼,這跟領域驅動設計實際上是相互違背的,咱們但願用代碼建立一個純淨的領域模型,這個模型封裝着領域專家的領域知識,除此以外的代碼都跟領域模型是無關的。
解決上面問題的思路是引入領域事件和事件溯源。領域模型在提供業務能力的過程就是領域模型狀態發生變化的過程,而一旦領域模型的狀態發生了變化就會產生一個事件,這跟事件風暴中提到的業務事件是一致的,例如」用戶下單「,訂單模型提供」用戶下單「的業務能力後發生了狀態變化,咱們經過定義」用戶已下單「這樣的領域事件來描述這種狀態變化。事件溯源的思路就是隻持久化領域事件,而後經過還原事件的方式將領域模型還原在最新的狀態。React生態中的Redux就是經過事件溯源的方式來進行狀態管理。
經過採起事件溯源,就能夠將領域模型持久化跟數據庫徹底解耦。
咱們經過領域驅動設計的思路來分析和發現問題域,經過分解把問題域劃分爲問題子域,經過人爲加限制的方式將問題子域轉換爲限界上下文。而這個過程就是咱們分解微服務的過程,通常來講每個限界上下文均可以映射爲一個微服務,但也不是絕對的,具體狀況具體分析。
每個微服務專一於解決對應的限界上下文中的問題,並不表明微服務之間沒有交流。單個微服務的領域模型在提供服務的過程當中會產生領域事件,領域事件爲基於事件驅動(Event based)的微服務集成提供了基礎,若是在微服務之間架設一條消息總線(不一樣於ESB,ESB被認爲是反模式)。不一樣的微服務將本身產生的領域事件廣播在消息總線上,微服務之間經過訂閱本身感興趣的事件就能完成微服務的集成。
迄今爲止咱們已經創建了領域模型,建立了微服務,經過消息和領域事件完成了微服務的集成。還須要把微服務的能力經過REST API展示出來,微服務在對外提供能力的過程就是領域模型狀態發生變化的過程,若是將領域模型理解爲一個設計精良的狀態機也一點不爲過。若是設法將領域模型在某個狀態下可以提供的能力經過REST API的的返回結果表達出來,這就是HATEOAS的核心思想。REST API不但能夠提供某種能力,還能夠告訴消費者此時領域模型可以提供的其餘能力。
本文從需求分析到API設計,試圖描述領域驅動設計的過程及思想。同時也能看的出領域驅動設計並非孤立存在的,他爲解決開發團隊和業務人員之間溝通而生,進而驅動微服務的設計架構以及API的設計,領域驅動設計並非高不可攀的方法論,每個專業術語和思想都是爲了解決基本的問題而定義,但願本篇博客可以帶你走入領域驅動設計。