黃光芳, 金義富
(湛江師范學院 a.信息與教育技術(shù)中心;b.科技處,廣東湛江524048)
現(xiàn)代信息化高速發(fā)展的今天,越來越多的應(yīng)用系統(tǒng)都被構(gòu)建在Web之上,關(guān)于它的開發(fā)也經(jīng)歷了面向過程、面向?qū)ο蟆⒚嫦蚍?wù)架構(gòu)(SOA)等開發(fā)過程,其復(fù)雜性也越來越高,使用的技術(shù)平臺有Java、.NET、Ruby等,目前基于Web的多層架構(gòu)體系(如 J2EE、ASP.NET)已經(jīng)成為解決企業(yè)級應(yīng)用的主要途徑。因此,如何在軟件中更好地處理業(yè)務(wù)邏輯,且高質(zhì)量高效率地完成軟件的開發(fā)便成為人們?nèi)找嬷匾暤膯栴}。但是長期以來,傳統(tǒng)的Web平臺開發(fā)工作趨向于一種以技術(shù)為先導的開發(fā)方式,開發(fā)的過程即先從業(yè)務(wù)方面分析企業(yè)需求,然后把需求傳達給開發(fā)團隊,開發(fā)人員再依據(jù)需求的描述創(chuàng)造出最有可能的設(shè)想進行開發(fā)[1]。這些軟件開發(fā)的指導原則依然是基于數(shù)據(jù)庫設(shè)計而非面向?qū)ο笤O(shè)計,即開發(fā)人員一開始便根據(jù)需求建立數(shù)據(jù)庫模型,系統(tǒng)中的業(yè)務(wù)對象被機械化的數(shù)據(jù)庫CRUD操作代替,忽略面向?qū)ο蟮拈_發(fā)思想,缺少領(lǐng)域模型的開發(fā),業(yè)務(wù)邏輯設(shè)計混亂,不能及時有效地反映用戶需求,開發(fā)的系統(tǒng)缺少通用性和科學性等。針對以上開發(fā)方法的不足,文中將領(lǐng)域驅(qū)動設(shè)計的開發(fā)思想融入到業(yè)務(wù)邏輯復(fù)雜的Web平臺的構(gòu)建中,力求尋找一種更佳的企業(yè)級Web平臺的開發(fā)方案。
領(lǐng)域驅(qū)動設(shè)計(Domain-Driven Design,DDD)是領(lǐng)域驅(qū)動設(shè)計大師Eric Evans在2004年發(fā)表的文獻[1]中提出的軟件開發(fā)概念,是一種基于模型驅(qū)動開發(fā)(MDD)思想的嶄新的開發(fā)方式,目的是讓軟件系統(tǒng)在實現(xiàn)時準確的基于對真實業(yè)務(wù)過程的建模并根據(jù)真實業(yè)務(wù)過程的調(diào)整而調(diào)整。
領(lǐng)域驅(qū)動設(shè)計很好地遵循了關(guān)注點分離的原則,提出了成熟、清晰的分層架構(gòu),對領(lǐng)域?qū)ο筮M行了明確的策略和職責劃分,讓領(lǐng)域?qū)ο蠛同F(xiàn)實世界中的業(yè)務(wù)形成良好的映射關(guān)系,為領(lǐng)域?qū)<遗c開發(fā)人員搭建了溝通的橋梁。領(lǐng)域模型分為用戶界面層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)結(jié)構(gòu)層四層,如圖1所示。
圖1 領(lǐng)域驅(qū)動設(shè)計的分層架構(gòu)
用戶界面層。主要負責向用戶呈現(xiàn)信息、接受并解釋用戶命令,并把用戶的請求發(fā)送到應(yīng)用層或領(lǐng)域?qū)印?/p>
應(yīng)用層。定義了系統(tǒng)要完成的工作,不包含業(yè)務(wù)邏輯的實現(xiàn),只保留任務(wù)的進度狀態(tài)。
領(lǐng)域?qū)?。系統(tǒng)的核心,負責系統(tǒng)業(yè)務(wù)邏輯的實現(xiàn)工作,包含領(lǐng)域行為和模型。
基礎(chǔ)結(jié)構(gòu)層。為上層提供通用的技術(shù)能力,持久化業(yè)務(wù)對象以及實現(xiàn)應(yīng)用層的管理等。
領(lǐng)域驅(qū)動設(shè)計除了對系統(tǒng)架構(gòu)進行了分層描述外,還對對象(Object)做了明確的職責和策略劃分,劃分的對象有實體、值對象、工廠、倉儲、服務(wù)、聚合等。
實體(Entities)。具備唯一ID,能夠被持久化,具備業(yè)務(wù)邏輯,對應(yīng)現(xiàn)實世界業(yè)務(wù)對象。
值對象(Value objects)。不具有唯一ID,由對象的屬性描述,一般為內(nèi)存中的臨時對象,可以用來傳遞參數(shù)或?qū)嶓w進行補充描述。
工廠(Factories)。主要用來創(chuàng)建實體,目前架構(gòu)實踐中一般采用IOC容器來實現(xiàn)工廠的功能。
倉儲(Repositories)。用來管理實體的集合,封裝持久化框架。
服務(wù)(Services)。為上層建筑提供可操作的接口,負責對領(lǐng)域?qū)ο筮M行調(diào)度和封裝,同時可以對外提供各種形式的服務(wù)。
聚合(Aggregate)。主要將復(fù)雜領(lǐng)域中關(guān)系密切的多個實體的合并在一起,以降低領(lǐng)域的復(fù)雜性。聚合內(nèi)實體可以相互引用,兩個聚合之間的實體必須通過聚合根才能引用。
領(lǐng)域驅(qū)動設(shè)計的專注點在于領(lǐng)域模型的研究,因為在領(lǐng)域驅(qū)動設(shè)計中,它是以模型驅(qū)動設(shè)計為根基,以軟件領(lǐng)域為著眼點,專注于領(lǐng)域模型的構(gòu)建與代碼匹配,并將模型作為領(lǐng)域?qū)<液蛙浖_發(fā)人員交流的一種開發(fā)方式[3]。相對于以往的數(shù)據(jù)庫驅(qū)動的設(shè)計方式,這種新開發(fā)方式具有以下優(yōu)勢:
(1)復(fù)用。在領(lǐng)域驅(qū)動設(shè)計中,領(lǐng)域?qū)ο笫呛诵模總€領(lǐng)域?qū)ο蠖际且粋€相對完整的內(nèi)聚的業(yè)務(wù)對象描述,所以可以形成直接的復(fù)用。同時設(shè)計過程是基于領(lǐng)域?qū)ο蠖皇腔跀?shù)據(jù)庫的Schema,所以整個設(shè)計也是可以復(fù)用。
(2)注重實踐。專注于具體場景的應(yīng)用,領(lǐng)域?qū)<摇㈤_發(fā)人員及用戶使用模型元素之間的交互來理清系統(tǒng)中的業(yè)務(wù)邏輯,且按模型允許方式將各種概念結(jié)合在一起,然后將這些應(yīng)用到圖和代碼中,消除了開發(fā)中思想的隔膜,保證領(lǐng)域模型與系統(tǒng)業(yè)務(wù)相對應(yīng)。
(3)重構(gòu)。領(lǐng)域驅(qū)動設(shè)計采用面對對象的設(shè)計,使領(lǐng)域模型在設(shè)計中隨時響應(yīng)用戶提出新的軟件需求,根據(jù)業(yè)務(wù)邏輯向更深層次重構(gòu)。
這里以一個業(yè)務(wù)邏輯稍為簡單的網(wǎng)上書店的電子商務(wù)平臺來闡述領(lǐng)域驅(qū)動設(shè)計在Web平臺中應(yīng)用情況。該系統(tǒng)實現(xiàn)網(wǎng)上書店的常用功能:包括瀏覽書籍、挑選書籍、提交訂單、查看訂單、自動折扣、處理訂單、取消訂單等。未登錄用戶可以瀏覽和挑選書籍;已登錄用戶可以提交和查看自己相關(guān)的訂單;管理員可以處理訂單。結(jié)合書店的業(yè)務(wù)場景,抽象出以下一些領(lǐng)域?qū)ο?,如訂單、賬戶、書籍、購物車、購物項、折扣等,現(xiàn)實業(yè)務(wù)和領(lǐng)域?qū)ο蟮膶?yīng)關(guān)系為:訂單—Order,賬戶—Account,書籍—Book,購物車—Cart,購物項—Item,折扣—Discount。通過對場景及業(yè)務(wù)邏輯的分析和設(shè)計,得到的領(lǐng)域模型如圖2所示。
圖2 網(wǎng)上書店業(yè)務(wù)邏輯圖
在圖2中,首先BookStoreAction負責處理表示層的請求,并把請求轉(zhuǎn)發(fā)給業(yè)務(wù)服務(wù)IBookStoreBS,業(yè)務(wù)服務(wù)負責調(diào)度上圖中顯示的領(lǐng)域?qū)ο?,處理該場景的所有業(yè)務(wù)。從圖中我們還可以清晰地看到各個領(lǐng)域?qū)ο笾g的關(guān)系。Order和Cart都聚合了Item,都是聚合根,對應(yīng)都是1…n,Item聚合了Book,Item是一個聚合根,Book是一個實體,對應(yīng)關(guān)系1…n,Order分別與折扣、賬戶發(fā)生關(guān)聯(lián)和調(diào)用等,整個網(wǎng)上書店的場景就這樣描述出來了。
與事務(wù)腳本的編程模式不同,領(lǐng)域驅(qū)動設(shè)計不是把業(yè)務(wù)邏輯放在業(yè)務(wù)服務(wù)(Business Service)層中,而是由具備屬性、行為和狀態(tài)的領(lǐng)域?qū)ο筇幚?。例如Order類,如果是貧血的POJO,那它內(nèi)部只有與數(shù)據(jù)表字段對應(yīng)的屬性以及getter和setter方法,而在領(lǐng)域驅(qū)動設(shè)計中,則是一個相對獨立的、能夠處理自身關(guān)聯(lián)業(yè)務(wù)的領(lǐng)域?qū)ο?。如在本系統(tǒng)中,訂單類中除了聯(lián)系方式、郵寄地址等基本屬性外,還有以下領(lǐng)域相關(guān)的行為:
(1)init(·),結(jié)算時調(diào)用方法,根據(jù)當前用戶與購物車中的Items初始化訂單,供用戶修改。
(2)submit(·),提交訂單時調(diào)用的方法,保存訂單。
(3)cancel(·),取消訂單,把訂單和相關(guān)item的狀態(tài)設(shè)置為“已取消”,然后委托基礎(chǔ)結(jié)構(gòu)層進行持久化。
(4)dispose(·),處理訂單,首先更新訂單項的狀態(tài),然后委托基礎(chǔ)結(jié)構(gòu)層持久化訂單數(shù)據(jù)。
……
通過以上的描述,我們可以看到,Order類基本上覆蓋了現(xiàn)實世界中訂單這個業(yè)務(wù)的所有行為和狀態(tài),是相對內(nèi)聚的,這樣的特性使其復(fù)用性大大增加,即使未來開發(fā)新的模塊,涉及到訂單業(yè)務(wù)的,可以直接復(fù)用Order類,同時在后期維護中,如果想了解訂單的業(yè)務(wù),直接讀Order的代碼就可以了。
在項目開發(fā)中,良好的框架設(shè)計可以有效地提高工作效率,縮短開發(fā)時間、降低開發(fā)成本,增強程序的可維護性和可擴展性。根據(jù)領(lǐng)域模型的特點,在具體的項目開發(fā)中,運用分層架構(gòu)和.NET提供的實體框架[4],對一些相關(guān)的類和框架進行抽象設(shè)計,設(shè)計了一些通用模塊,如層超類、接口、倉儲框架、倉儲工廠、倉儲基類和工作單元等,為將來構(gòu)建一個大型的網(wǎng)上書店的設(shè)計與實現(xiàn)打下基礎(chǔ),而且,項目的一些通用模塊與具體的實現(xiàn)細節(jié)無關(guān),即可以直接應(yīng)用到其他系統(tǒng)的設(shè)計中,提高開發(fā)效率。
當某一層中所有的對象都具有某些方法,同時為了避免這些方法在系統(tǒng)內(nèi)被多次復(fù)制而產(chǎn)生冗余時,便將這些行為移到一個通用的類中,這個類就是層超類型(Layer Supertype),然后其他接口都被重構(gòu)[6]到這一超類中。在網(wǎng)上書店的系統(tǒng)框架中,設(shè)計一個EntityBase的抽象類作為基礎(chǔ)結(jié)構(gòu)層的層超類型,領(lǐng)域模型中的所有實體類都繼承它的標識Key。
層超類的分別包含一個缺省的構(gòu)造器和一個重載構(gòu)造器,重載構(gòu)造器允許傳入一個只讀屬性的Key,考慮到不同的實體標識類型不一致,所以這里的Key的類型是System.Object,體現(xiàn)了超類設(shè)計的靈活性。
倉儲是領(lǐng)域?qū)优c基礎(chǔ)結(jié)構(gòu)層的一個銜接組件,領(lǐng)域?qū)油ㄟ^倉儲訪問外部存儲機制,這樣就使得領(lǐng)域?qū)訜o需關(guān)心任何技術(shù)架構(gòu)上的實現(xiàn)細節(jié)。因此,倉儲這個角色的職責不僅僅是讀取、保存、查詢、刪除,它還解耦了領(lǐng)域?qū)优c基礎(chǔ)結(jié)構(gòu)層,將繁雜的數(shù)據(jù)庫操作從領(lǐng)域?qū)又薪夥懦鰜恚归_發(fā)人員更好地專注于領(lǐng)域?qū)拥脑O(shè)計。在實踐中,可以使用依賴注入[7]的方式,將倉儲實例注入到領(lǐng)域?qū)樱瑥亩@得靈活的體系結(jié)構(gòu),如圖3所示。
在本系統(tǒng)中,IRepository(倉儲接口)是一個泛型接 口[8],泛 型 類 型 被 where 子 句 限 定 為EntityFramework中的EntityBase,該接口使用泛型,方便被其他特定的聚合類型的倉儲繼承后使用,定義方式見如下代碼所示:
為了消除大量重復(fù)的代碼,倉儲框架將在倉儲接口的基礎(chǔ)上添加了抽象的倉儲基類RepositoryBase,該基類實現(xiàn)了IRepository<T>,從而方便系統(tǒng)中具體倉儲類的重構(gòu)。SqlRepositoryBase<T>基類實現(xiàn)了RepositoryBase<T>類,是專門針對SQL Server數(shù)據(jù)庫讀取和寫入數(shù)據(jù)的通用的基類,它可以減少大量具體倉儲中的重復(fù)代碼。具體如圖4所示。
圖3 基礎(chǔ)結(jié)構(gòu)層將倉儲實現(xiàn)注入領(lǐng)域?qū)?/p>
圖4 倉儲框架的構(gòu)建模式
在具體業(yè)務(wù)的實現(xiàn)中,倉儲作為領(lǐng)域模型的一部分,領(lǐng)域模型依賴于倉儲的抽象,倉儲提供一套方法將聚合從持久層中提取出來,包括一系列的查詢,保存根的方法,以及從根開始訪問聚合內(nèi)其他領(lǐng)域模型的方法。如在Order(訂單)聚合根中,具體實現(xiàn)倉儲為Order,該倉儲繼承了 IOrder接口和 SqlRepositoryBase抽象類,SqlRepositoryBase繼承了倉儲框架中的RepositoryBase類。為了方便倉儲類重構(gòu),系統(tǒng)在初始的框架RepositoryBase基類中添加了一些常用的方法如 Add、Edit、Delete、FindBy、FindAll 等,然 在SqlRepositoryBase基類中覆蓋該方法,于是在具體的倉儲實現(xiàn)類Order中便可以直接調(diào)用這些方法而不需要再次實現(xiàn),當然,如果實現(xiàn)的功能稍有差導,也可以重寫該方法。同理,其他實現(xiàn)類如Account(賬戶),Book(書籍),Cart(購物車)都可以像Order類那樣創(chuàng)建倉儲并繼承倉儲基類和層超類,這樣整個系統(tǒng)便提高了代碼利用率,同時也方便系統(tǒng)向更深層次模型重構(gòu),以獲取更合理的實現(xiàn)方式,確保系統(tǒng)在開發(fā)中有更好的可維護性和可擴展性。
工廠模式[9](Factory)實際就是在處理復(fù)雜的對象創(chuàng)建時,將對象創(chuàng)建的職責交給第三方來完成,從而降低對象創(chuàng)建的復(fù)雜度,增加系統(tǒng)的可靠性。這里本系統(tǒng)使用分離接口模式(Separated Interface)創(chuàng)建倉儲工廠實例。在基礎(chǔ)結(jié)構(gòu)層中共設(shè)計了四個配置類來創(chuàng)建倉儲的映射。通過讀取配置節(jié)設(shè)置,并復(fù)制到一個良好的對象模型中,倉儲工廠類便可以利用這個對象模型創(chuàng)建倉儲。工廠類首先使用反射[10]來取得接口類型的名稱,然后查找基于映射配置中的將要創(chuàng)建的倉儲類型,如沒有,則使用Activator對象的反射能力創(chuàng)建一個正確的倉儲工廠實例,并將其放到靜態(tài)詞典中以供下次檢索,從而避免系統(tǒng)頻繁構(gòu)建倉儲,節(jié)省系統(tǒng)開銷。
Unit of Work(UoW)模式在企業(yè)應(yīng)用架構(gòu)中被廣泛使用,它能夠?qū)㈩I(lǐng)域模型中對象狀態(tài)的變化收集起來,并在適當?shù)臅r候在同一數(shù)據(jù)庫連接和事務(wù)處理上下文中一次性將對象的變更提交到數(shù)據(jù)庫中,減少與數(shù)據(jù)庫交互次數(shù),提高系統(tǒng)性能[11]。在系統(tǒng)基礎(chǔ)結(jié)構(gòu)層中設(shè)計了一個工作單元接口IUoW,用于標識已經(jīng)添加、更改或移除的實體,UoW類實現(xiàn)IUoW接口,UoW類分別遍歷所有刪除、添加和更改的注冊信息,整個操作被包裝進一個事務(wù),僅調(diào)用接口的Commit方法就可以將所有的更改提交到數(shù)據(jù)庫中,保證了數(shù)據(jù)的一致性。UoW類引用了IUnitOfWorkRespository接口,這個接口被倉儲中的RepositoryBase基類實現(xiàn),于是工作單元的實現(xiàn)便回調(diào)到倉儲里實現(xiàn)。
應(yīng)用服務(wù)層位于分層架構(gòu)中的第二層,它不負責處理任何業(yè)務(wù)邏輯,主要是協(xié)調(diào)其他層的數(shù)據(jù)傳輸、工廠調(diào)用或?qū)ο蟀l(fā)送等,為業(yè)務(wù)邏輯的正確執(zhí)行提供適當?shù)倪\行環(huán)境。在應(yīng)用服務(wù)層,采用了微軟的窗口通信基礎(chǔ)[12](Windows Communication Foundation,WCF)技術(shù)為系統(tǒng)的上下層提供數(shù)據(jù)通信。WCF使用SOAP通信機制[13],保證了系統(tǒng)之間的互操作性,即使是運行不同開發(fā)語言,也可以跨進程、跨機器甚至于跨平臺的通信,同時可以提供高效且安全性的訪問。WCF在數(shù)據(jù)傳輸中使用數(shù)據(jù)合約(Data Contract)來訂定雙方溝通時的數(shù)據(jù)格式,如涉及到訂單處理的部分,應(yīng)用層僅僅是協(xié)調(diào)倉儲操作和事務(wù)處理,業(yè)務(wù)邏輯由Order的倉儲方法實現(xiàn)。對于部分不屬于單獨的對象、不能輕易地合并到某個實體和值對象的操作或者涉及到幾個場景實體的業(yè)務(wù)邏輯,這種行為一般放在服務(wù)層,聲明為服務(wù),設(shè)計好后供表示層直接調(diào)用,簡化了對領(lǐng)域模型的設(shè)計,使領(lǐng)域模型更純凈。如在管理員處理訂單這個場景中,首先需要根據(jù)訂單信息獲取賬戶,根據(jù)賬戶信息確定折扣率,同時進行余額校驗,如果校驗通過,就會調(diào)用訂單對象的dispose方法處理訂單,這個場景會涉及到 Order、Account、Discount等對象,這樣的業(yè)務(wù)邏輯,則聲明為一種服務(wù),在應(yīng)用層實現(xiàn)。
對于領(lǐng)域驅(qū)動設(shè)計,最核心的就是如何解決復(fù)雜業(yè)務(wù)的設(shè)計問題,如何抓住業(yè)務(wù)邏輯的本質(zhì),并轉(zhuǎn)換成業(yè)務(wù)邏輯模型。領(lǐng)域驅(qū)動設(shè)計良好的支撐框架和富有彈性的需求分析過程,已經(jīng)得到許多企業(yè)的認可,并且它不依附于哪一個特定的平臺,這就為廣大開發(fā)者提供更有彈性的開發(fā)空間,更有利領(lǐng)域驅(qū)動設(shè)計的思想融入各個領(lǐng)域Web平臺中,加快這方面的研究和應(yīng)用。目前已經(jīng)有許多開發(fā)人員嘗試著應(yīng)用到石油、航海[14]、物流[15]及信息系統(tǒng)等項目中,隨著軟件市場的進一步成熟和客戶需求的不斷提高,相信這是一個切實可行且具有很好應(yīng)用前景的開發(fā)方法。
[1] 宋 波,趙永翼,張 悅,等.一種規(guī)范Web開發(fā)框架的研究與實現(xiàn)[J].微電子學與計算機,2007(7):201-208.
[2] Eric Evan.領(lǐng)域驅(qū)動設(shè)計-軟件核心復(fù)雜性應(yīng)對之道[M].陳大峰,張澤鑫譯.北京:清華大學出版社,2006.
[3] 嚴欣品.領(lǐng)域驅(qū)動設(shè)計方法的研究及其應(yīng)用[D].南昌:南昌大學,2010.
[4] 雷 蕾,陸新泉,李 睿,等.應(yīng)用_NET框架命名空間技術(shù)實現(xiàn)Web測試自動化[J].計算機應(yīng)用研究,2010,27(6):.
[5] Tim Macarthy.領(lǐng)域驅(qū)動設(shè)計C#2008實現(xiàn)[M].UMLChina譯.北京:清華大學出版社,2010.
[6] 科瑞夫斯蓋.重構(gòu)與模式[M].楊 光,劉基誠譯.北京:人民郵電出版社,2006.
[7] 張 浩.利用反向控制原則和依賴注入的可復(fù)用框架設(shè)計解耦方法[J].計算機應(yīng)用,2010,30(12):227-229.
[8] 陳葉旺,余金山.泛型編程與設(shè)計模式[J].計算機科學,2006,33(4):253-257.
[9] 彭世康,周逢權(quán).新的設(shè)計模式——數(shù)組工廠和數(shù)組原型模式[J].計算機應(yīng)用,2012,32(S2):107-112.
[10] 吳東慶,胡小健,楊逢建.反射機制下類工廠模式的實現(xiàn)與研究[J].計算機應(yīng)用,2006,26(3):705-707.
[11] Martin Fowler.企業(yè)應(yīng)用架構(gòu)模式[M].王懷民,周斌譯.北京:機械工作出版社,2004.
[12] 劉黎志,吳云韜.應(yīng)用WCF分布式框架實現(xiàn)移動數(shù)據(jù)同步[J].計算機應(yīng)用,2011,12(31):3281-3284.
[13] 劉嘉?。赟OA架構(gòu)的ERP與電子商務(wù)系統(tǒng)研究[J].企業(yè)經(jīng)濟,2011(5):88-90.
[14] 張金松.領(lǐng)域驅(qū)動設(shè)計在航務(wù)海事系統(tǒng)中的應(yīng)用研究[D].大連:大連海事大學,2010.
[15] 丁 濤.基于領(lǐng)域驅(qū)動設(shè)計的物流平臺系統(tǒng)實現(xiàn)[D].成都:電子科技大學,2010.