軟件系統(tǒng)面向?qū)ο蟮脑O(shè)計(jì)思想可謂歷史悠久,20世紀(jì)70年代的Smalltalk可以說是面向?qū)ο笳Z言的經(jīng)典,直到今天我們依然將這門語言視為面向?qū)ο笳Z言的基礎(chǔ)。隨著編程語言和技術(shù)的發(fā)展,各種語言特性層出不窮,面向?qū)ο笫谴蟛糠终Z言的一個(gè)基本特性,像C++、Java、C#這樣的靜態(tài)語言,Ruby、Python這樣的動(dòng)態(tài)語言都是面向?qū)ο蟮恼Z言。
但是面向?qū)ο笳Z言并不是銀彈,如果開發(fā)人員認(rèn)為使用面向?qū)ο笳Z言寫出來的程度本身就是面向?qū)ο蟮模蔷痛箦e(cuò)特錯(cuò)了。實(shí)際開發(fā)中,大量的業(yè)務(wù)邏輯堆積在一個(gè)巨型類中的例子屢見不鮮,代碼的復(fù)用性和擴(kuò)展性無法得到保證。為了解決這樣的問題,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)提出了清晰的分層架構(gòu)和領(lǐng)域?qū)ο蟮母拍睿屆嫦驅(qū)ο蟮姆治龊驮O(shè)計(jì)進(jìn)入了一個(gè)新的階段,對(duì)企業(yè)級(jí)軟件開發(fā)起到了巨大的推動(dòng)作用。
本文主要介紹了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的基本概念、要素、特點(diǎn),對(duì)比了事務(wù)腳本和領(lǐng)域模型的特點(diǎn),最后介紹了我們?cè)谲浖_發(fā)過程中的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐。
2004年著名建模專家Eric Evans發(fā)表了他最具影響力的書籍:《Domain-Driven Design: Tackling Complexity in the Heart of Software》(中文譯名:領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道),書中提出了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(簡(jiǎn)稱 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)行了策略和類型的劃分。
領(lǐng)域模型是領(lǐng)域驅(qū)動(dòng)的核心。采用DDD的設(shè)計(jì)思想,業(yè)務(wù)邏輯不再集中在幾個(gè)大型的類上,而是由大量相對(duì)小的領(lǐng)域?qū)ο?類)組成,這些類具備自己的狀態(tài)和行為,每個(gè)類是相對(duì)完整的獨(dú)立體,并與現(xiàn)實(shí)領(lǐng)域的業(yè)務(wù)對(duì)象映射。領(lǐng)域模型就是由這樣許多的細(xì)粒度的類組成?;陬I(lǐng)域驅(qū)動(dòng)的設(shè)計(jì),保證了系統(tǒng)的可維護(hù)性、擴(kuò)展性和復(fù)用性,在處理復(fù)雜業(yè)務(wù)邏輯方面有著先天的優(yōu)勢(shì)。
領(lǐng)域驅(qū)動(dòng)的核心應(yīng)用場(chǎng)景就是解決復(fù)雜業(yè)務(wù)的設(shè)計(jì)問題,其特點(diǎn)與這一核心主題息息相關(guān):
面對(duì)復(fù)雜的業(yè)務(wù)場(chǎng)景和需求,如果沒有建立和實(shí)現(xiàn)領(lǐng)域模型,會(huì)導(dǎo)致應(yīng)用架構(gòu)出現(xiàn)胖服務(wù)層和貧血的領(lǐng)域模型,在這樣的架構(gòu)中,Service層開始積聚越來越多的業(yè)務(wù)邏輯,領(lǐng)域?qū)ο髣t成為只有g(shù)etter和setter方法的數(shù)據(jù)載體。這種做法還會(huì)導(dǎo)致領(lǐng)域特定業(yè)務(wù)邏輯和規(guī)則散布于多個(gè)的Service類中,有些情況下還會(huì)出現(xiàn)重復(fù)的邏輯。我們?cè)?jīng)見過5000多行的Service類,上百個(gè)方法,代碼基本上是不可讀的。
在大多數(shù)情況下,貧血的領(lǐng)域模型沒有成本效益。它們不會(huì)給公司帶來超越其它公司的競(jìng)爭(zhēng)優(yōu)勢(shì),因?yàn)樵谶@種架構(gòu)里要實(shí)現(xiàn)業(yè)務(wù)需求變更,開發(fā)并部署到生產(chǎn)環(huán)境中去要花費(fèi)太長(zhǎng)的時(shí)間。
下面我們簡(jiǎn)單介紹一下領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的分層架構(gòu)和構(gòu)成要素,這部分內(nèi)容在Eric Evans的書中有非常詳盡的描述,想要詳細(xì)了解的,最好去讀原版書籍。
下面這張圖是該書中著名的分層架構(gòu)圖,如下:
整個(gè)架構(gòu)分為四層,其核心就是領(lǐng)域?qū)樱―omain),所有的業(yè)務(wù)邏輯應(yīng)該在領(lǐng)域?qū)訉?shí)現(xiàn),具體描述如下:
用戶界面/展現(xiàn)層 |
負(fù)責(zé)向用戶展現(xiàn)信息以及解釋用戶命令。 |
應(yīng)用層 |
很薄的一層,用來協(xié)調(diào)應(yīng)用的活動(dòng)。它不包含業(yè)務(wù)邏輯。它不保留業(yè)務(wù)對(duì)象的狀態(tài),但它保有應(yīng)用任務(wù)的進(jìn)度狀態(tài)。 |
領(lǐng)域?qū)? |
本層包含關(guān)于領(lǐng)域的信息。這是業(yè)務(wù)軟件的核心所在。在這里保留業(yè)務(wù)對(duì)象的狀態(tài),對(duì)業(yè)務(wù)對(duì)象和它們狀態(tài)的持久化被委托給了基礎(chǔ)設(shè)施層。 |
基礎(chǔ)設(shè)施層 |
本層作為其他層的支撐庫(kù)存在。它提供了層間的通信,實(shí)現(xiàn)對(duì)業(yè)務(wù)對(duì)象的持久化,包含對(duì)用戶界面層的支撐庫(kù)等作用。 |
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)除了對(duì)系統(tǒng)架構(gòu)進(jìn)行了分層描述,還對(duì)對(duì)象(Object)做了明確的職責(zé)和策略劃分:
當(dāng)然,DDD中還提出了聚合和聚合根(Aggregate Root)的概念,不過我們?cè)趯?shí)踐過程發(fā)現(xiàn)聚合根有問題復(fù)雜化的傾向,用傳統(tǒng)的聚合、組合等概念去描述領(lǐng)域?qū)ο笾g的關(guān)系更容易理解,所以這里對(duì)這個(gè)概念就不做介紹了。
Martin Fowler 2004年所著的企業(yè)應(yīng)用架構(gòu)模式(Patterns of Enterprise Application Architecture)中的第九章領(lǐng)域邏輯模式(Domain Logic Patterns)專門介紹了事務(wù)腳本(Transaction Script)和領(lǐng)域模型(Domain Model),理解這兩種模式對(duì)設(shè)計(jì)和構(gòu)建企業(yè)應(yīng)用軟件非常有幫助,所以有必要介紹一下。
事務(wù)腳本:
事務(wù)腳本的核心是過程,通過過程的調(diào)用來組織業(yè)務(wù)邏輯,每個(gè)過程處理來自表現(xiàn)層的單個(gè)請(qǐng)求。大部分業(yè)務(wù)應(yīng)用都可以被看成一系列事務(wù),從某種程度上來說,通過事務(wù)腳本處理業(yè)務(wù),就像執(zhí)行一條條SQL語句來實(shí)現(xiàn)數(shù)據(jù)庫(kù)信息的處理。事務(wù)腳本把業(yè)務(wù)邏輯組織成單個(gè)過程,在過程中直接調(diào)用數(shù)據(jù)庫(kù),業(yè)務(wù)邏輯在服務(wù)(Service)層處理。
事務(wù)腳本模式可以簡(jiǎn)單的通過UML圖表示成這樣:
由Action層處理UI層的動(dòng)作請(qǐng)求,將Request中的數(shù)據(jù)組裝后傳遞給BusinessService,BS層做簡(jiǎn)單的邏輯處理后,調(diào)用數(shù)據(jù)訪問對(duì)象進(jìn)行數(shù)據(jù)持久化,其中VO充當(dāng)了數(shù)據(jù)傳輸對(duì)象的作用,一般是貧血的POJO,只具備getter和setter方法,沒有狀態(tài)和行為。
事務(wù)腳本模式的特點(diǎn)是簡(jiǎn)單容易理解,面向過程設(shè)計(jì)。對(duì)于少量邏輯的業(yè)務(wù)應(yīng)用來說,事務(wù)腳本模式簡(jiǎn)單自然,性能良好,容易理解,而且一個(gè)事務(wù)的處理不會(huì)影響其他事務(wù)。不過缺點(diǎn)也很明顯,對(duì)于復(fù)雜的業(yè)務(wù)邏輯處理力不從心,難以保持良好的設(shè)計(jì),事務(wù)之間的冗余代碼不斷增多,通過復(fù)制粘貼方式進(jìn)行復(fù)用。可維護(hù)性和擴(kuò)展性變差。
領(lǐng)域模型:
領(lǐng)域模型的特點(diǎn)也比較明顯, 屬于面向?qū)ο笤O(shè)計(jì),領(lǐng)域模型具備自己的屬性行為狀態(tài),并與現(xiàn)實(shí)世界的業(yè)務(wù)對(duì)象相映射。各類具備明確的職責(zé)劃分,領(lǐng)域?qū)ο笤刂g通過聚合和引用等關(guān)系配合解決實(shí)際業(yè)務(wù)應(yīng)用和規(guī)則??蓮?fù)用,可維護(hù),易擴(kuò)展,可以采用合適的設(shè)計(jì)模型進(jìn)行詳細(xì)設(shè)計(jì)。缺點(diǎn)是相對(duì)復(fù)雜,要求設(shè)計(jì)人員有良好的抽象能力。
領(lǐng)域模型對(duì)應(yīng)的就是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中劃分的領(lǐng)域?qū)?,這里就不詳細(xì)討論了。
在實(shí)際的設(shè)計(jì)中,我們需要根據(jù)具體的需求選擇相應(yīng)的設(shè)計(jì)模式。具備復(fù)雜業(yè)務(wù)邏輯的核心業(yè)務(wù)系統(tǒng)適合使用領(lǐng)域模型,簡(jiǎn)單的信息管理系統(tǒng)可以考慮采用事務(wù)腳本模式。
下面主要講一下我們?cè)跇?gòu)建企業(yè)級(jí)應(yīng)用開發(fā)平臺(tái)中對(duì)DDD的實(shí)踐和擴(kuò)展。
本人近年來一直在從事企業(yè)級(jí)應(yīng)用開發(fā)平臺(tái)的相關(guān)工作,GAP平臺(tái)是我們的一個(gè)軟件產(chǎn)品,用來解決企業(yè)級(jí)軟件開發(fā)過程中復(fù)用、快速開發(fā)和過程規(guī)范等問題。設(shè)計(jì)這樣一個(gè)平臺(tái),從底層的框架上就應(yīng)該能夠支撐復(fù)雜業(yè)務(wù)邏輯的系統(tǒng)構(gòu)建,所以我們?cè)诖蟮募軜?gòu)設(shè)計(jì)思路上采用了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的思路,并根據(jù)實(shí)際采用的技術(shù)和要實(shí)現(xiàn)的功能對(duì)DDD的四層架構(gòu)進(jìn)行了細(xì)化和實(shí)現(xiàn):
整個(gè)平臺(tái)采用了JavaEE的技術(shù)及其相關(guān)的開源框架。系統(tǒng)的核心業(yè)務(wù)邏輯由Domain層處理,其中的業(yè)務(wù)服務(wù)(BusinessService)負(fù)責(zé)處理某個(gè)相對(duì)內(nèi)聚的業(yè)務(wù)邏輯單元,同時(shí)對(duì)內(nèi)對(duì)外提供本地或遠(yuǎn)程的服務(wù)。
下面是對(duì)各層的簡(jiǎn)要描述:
另外,我們引入了Spring的IOC容器,系統(tǒng)的控制層、領(lǐng)域?qū)雍统志没瘜釉囟加蠭OC容器統(tǒng)一管理,實(shí)現(xiàn)完全的接口分離和解耦。同時(shí)在控制、領(lǐng)域和持久化層都可以引用日志服務(wù)。
我們對(duì)領(lǐng)域驅(qū)動(dòng)要素的定義上和原有的命名和含義上稍有區(qū)別。
原來的服務(wù)(Service),我們定義為業(yè)務(wù)服務(wù)(BusinessService),面向業(yè)務(wù)服務(wù)的架構(gòu)是GAP平臺(tái)的核心設(shè)計(jì)思想,一個(gè)業(yè)務(wù)服務(wù)可以由一個(gè)或多個(gè)領(lǐng)域模型和數(shù)據(jù)訪問對(duì)象(DAO)組成,去實(shí)現(xiàn)一個(gè)完整的業(yè)務(wù)邏輯單元。業(yè)務(wù)服務(wù)主要負(fù)責(zé)事務(wù)處理和維護(hù)各個(gè)領(lǐng)域?qū)ο笾g的關(guān)系,同時(shí)為上層訪問提供本地和遠(yuǎn)程服務(wù),服務(wù)類型包括Web Service,RMI等。
領(lǐng)域?qū)ο笥蓪?shí)體(Entity)和值對(duì)象(VO)構(gòu)成,實(shí)體類具備自己的屬性和行為、狀態(tài),可以聚合VO,實(shí)體類之間可以有聚合關(guān)聯(lián)等關(guān)系,可以由數(shù)據(jù)訪問對(duì)象(DAO)進(jìn)行持久化。
持久化由數(shù)據(jù)訪問對(duì)象(DAO)實(shí)現(xiàn),不處理業(yè)務(wù)邏輯,主要負(fù)責(zé)實(shí)體類的持久化。提供多種持久化方式(O/R Mapping和JDBC)。
那么如何在去實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)呢?我們總結(jié)了以下四個(gè)步驟:
為了更好的理解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),我們基于以上設(shè)計(jì)方法,實(shí)現(xiàn)了一套簡(jiǎn)單的網(wǎng)上書店系統(tǒng)。
網(wǎng)上書店系統(tǒng)是采用DDD設(shè)計(jì)思想構(gòu)建的一個(gè)應(yīng)用系統(tǒng)示例。通過網(wǎng)上書店系統(tǒng),可以快速理解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。該系統(tǒng)實(shí)現(xiàn)網(wǎng)上書店的常用功能:包括瀏覽書籍、挑選書籍、提交訂單、查看訂單、自動(dòng)折扣、處理訂單、取消訂單等。未登錄用戶可以瀏覽和挑選書籍;已登錄用戶可以提交和查看自己相關(guān)的訂單;管理員可以處理訂單。
經(jīng)過業(yè)務(wù)抽象,即使是這樣一個(gè)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景也包含了很多領(lǐng)域?qū)ο螅缬唵?、賬戶、書籍、購(gòu)物車、購(gòu)物項(xiàng)、折扣等,通過分析和設(shè)計(jì),我們可以得到這樣的設(shè)計(jì)圖(為了查看方便,圖中的類隱藏了屬性信息):
BookStoreAction負(fù)責(zé)處理展現(xiàn)層的請(qǐng)求,并把請(qǐng)求轉(zhuǎn)發(fā)給業(yè)務(wù)服務(wù)IBookStoreBS,業(yè)務(wù)服務(wù)負(fù)責(zé)調(diào)度上圖中顯示的領(lǐng)域?qū)ο?,處理該?chǎng)景的所有業(yè)務(wù)。
其中領(lǐng)域?qū)ο蠛同F(xiàn)實(shí)業(yè)務(wù)的對(duì)應(yīng)關(guān)系為:
與事務(wù)腳本的編程模式不同,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)不是把業(yè)務(wù)邏輯放在BS(BusinessService)中,而是由具備屬性、行為和狀態(tài)的領(lǐng)域?qū)ο筇幚?。例如Order類,如果是貧血的POJO,那它內(nèi)部只有與數(shù)據(jù)表字段對(duì)應(yīng)的屬性以及getter和setter方法,而在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,則是一個(gè)相對(duì)獨(dú)立的、能夠處理自身關(guān)聯(lián)業(yè)務(wù)的領(lǐng)域?qū)ο?。在本系統(tǒng)中,我們對(duì)Order的描述如下:
訂單的實(shí)現(xiàn)類是gap.template.bookstore.model.Order,類中除了聯(lián)系方式、郵寄地址等基本屬性外,還有以下領(lǐng)域相關(guān)的行為:
通過以上的描述,我們可以看到,Order類基本上覆蓋了現(xiàn)實(shí)世界中訂單這個(gè)業(yè)務(wù)的所有行為和狀態(tài),是相對(duì)內(nèi)聚的,這樣的特性使其復(fù)用性大大增加,即使未來開發(fā)新的模塊,涉及到訂單業(yè)務(wù)的,可以直接復(fù)用Order類。同時(shí)在后期維護(hù)中,如果我想了解訂單的業(yè)務(wù),直接讀Order的代碼就可以了。
從上圖中我們還可以清晰的看到各個(gè)領(lǐng)域?qū)ο笾g的關(guān)系。Order和Cart都聚合了Item,對(duì)應(yīng)都是1...n,Item聚合了Book,對(duì)應(yīng)關(guān)系1...1。Order分別與折扣、賬戶發(fā)生關(guān)聯(lián)和調(diào)用等等,整個(gè)網(wǎng)上書店的場(chǎng)景就這樣描述出來了。
另外,不要忘了BS,除了起到基礎(chǔ)設(shè)施的作用外(事務(wù)管理和服務(wù)共享),它還要負(fù)責(zé)調(diào)度和維護(hù)領(lǐng)域?qū)ο笾g的關(guān)系。因?yàn)榭倳?huì)有些業(yè)務(wù)邏輯,既不屬于這個(gè)領(lǐng)域?qū)ο螅膊粚儆谀莻€(gè),那這部分業(yè)務(wù)由誰來處理呢?由BS來處理。例如在管理員處理訂單這個(gè)場(chǎng)景中,首先需要根據(jù)訂單信息獲取賬戶,根據(jù)賬戶信息確定折扣率,同時(shí)進(jìn)行余額校驗(yàn),如果校驗(yàn)通過,就會(huì)調(diào)用訂單對(duì)象的dispose方法處理訂單,這個(gè)場(chǎng)景會(huì)涉及到Order、Account、Discount等對(duì)象,這樣的業(yè)務(wù)邏輯,應(yīng)該由BS實(shí)現(xiàn)。
IBookStoreDao是數(shù)據(jù)訪問對(duì)象,可以被BS調(diào)用,用來持久化對(duì)象,也可以被領(lǐng)域?qū)ο笠?,用來持久化自身?/p>
通過以上的描述,我們可以看到,整個(gè)設(shè)計(jì)和實(shí)現(xiàn)是優(yōu)雅、清晰的。業(yè)務(wù)邏輯沒有堆積在BS中,而是分散在BS和各個(gè)領(lǐng)域?qū)ο笾校?wù)和對(duì)象都與現(xiàn)實(shí)世界的業(yè)務(wù)息息相關(guān),無論是對(duì)領(lǐng)域?qū)<?、開發(fā)人員和后期維護(hù)人員,都能這種方式中獲得自己需要的內(nèi)容。
我們采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)相對(duì)比較早,就我個(gè)人的檢驗(yàn)和實(shí)踐而言,DDD對(duì)構(gòu)建企業(yè)級(jí)應(yīng)用開發(fā)平臺(tái)和大型核心業(yè)務(wù)系統(tǒng)的作用是非常明顯的,無論是在產(chǎn)品的穩(wěn)定性、擴(kuò)展性、可維護(hù)性、生命周期等方面都有顯著的提升。
但是,由于這樣那樣的原因(復(fù)雜度、工期、開發(fā)人員能力限制等等),很多人會(huì)不自覺的抵制采用DDD,有時(shí)候一個(gè)軟件項(xiàng)目重寫了兩次,第二次依然不去做良好的設(shè)計(jì)。事實(shí)上采用了DDD的設(shè)計(jì)方法,我們的設(shè)計(jì)階段已經(jīng)變得非常輕量級(jí)和敏捷了,開發(fā)人員只要能夠把領(lǐng)域模型之間的關(guān)系畫出來并描述說明,并與需求人員達(dá)成一致,那么做出來的東西基本上是靠譜的。
在技術(shù)領(lǐng)域,只有主動(dòng)的嘗試和提升,效果才是最明顯的。很多人問過我,如何開始學(xué)習(xí)和實(shí)踐XXX,其實(shí)很簡(jiǎn)單,現(xiàn)在就開始吧!
上一篇 向上管理:管理自己的老板