|
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)的概念源于2004年著名建模專家Eric Evans發(fā)表的書(shū)籍:《Domain-Driven Design – Tackling Complexity in the Heart of Software》(中文譯名:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)—軟件核心復(fù)雜性應(yīng)對(duì)之道),池建強(qiáng)在2011年發(fā)表的一篇文章《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和實(shí)踐》中是這樣形容DDD的:
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)事實(shí)上是針對(duì)OOAD的一個(gè)擴(kuò)展和延伸,DDD基于面向?qū)ο蠓治雠c設(shè)計(jì)技術(shù),對(duì)技術(shù)架構(gòu)進(jìn)行了分層規(guī)劃,同時(shí)對(duì)每個(gè)類進(jìn)行了策略和類型的劃分。
本文主要介紹為什么我們?cè)诤阃?a href=/yuedu/kaiyuan/ target=_blank class=infotextkey>開(kāi)源內(nèi)部推廣DDD,我們?nèi)绾瓮ㄟ^(guò)開(kāi)發(fā) DDDLib 和 Koala 等工具來(lái)完善這一過(guò)程,推廣過(guò)程中遇到了哪些問(wèn)題,以及我們?nèi)绾谓鉀Q這一問(wèn)題。
為什么選擇DDD
傳統(tǒng)的模式的最大優(yōu)點(diǎn)在于開(kāi)發(fā)人員非常熟悉,開(kāi)發(fā)成本低,但它也有一些問(wèn)題:
采用DDD開(kāi)發(fā)模式之前,傳統(tǒng)的開(kāi)發(fā)模型是最流行的Model-Dao-Service-UI開(kāi)發(fā)模型,通常是基于事務(wù)腳本(Transaction Script)和表模塊(Table Module)模式的實(shí)現(xiàn),這種模式通常是先設(shè)計(jì)表,再建模,實(shí)現(xiàn)容易依賴特定的表的一些特性,如存儲(chǔ)過(guò)程。基于表的設(shè)計(jì)模式容易帶來(lái)以下幾個(gè)問(wèn)題:
- 業(yè)務(wù)建模完全是表的復(fù)制,無(wú)法真實(shí)反映業(yè)務(wù)。
- 核心業(yè)務(wù)分散在各個(gè)地方,非常危險(xiǎn),修改擴(kuò)展難,且難以閱讀。
這種開(kāi)發(fā)模式適合一些需求小,后續(xù)維護(hù)擴(kuò)展需求小的中小型項(xiàng)目,但在大型企業(yè)級(jí)系統(tǒng)或產(chǎn)品,擴(kuò)展維護(hù)或需求變量非常多的情況下,缺點(diǎn)也非常明顯。
相對(duì)而言,DDD則有以下四點(diǎn)好處:
1、面向?qū)ο螅P驼鎸?shí)反映業(yè)務(wù)現(xiàn)實(shí):使用DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),模型通常是業(yè)務(wù)的真實(shí)反映,業(yè)務(wù)集中在領(lǐng)域而不是分散在各Service中,有利于對(duì)業(yè)務(wù)的理解。
2、使用領(lǐng)域統(tǒng)一建模語(yǔ)言:有利于業(yè)務(wù)溝通與建模: DDD倡導(dǎo)先對(duì)業(yè)務(wù)建模,而非關(guān)注表或腳本的設(shè)計(jì);在建模過(guò)程中,由于領(lǐng)域本身是對(duì)真實(shí)業(yè)務(wù)的反映與建模,因此與業(yè)務(wù)專家更容易溝通,打破技術(shù)與業(yè)務(wù)的溝通隔閡。
3、可重用性高:DDD中,領(lǐng)域?qū)訛楹诵模總€(gè)領(lǐng)域?qū)ο蠖际且粋€(gè)相對(duì)完整的內(nèi)聚的業(yè)務(wù)對(duì)象描述,所以可以形成直接的復(fù)用。基于領(lǐng)域建模的設(shè)計(jì),并不會(huì)依賴特定的數(shù)據(jù)庫(kù)及特性,模型是可以完全重用且沒(méi)有技術(shù)上的沖突。
4、業(yè)務(wù)越復(fù)雜,DDD的優(yōu)勢(shì)越明顯:領(lǐng)域模型采用OO設(shè)計(jì),通過(guò)將職責(zé)分配到相應(yīng)的模型對(duì)象或Service,可以很好的組織業(yè)務(wù)邏輯,當(dāng)業(yè)務(wù)變得復(fù)雜時(shí),領(lǐng)域模型顯出巨大的優(yōu)勢(shì)。
DDDLib登場(chǎng)
DDD本質(zhì)上是一種思想,并不是新技術(shù)。在恒拓開(kāi)源,由我們的楊宇老師和陳操總共同創(chuàng)作的DDDLib庫(kù),是對(duì)DDD思想的核心支持與實(shí)現(xiàn)。
DDDLib是一整套支持DDD思想實(shí)現(xiàn)的類庫(kù),DDDLib下還是使用的 Hibernate、JPA或MyBatis、noSQL等技術(shù)為實(shí)現(xiàn)。
如同DDD所要求樣,使用DDDLib 的項(xiàng)目分層圖為:
- 用戶界面/展現(xiàn)層
- 用于向用戶展現(xiàn)信息,處理用戶在界面上的請(qǐng)求,比如struts,tapestry,springMVC等頁(yè)面框架。
- 應(yīng)用層
- 用來(lái)處理應(yīng)用的活動(dòng),不包含領(lǐng)域中的業(yè)務(wù)邏輯。
- 應(yīng)用層可以用來(lái)處理一些與領(lǐng)域概念無(wú)關(guān)的攔截性質(zhì)的工作,比如日志,事務(wù)等。此外,應(yīng)用層也可以用來(lái)處理一些既不屬于展現(xiàn)層,也不屬于領(lǐng)域?qū)樱菍儆谀壳?a href=/pingce/yingyong/ target=_blank class=infotextkey>應(yīng)用相關(guān)的一些服務(wù)。比如資金轉(zhuǎn)賬的業(yè)務(wù)的讀取輸入功能(讀取輸入不是轉(zhuǎn)賬的核心業(yè)務(wù)含義)。
- 領(lǐng)域?qū)?/li>
- 此層是DDD的核心:領(lǐng)域?qū)ο螅I(lǐng)域服務(wù),倉(cāng)儲(chǔ)接口均位于此層。
- 領(lǐng)域的信息,是業(yè)務(wù)軟件的核心所在。
- 需要保留業(yè)務(wù)對(duì)象的狀態(tài),對(duì)業(yè)務(wù)對(duì)象及其狀態(tài)持久化的操作交給基礎(chǔ)設(shè)施層。
- 領(lǐng)域?qū)討?yīng)該遵從以下原則:除非業(yè)務(wù)發(fā)生變化,否則其他任何變化均不應(yīng)該影響到領(lǐng)域?qū)印_@些其他變化包括:不同的展現(xiàn)框架,不同的頁(yè)面展現(xiàn)內(nèi)容,是否要分頁(yè),是否支持手機(jī)客戶端,是否公開(kāi)WebService,是否提供OpenAPI等等。
- 基礎(chǔ)設(shè)施層
DDDLib的核心實(shí)現(xiàn)如下:
上圖就是使用 DDDLib 項(xiàng)目的整體技術(shù)架構(gòu)圖,也表明了DDDLib的整體原則:
- 領(lǐng)域?qū)邮菢I(yè)務(wù)核心,這一層不依賴任何特定的技術(shù)框架,保證它的業(yè)務(wù)純潔性。DDDLib中的領(lǐng)域?qū)又灰蕾嘕DK、DDDLib的Domain庫(kù)以及倉(cāng)儲(chǔ)接口及其它自定義接口。
- 使用倉(cāng)儲(chǔ)和查詢通道作為與存儲(chǔ)介質(zhì)相關(guān)的操作接口,隔離對(duì)特定數(shù)據(jù)庫(kù)技術(shù)或存儲(chǔ)介質(zhì)的依賴。
- 提供多種不同的IoC容器實(shí)現(xiàn)及InstanceFactory實(shí)例工廠,隔離對(duì)特定IOC 技術(shù)的依賴。
- 領(lǐng)域?qū)影ㄖ祵?duì)象、領(lǐng)域?qū)ο笠约邦I(lǐng)域服務(wù)三個(gè)要素,領(lǐng)域?qū)硬皇菙?shù)據(jù)庫(kù)操作層而是業(yè)務(wù)建模層。許多開(kāi)發(fā)者在使用的過(guò)程中,最終還是把領(lǐng)域?qū)幼鳛閿?shù)據(jù)庫(kù)操作層來(lái)使用,對(duì)實(shí)體的方法也是以數(shù)據(jù)庫(kù)的操作行為為標(biāo)準(zhǔn) ,如查詢,新增,刪除一個(gè)實(shí)體,最終依然回歸到以數(shù)據(jù)庫(kù)為中心的方向去了,這是需要避免的。
- 改變以數(shù)據(jù)庫(kù)為中心的核心是意識(shí)到業(yè)務(wù)行為才是核心,數(shù)據(jù)庫(kù)存儲(chǔ)是支撐。意識(shí)到數(shù)據(jù)庫(kù)是支撐非常關(guān)鍵,業(yè)務(wù)上的任何行為,在系統(tǒng)中最終需要存儲(chǔ)記錄,數(shù)據(jù)庫(kù)存儲(chǔ)是對(duì)業(yè)務(wù)實(shí)現(xiàn)的支撐,也可以使用文件,緩存或云空間等其它存儲(chǔ)介質(zhì)。想像一下,使用數(shù)據(jù)庫(kù)進(jìn)行設(shè)計(jì)的項(xiàng)目,最終就限定了存儲(chǔ)介質(zhì)為特定的數(shù)據(jù)庫(kù),如果下一次需要更換為云空間或緩存等其它存儲(chǔ)形式,就會(huì)發(fā)現(xiàn)整個(gè)系統(tǒng)需要重新設(shè)計(jì)開(kāi)發(fā),但使用DDDLib,只需更換倉(cāng)儲(chǔ)實(shí)現(xiàn),提供一個(gè)云空間的實(shí)現(xiàn)就行了,核心業(yè)務(wù)邏輯完全不需要變動(dòng)與修改。
DDDLib在實(shí)現(xiàn)過(guò)程中也經(jīng)歷了內(nèi)部的不少爭(zhēng)議,經(jīng)過(guò)很多次的討論和打磨形成了現(xiàn)在的格局。在下面的部分,我將介紹DDDLib在幾個(gè)重要組件上的實(shí)現(xiàn)細(xì)節(jié)。
DDDLib倉(cāng)儲(chǔ)的實(shí)現(xiàn)
從DDDLib 1.0到3.5版本,倉(cāng)儲(chǔ)實(shí)現(xiàn)歷經(jīng)幾個(gè)階段,分別是:
- 給每個(gè)領(lǐng)域?qū)ο蠖x一個(gè)倉(cāng)儲(chǔ)接口及一個(gè)倉(cāng)儲(chǔ)實(shí)現(xiàn)。
這種倉(cāng)儲(chǔ)實(shí)現(xiàn)非常受爭(zhēng)議,開(kāi)發(fā)人員并不認(rèn)可這種方式,倉(cāng)儲(chǔ)接口及實(shí)現(xiàn)非常多,一方面導(dǎo)致項(xiàng)目類太多,并且也帶來(lái)編碼的重復(fù)操作。
- 與spring data整合,給每個(gè)領(lǐng)域?qū)ο蠖x倉(cāng)儲(chǔ)接口,無(wú)須定義實(shí)現(xiàn)。這種模式對(duì)前面的模式有了優(yōu)化,只定義接口不定義實(shí)現(xiàn),但是spring data這種依賴方法名,參數(shù)來(lái)進(jìn)行查詢的模式,針對(duì)一些復(fù)雜的查詢,難以勝任。
- 提供默認(rèn)的hibernate及JPA的通用倉(cāng)儲(chǔ)接口。
為每個(gè)倉(cāng)儲(chǔ)定義一個(gè)接口,這種模式慢慢的不被接受,使用spring data帶來(lái)的優(yōu)化方案,也有非常多的問(wèn)題,后面根據(jù)JPA的實(shí)體管理思路,于是形成了通用倉(cāng)儲(chǔ)接口及不同技術(shù)實(shí)現(xiàn)的思路,定義一個(gè)通用的倉(cāng)儲(chǔ)接口,包括通用的增刪改查數(shù)據(jù)庫(kù)行為。
- 支持MyBatis的通用倉(cāng)儲(chǔ)接口
DDDLib的JPA及hibernate倉(cāng)儲(chǔ)實(shí)現(xiàn),這個(gè)方案是一個(gè)較佳的方案,所有領(lǐng)域?qū)嶓w使用通用的倉(cāng)儲(chǔ),避免了大量重復(fù)代碼,但DDDLib一直是基于Hibernate/JPA提供的實(shí)現(xiàn)與技術(shù)支持,在項(xiàng)目的使用過(guò)程中,經(jīng)常會(huì)遇到不適合使用Hibernate/JPA模式的項(xiàng)目,對(duì)MyBatis的需求也非常大,這種情況下,Koala團(tuán)隊(duì)定義實(shí)現(xiàn)了MyBatis的倉(cāng)儲(chǔ)實(shí)現(xiàn),并保證其與JPA/Hibernate模式下API的一致性。
DDDLib中的DTO
DTO,數(shù)據(jù)傳輸對(duì)象,領(lǐng)域?qū)ο箅m然有數(shù)據(jù)(屬性),但是領(lǐng)域?qū)ο笊厦孢€帶有操作,在某些場(chǎng)合不適合進(jìn)行傳輸,有些時(shí)候傳輸還需要序列化。但并不是所有的領(lǐng)域?qū)ο髮傩远伎梢员┞叮矣行傩钥赡芤喜ⅲ赡芤纸猓蟛庞欣谇岸说氖褂谩S谑蔷陀辛藢iT用來(lái)傳輸數(shù)據(jù)的DTO,只有屬性,沒(méi)有操作,必要的時(shí)候加上序列化標(biāo)記,實(shí)現(xiàn)遠(yuǎn)程調(diào)用。
這是DDD中DTO的作用,但是DTO同時(shí)也帶來(lái)了實(shí)體與DTO的轉(zhuǎn)換性能問(wèn)題,在大數(shù)據(jù)量下尤其明顯。
DDDLib中的數(shù)據(jù)庫(kù)支撐行為類
在DDDLib實(shí)現(xiàn)中,提供了Repository以及QueryChannelService兩個(gè)接口,分別使用在領(lǐng)域?qū)右约?a href=/pingce/yingyong/ target=_blank class=infotextkey>應(yīng)用層,都是對(duì)數(shù)據(jù)庫(kù)的操作接口。
SQL/HQL/JPQL寫在哪
在使用DDDLib的過(guò)程中,不同的持久層框架的SQL語(yǔ)言不一樣,比如MyBatis使用的是SQL,Hibernate使用的是 HQL,JPA下使用的是JPQL。
這些語(yǔ)句寫在哪兒在公司也經(jīng)歷過(guò)一番爭(zhēng)議與變更,歷史如下:
1、寫在代碼中
public static Resource newResource(String name,String identifier,String level,String menuIcon){ Resource resource = null; List<Resource> resources = Resource.getRepository().find("select r from Resource r where r.name = ? " + "and r.identifier = ?", new Object[]{name,identifier}, Resource.class); ...}
it知識(shí)庫(kù):DDD &amp; DDDLib在恒拓開(kāi)源的發(fā)展歷程與推廣經(jīng)驗(yàn),轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。