原文發(fā)布于:http://www.gufeng.tech/ 谷風(fēng)的個(gè)人主頁
創(chuàng)新互聯(lián)公司專注于淶水企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城開發(fā)。淶水網(wǎng)站建設(shè)公司,為淶水等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站制作,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)2004年Eric Evans 發(fā)表了一本書:《Domain-Driven Design: Tackling Complexity in the Heart of Software》(中文名:《領(lǐng)域驅(qū)動設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對之道》),在這本書中作者提出了領(lǐng)域驅(qū)動設(shè)計(jì)(DDD)的概念,到現(xiàn)在已經(jīng)10多年的時(shí)間了。
面向?qū)ο笏枷胍呀?jīng)存在相當(dāng)長的歷史了(相對于軟件的歷史),我而們使用的語言,很多也都是面向?qū)ο蟮?,但是我們使用面向?qū)ο蟮恼Z言就一定能寫出來面向?qū)ο蟮某绦騿幔匡@然是不可能的。業(yè)務(wù)邏輯代碼的堆積、缺乏良好設(shè)計(jì)的系統(tǒng)或模塊亦或是功能,這樣是不能保證代碼的復(fù)用性、擴(kuò)展性的。
領(lǐng)域驅(qū)動設(shè)計(jì)的出現(xiàn),就是為了解決這一問題的。領(lǐng)域驅(qū)動設(shè)計(jì)是以建立正確的領(lǐng)域模型為核心,以構(gòu)建清晰的分層架構(gòu)基礎(chǔ),從而使面向?qū)ο蟮拈_發(fā)進(jìn)入到了一個(gè)新的階段。
領(lǐng)域驅(qū)動設(shè)計(jì)的前提是有一種能夠在領(lǐng)域?qū)<遥I(yè)務(wù)專家)、設(shè)計(jì)人員、開發(fā)人員(為什么會有開發(fā)人員,我們會在后面介紹原因)三類參與者通用的溝通語言,在三類參與者的不斷交流、溝通中發(fā)現(xiàn)領(lǐng)域概念(業(yè)務(wù)概念),再將概念固化成模型,最后由領(lǐng)域模型驅(qū)動設(shè)計(jì)并實(shí)現(xiàn)。
說到這里,看上去領(lǐng)域模型并沒有什么特別的地方,與我們?nèi)粘7治龅姆绞經(jīng)]什么大的區(qū)別,我們首先來簡單介紹寫領(lǐng)域模型的兩個(gè)特點(diǎn):
1)業(yè)務(wù)邏輯集中在領(lǐng)域?qū)ο螅悾┥希?/small>
2)每個(gè)領(lǐng)域?qū)ο笫峭暾酮?dú)立的,并具有自己的屬性和行為。
在接下來的內(nèi)容中,我們一起來了解下如何實(shí)現(xiàn)領(lǐng)域驅(qū)動設(shè)計(jì)以及領(lǐng)域驅(qū)動設(shè)計(jì)的優(yōu)點(diǎn)。
領(lǐng)域驅(qū)動設(shè)計(jì)涵蓋了領(lǐng)域模型、領(lǐng)域語言、架構(gòu)設(shè)計(jì)、實(shí)現(xiàn)幾部分內(nèi)容,下面我們逐一了解一下。
關(guān)于什么是領(lǐng)域模型以及領(lǐng)域模型的特點(diǎn),在前面內(nèi)容中我們有了整體的了解,接下來我們就領(lǐng)域模型本身進(jìn)行一下簡單的了解。
領(lǐng)域模型是某個(gè)邊界內(nèi)的領(lǐng)域的一個(gè)抽象,是客觀世界的模型,首先它使有邊界的,清晰的邊界是領(lǐng)域模型抽象是否完整的一個(gè)重要衡量指標(biāo)。在該領(lǐng)域模型內(nèi),我們只關(guān)心領(lǐng)域內(nèi)的內(nèi)容。
領(lǐng)域模型只是實(shí)際業(yè)務(wù)的一種反映,與具體實(shí)現(xiàn)技術(shù)無關(guān)??梢哉f領(lǐng)域模型建立的成功與否,直接關(guān)系到最終的實(shí)現(xiàn)、使用等等。領(lǐng)域模型確保任參與人在任何時(shí)間看到的內(nèi)容都是一樣的,了解了模型,就能知道實(shí)現(xiàn)的步驟。
領(lǐng)域模型對于提高軟件的維護(hù)性、復(fù)用性以及業(yè)務(wù)可理解性等方面都有很好的幫助。領(lǐng)域模型貫穿整個(gè)分析、設(shè)計(jì)、開發(fā)過程,前面提到的三類參與者使用一種大家都能理解的語言進(jìn)行溝通,確保所有人對模型的理解是一致的,這樣最終開發(fā)出來的結(jié)果和最初的設(shè)計(jì)才能大程度的吻合。
要建立一個(gè)好的領(lǐng)域模型并不簡單,甚至可能是一路坎坷,需要領(lǐng)域?qū)<摇⒃O(shè)計(jì)人員、開發(fā)人員通力配合、深入交流、共享信息和知識。最后,領(lǐng)域模型要通過文檔或圖形方式展現(xiàn)出來(推薦使用圖形分解整體結(jié)構(gòu),配以文字說明)。設(shè)計(jì)足夠好的領(lǐng)域模型,肯定是符合業(yè)務(wù)需求的,同時(shí)也能夠快速響應(yīng)需求變化。
與領(lǐng)域模型緊密相關(guān)的還有另外一組概念:聚合、聚合根。下面我們來簡單了解下這兩個(gè)概念。
聚合:通過定義對象間的隸屬關(guān)系和邊界來實(shí)現(xiàn)領(lǐng)域模型的內(nèi)聚,
聚合根:聚合內(nèi)的某個(gè)實(shí)體,外部調(diào)用聚合時(shí),必須從聚合根開始調(diào)用,不能繞過。
關(guān)于聚合的一些特點(diǎn):
1)每個(gè)聚合有一個(gè)根和邊界;
2)內(nèi)部對象可互相引用,但是外部對象訪問聚合時(shí),必須從聚合根開始;
3)除根外,其它對象在聚合內(nèi)保持唯一即可;
4)聚合內(nèi)部對象可以保持對其它聚合根的引用;
5)刪除聚合根時(shí),必須同時(shí)刪除其它聚合內(nèi)對象。
所有具有獨(dú)立含義并且能夠被單獨(dú)訪問的內(nèi)容是聚合。
設(shè)想一下,領(lǐng)域?qū)<覞M口的專業(yè)術(shù)語,設(shè)計(jì)人員滿口的設(shè)計(jì)理論,開發(fā)人員滿口的開發(fā)語言及算法,這樣的團(tuán)隊(duì)怎么溝通!當(dāng)然可以引入“翻譯”,但是“翻譯”的結(jié)果以及對結(jié)果的理解會造成多大程度上的信息丟失,誰也不確定。
基于以上的原因,迫切需要一種大家都能夠表達(dá)出來和理解的語言——這就是領(lǐng)域通用語言。領(lǐng)域通用語言是領(lǐng)域驅(qū)動設(shè)計(jì)的基礎(chǔ)和前提。在三類參與者的各種形式溝通中,都要使用領(lǐng)域通用語言,確保自己的信息能夠被其他人完整、快速的理解。
假設(shè)我們已經(jīng)擁有了一個(gè)非常正確且嚴(yán)謹(jǐn)?shù)哪P?,那么是否能將這個(gè)模型直接轉(zhuǎn)換成代碼嗎?肯定是不行的。所以要求我們在領(lǐng)域建模和設(shè)計(jì)時(shí),就要考慮最終的代碼實(shí)現(xiàn),將領(lǐng)域模型與實(shí)現(xiàn)緊密關(guān)聯(lián)起來,這就是為什么要有開發(fā)人員參與的原因。
這樣的結(jié)構(gòu)(開發(fā)人員參與模型建立、結(jié)構(gòu)設(shè)計(jì))有利于盡早發(fā)現(xiàn)那些不適合在軟件中實(shí)現(xiàn)的模型部分并要求修正,這樣也避免了在最后實(shí)現(xiàn)時(shí)發(fā)現(xiàn)問題、修正設(shè)計(jì)所帶來的巨大時(shí)間損失。同時(shí),因?yàn)殚_發(fā)人員參與了模型設(shè)計(jì),所以在編碼實(shí)現(xiàn)時(shí),都會盡力保護(hù)模型不被破壞(因?yàn)檫@是大家共同努力的結(jié)果),同時(shí)當(dāng)開發(fā)人員發(fā)現(xiàn)編碼實(shí)現(xiàn)有不滿足模型或者不完善的地方,也會去完善它,進(jìn)行代碼重構(gòu),這樣能夠在很大程度上提升軟件的可靠性,也便于其他人員在接手時(shí)能快速了解模型、掌握實(shí)現(xiàn)。
我們先來看一張Eric Evans 在他的《Domain-Driven Design: Tackling Complexity in the Heart of Software》一書中提到的分層圖:
關(guān)于這張圖可能都不陌生,但是每一層在領(lǐng)域驅(qū)動設(shè)計(jì)中的職責(zé)是什么?完成什么樣的功能?層與層之間的協(xié)作關(guān)系是什么樣的?這些問題會在后面一一解釋。
1)用戶界面
人機(jī)交互部分,沒有特殊內(nèi)容。
2)應(yīng)用
此應(yīng)用非彼應(yīng)用,這里的應(yīng)用只是很薄的一層,用于給User Interface提供功能接口,并調(diào)用Domain完成功能邏輯,看到這里應(yīng)該有了比較明確的認(rèn)識了,Application不包括任何業(yè)務(wù),只是將根據(jù)User Interface的需要提供接口,并完成對一個(gè)或者多個(gè)Domain的調(diào)用。
本層包含了所有軟件系統(tǒng)要完成的任務(wù),通過本層就能了解整體功能,User Interface僅僅是一種展現(xiàn)方式。
3)領(lǐng)域
這一層是整個(gè)系統(tǒng)的核心部分,包括了全部的業(yè)務(wù)邏輯、業(yè)務(wù)規(guī)則等全部業(yè)務(wù)相關(guān)內(nèi)容。
4)基礎(chǔ)設(shè)施
這里的基礎(chǔ)設(shè)施指的是基礎(chǔ)技術(shù)組件,包括消息通信、持久化、緩存等等所有的基礎(chǔ)技術(shù)組件。
1)實(shí)體(Entity)
具有跨越系統(tǒng)的生命周期甚至能超越軟件系統(tǒng)的一系列的延續(xù)性和標(biāo)識符的對象成為實(shí)體。簡單說就是具有絕對唯一標(biāo)識的對象。比如銀行賬戶的ID是唯一的標(biāo)識,那么一個(gè)銀行賬戶就是一個(gè)實(shí)體。實(shí)體擁有自己的屬性,管理自己的內(nèi)部狀態(tài)并對外暴露行為。
2)值對象(Value Object)
當(dāng)我們關(guān)心對象的唯一標(biāo)識而只關(guān)心其屬性值的時(shí)候,這個(gè)對象就是一個(gè)值對象。對于值對象,理論上可以被輕易的創(chuàng)建以丟掉。如果是可共享值對象,那應(yīng)該確保它的值是不可變的。“值對象應(yīng)該保持盡量的簡單。當(dāng)其他當(dāng)事人需要一個(gè)值對象時(shí),可以簡單地傳遞值,或者創(chuàng)建一個(gè)副本。”
3)服務(wù)(Service)
是不是所有的領(lǐng)域都能映射成對象呢?顯然是不可能的,那么如何處理不能夠映射成對象的領(lǐng)域呢?這時(shí)候就需要服務(wù)這個(gè)東西了。服務(wù)通常對應(yīng)領(lǐng)域的動作,代表領(lǐng)域中得一些重要行為,而這些行為又不屬于任何一個(gè)實(shí)體或者值對象。這些行為可以定義為服務(wù)對象。
服務(wù)可能存在于領(lǐng)域?qū)?、基礎(chǔ)設(shè)施層等,所以要區(qū)分服務(wù),不要濫用服務(wù)。
服務(wù)對象不包含內(nèi)部狀態(tài),只有行為,所以它提供的主要是行為,作為操作接口存在。我們需要注意的是,不需要對每一個(gè)操作創(chuàng)建服務(wù)。我們一起來一下服務(wù)的幾個(gè)特征:
(1)服務(wù)執(zhí)行的操作涉及一個(gè)領(lǐng)域概念,這個(gè)領(lǐng)域概念通常不屬于一個(gè)實(shí)體或者值對象;
(2)被執(zhí)行的操作涉及到領(lǐng)域中的其他的對象;
(3)操作是無狀態(tài)的。
4)模塊(Module)
當(dāng)模型巨大,難以整體討論時(shí),需要把這個(gè)大得模型拆成幾個(gè)關(guān)聯(lián)的模塊。
5)聚合&聚合根
聚合是針對數(shù)據(jù)變化可以考慮成一個(gè)單元的一組相關(guān)的對象。聚合使用邊界將內(nèi)部和外部的對象劃分開來。每個(gè)聚合有一個(gè)根,是一個(gè)實(shí)體,并且它是外部可以訪問的唯一的對象。
關(guān)于聚合與聚合根的內(nèi)容,可參考2.1.1節(jié)。
6)Factory(工廠)
引入工廠模式,是因?yàn)轭I(lǐng)域模型本身的復(fù)雜性決定的,創(chuàng)建領(lǐng)域?qū)ο笠h(yuǎn)遠(yuǎn)比創(chuàng)建pojo對象復(fù)雜得多,尤其是聚合會更加復(fù)雜。此時(shí)引入工廠模式,可以將復(fù)雜的實(shí)現(xiàn)放在工廠內(nèi),外部調(diào)用工廠方法即可得到相應(yīng)領(lǐng)域?qū)ο?,同時(shí)也隱藏了創(chuàng)建邏輯(主要是提供給Application和Infrastructure使用的)。
7)Repository(倉儲、資源庫)
倉儲最初的設(shè)計(jì)目的是用來管理內(nèi)存中的對象,但我們可以擴(kuò)展使用,對于需要持久化的領(lǐng)域?qū)ο螅褂肦epository將其持久化到數(shù)據(jù)庫(或其它持久化存儲)中,再次需要時(shí),可以通過Repository將對象從數(shù)據(jù)庫中恢復(fù)。通常情況下,一個(gè)聚合對應(yīng)一個(gè)倉儲。
那么對于那些不能夠通過單一Repository查詢出來的結(jié)果(比如界面中需要展現(xiàn)的數(shù)據(jù)來源于多個(gè)Repository的情況)我們該怎么辦呢?當(dāng)然可以通過調(diào)用多個(gè)Repository查詢出結(jié)果,但更好的方式是通過CQRS架構(gòu)來實(shí)現(xiàn),也就是說對于查詢可繞過Domain,直接由Application發(fā)起調(diào)用另外的架構(gòu)或者層來實(shí)現(xiàn)。
8)CQRS(Command Query Responsibility Segregation,命令查詢職責(zé)分離)
從字面理解,就是命令和查詢要分離開,那么什么事命令呢?非查詢的操作即命令。結(jié)合領(lǐng)域驅(qū)動設(shè)計(jì),我們可以理解成命令可以通過領(lǐng)域驅(qū)動設(shè)計(jì)完成,查詢則可使用簡單、直接的方式完成(如直接寫SQL)。
由于是分離的,所以兩部分可以采用相同甚至完全不同的架構(gòu)來實(shí)現(xiàn),由此引申,是不是數(shù)據(jù)庫也可以分開設(shè)計(jì)呢?當(dāng)然是可以的。
本文中我們粗略的了解了領(lǐng)域驅(qū)動設(shè)計(jì)的一些基本概念、原則和一些所謂的“模式”,在實(shí)際使用或者叫“領(lǐng)域驅(qū)動設(shè)計(jì)落地”的過程中,除了一些必須遵守的原則外,我們可以根據(jù)自己的業(yè)務(wù)特點(diǎn)、團(tuán)隊(duì)優(yōu)勢進(jìn)行裁剪。
沒有任何一種語言是具有絕對優(yōu)勢的,同樣也沒有任何一種設(shè)計(jì)方法是絕對正確的。找準(zhǔn)我們自己的方向,找出適合我們業(yè)務(wù)特點(diǎn)、團(tuán)隊(duì)特點(diǎn)的方法,并對該方法進(jìn)行落地裁剪,使之更具生命力、能夠解決我們的實(shí)際問題。
最后,不要迷信、迷戀任何一種或幾種方法、模式,所有的方法都是人根據(jù)經(jīng)驗(yàn)總結(jié)出來,方法、模式可以參考并綜合使用,最終達(dá)到擁有自己的方法、自己的模式,這樣才能更好的服務(wù)于自己的業(yè)務(wù),創(chuàng)造技術(shù)體系。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。