摘 要: 領(lǐng)域驅(qū)動開發(fā)(DDD)是完全基于內(nèi)存的業(yè)務(wù)對象建模(In?Memory)方法,而目前的DDD框架不能完全覆蓋系統(tǒng)業(yè)務(wù),也不成熟穩(wěn)定,無法應(yīng)用于實(shí)際項(xiàng)目的開發(fā)。在Spring框架的基礎(chǔ)上,實(shí)現(xiàn)了一個基于領(lǐng)域消息驅(qū)動和內(nèi)存建模的DDD插件,在不影響目前系統(tǒng)架構(gòu)的基礎(chǔ)上,讓項(xiàng)目實(shí)施完全兼容DDD設(shè)計。同時基于JDK和Disruptor并發(fā)框架實(shí)現(xiàn)的領(lǐng)域事件消息和領(lǐng)域?qū)ο缶彺婺P?,有效地降低了系統(tǒng)的解耦關(guān)系,并提高了系統(tǒng)的整體性能及效率。
關(guān)鍵詞: 領(lǐng)域消息驅(qū)動; 內(nèi)存建模; 領(lǐng)域事件; 領(lǐng)域緩存
中圖分類號: TN911?34; TP319 文獻(xiàn)標(biāo)識碼: A 文章編號: 1004?373X(2015)24?0062?04
Design and implementation of light?weighted Spring?DDD plug?in driven by
domain message
WANG Wei1, HUANG Qiang2, 3
(1. Department of Electronic Business, Sichuan Finance and Economics Vocational College, Chengdu 610101, China;
2. College of transportation, Southwest Jiaotong University, Chengdu 610031, China;
3. College of Information and Engineering, Sichuan Agricultural University, Ya’an 625014, China)
Abstract: DDD (domain driven development) is entirely memory?based business object modeling (In?Memory) method, but the current DDD framework is not stable enough to cover the system business completely and can not be applied to the development of practical project. On the basis of Spring framework, the DDD plug?in based on domain message driving and memory modeling was realized, in which the implementation of the project is fully compatible with DDD design without affecting the current system architecture. The domain event message and domain object caching model based on JDK and Disruptor concurrency control framework can effectively reduce the decoupling relationship and improve the whole performance and efficiency of the system.
Keywords: domain message?driven; memory modeling; domain event; domain cache
0 引 言
領(lǐng)域建模(Domain Modeling,DM)的初期是基于對象關(guān)系映射的ORM(Object Relationship Mapping)技術(shù)。ORM通過數(shù)據(jù)庫與對象的數(shù)據(jù)映射,初步解決了關(guān)系與對象的不匹配問題[1],使系統(tǒng)設(shè)計分析人員能夠采用純粹的對象技術(shù)來解決領(lǐng)域問題。但是隨著業(yè)務(wù)分析的深入,ORM產(chǎn)生的貧血對象模型由于行為能力的缺失,讓系統(tǒng)又從對象模型退化為事務(wù)處理過程,與DM理論逐漸背離;隨后為了避免貧血模型的弊端而出現(xiàn)的充血模型,將業(yè)務(wù)與數(shù)據(jù)全部合并到領(lǐng)域模型中。在后來的幾年中,有很多框架都在充血模型的理論基礎(chǔ)上進(jìn)行了大量實(shí)踐(如ROR,Grails,Spring Roo等),這些快速開發(fā)框架出現(xiàn)的初期在小型項(xiàng)目上應(yīng)用非常成功。但是隨著業(yè)務(wù)增長,領(lǐng)域?qū)ο髸彼倥蛎?,維護(hù)難度會急劇增加,讓系統(tǒng)處于一個不可控的狀態(tài),并且業(yè)務(wù)和數(shù)據(jù)的領(lǐng)域整合會讓系統(tǒng)的結(jié)構(gòu)變得模糊不清,給系統(tǒng)帶來另外的壓力,所以這類框架始終沒有能成功地應(yīng)用到大型項(xiàng)目開發(fā)中去。在2004年,著名的軟件工程專家Evans提出了一個全新的領(lǐng)域開發(fā)模型Evans DDD[2]。Evans DDD不但彌補(bǔ)了ORM的對象行為缺失和生命周期問題,也通過領(lǐng)域聚合和分解有效地解決了充血模型隨著領(lǐng)域擴(kuò)大而迅速臃腫的缺陷,為大型系統(tǒng)設(shè)計和開發(fā)提供了一個合理的解決途徑。但是由于Evans DDD的設(shè)計理念比較靈活,依附于業(yè)務(wù)設(shè)計,且始終沒有一個統(tǒng)一的底層技術(shù)構(gòu)架來支持該模型,導(dǎo)致系統(tǒng)開發(fā)人員很難將系統(tǒng)分析轉(zhuǎn)換為編碼,故大大降低了系統(tǒng)的實(shí)施效率。同時現(xiàn)有的框架體系基本是以數(shù)據(jù)庫為核心的分層結(jié)構(gòu),也為DDD的實(shí)踐造成了很大的障礙。一些全新的框架由于不能很好地兼容以前的遺留系統(tǒng),也很難得到推廣。為此,本文以Java為例,在充分利用目前廣泛使用的Spring開發(fā)框架基礎(chǔ)上,提出了一種基于AOP和JDK Concurrent/Disruptor并發(fā)框架實(shí)現(xiàn)的領(lǐng)域事件消息和領(lǐng)域?qū)ο缶彺婺P?,并完成了能夠較好兼容Evans DDD的整套領(lǐng)域驅(qū)動開發(fā)插件Takia。在多個項(xiàng)目中的實(shí)施證明,該插件在保證系統(tǒng)開發(fā)效率的前提下,能夠有效匹配基于DDD的設(shè)計方案,消息模型使領(lǐng)域?qū)ο笾g以及領(lǐng)域?qū)ο笈c服務(wù)倉儲等組件之間的交互變得更加靈活。配合領(lǐng)域緩存,可以完全消除領(lǐng)域?qū)ο髮?shù)據(jù)庫的依賴,能夠很好地為系統(tǒng)構(gòu)架師所利用,以權(quán)衡系統(tǒng)整體的得失做出全局性的決策,讓系統(tǒng)的綜合性能達(dá)到最高,為基于DDD的項(xiàng)目分析和實(shí)施提供有效的架構(gòu)指導(dǎo)。
1 基于消息的輕量級Spring?DDD領(lǐng)域插件模型
Takia構(gòu)架設(shè)計
本文提出的異步領(lǐng)域消息模型能夠更好地適應(yīng)領(lǐng)域?qū)ο蟮淖饔藐P(guān)系和領(lǐng)域擴(kuò)張問題,更好地支持領(lǐng)域建模。Takia以領(lǐng)域的四要素為基礎(chǔ)[3],采用分布式緩存系統(tǒng)實(shí)現(xiàn)了領(lǐng)域的In?Memory模型,并提出基于異步消息的數(shù)據(jù)通信方式,優(yōu)化了領(lǐng)域?qū)ο蟮纳a(chǎn)和存儲性能,很大程度提高領(lǐng)域?qū)ο笾g的解耦程度,以及領(lǐng)域?qū)ο蟮母卟l(fā)數(shù)據(jù)處理能力,其整體構(gòu)架如圖1所示[ 4?6]。
從圖1中可以看出,該構(gòu)架底層基于Spring業(yè)務(wù)框架,Hibernate ORM框架和數(shù)據(jù)庫管理系統(tǒng),對于常規(guī)模型的開發(fā)人員來說相對比較熟悉,能夠較容易地完成開發(fā)過渡,而Takia插件構(gòu)架于Spring業(yè)務(wù)框架之上用以提供領(lǐng)域建模支持。在系統(tǒng)設(shè)計時,業(yè)務(wù)模塊在被劃分成多個領(lǐng)域進(jìn)行分散式并行開發(fā),一個領(lǐng)域一般由領(lǐng)域?qū)ο螅―omain Object)、值對象(Value Object)、服務(wù)(Usecase Service)和倉儲(Repository)等組件構(gòu)成[7];領(lǐng)域的外圍是Takia提供的透明緩存和消息服務(wù),這些服務(wù)通過基于Annotation的領(lǐng)域模型配置,使用AOP方式注入到相關(guān)的領(lǐng)域?qū)ο笾?,緩存用于提升領(lǐng)域?qū)ο蟮男阅?,消息用于降低系統(tǒng)及模塊之間的直接耦合。Takia在設(shè)計時支持多種緩存實(shí)現(xiàn),默認(rèn)為EhCache以支持集群和云結(jié)構(gòu)。
圖1 Takia DDD的總體構(gòu)架模型
2 Takia的消息模型設(shè)計
并發(fā)的事件驅(qū)動模式是Takia的重要特點(diǎn)。通過DomainMessage作為橋梁來實(shí)現(xiàn)領(lǐng)域組件之間的信息交互,同時采用AOP編程模型,取代了傳統(tǒng)的面向?qū)ο笤O(shè)計(OOSD)中,組件的直接組合和繼承耦合,有效地消除了服務(wù)和倉儲組件對領(lǐng)域模型的結(jié)構(gòu)污染。Takia采用標(biāo)準(zhǔn)的生產(chǎn)者?消費(fèi)者(Producer?Consumer)設(shè)計模式構(gòu)架領(lǐng)域消息模型,并針對虛擬機(jī)內(nèi)部機(jī)制進(jìn)行有效的優(yōu)化,除了支持普通的JDK一對一同步/異步消息以外,還提供了高并發(fā)環(huán)境下的基于Disruptor框架的一對多異步消息支持,有效地解決了傳統(tǒng)JavaEE分層構(gòu)架的高并發(fā)環(huán)境下系統(tǒng)吞吐量迅速衰減的問題。下面依次對JDK和Disruptor兩種消息模型結(jié)構(gòu)進(jìn)行分析。
2.1 JDK?Future消息模型設(shè)計
Future消息模型基于JDK的Concurrent包實(shí)現(xiàn),核心組件是ChannelExecutor。該組件由一個線程池(ThreadPool)和一個同步組件組成,用于運(yùn)行消息監(jiān)聽器(MessageListener)。當(dāng)使用異步模式時,由ThreadPool來運(yùn)行MessageListener;使用同步模式時,由同步組件將MessageListener放入當(dāng)前線程中執(zhí)行。Future消息模型的詳細(xì)結(jié)構(gòu)和處理過程如圖2所示。
由圖2可以看出,DomainMessage作為消息生產(chǎn)者DomainObject和消息消費(fèi)者M(jìn)essageListener之間的橋梁用以傳遞信息,由事件源(EventSource)和Future結(jié)果事件(FutureResultEvent)組成,結(jié)果事件中包含了結(jié)果對象,事件元數(shù)據(jù)配置和一個FutureTask對象,F(xiàn)utureTask作為簡單的內(nèi)存障(MemoryBarrier),與ChannelExecutor配合使用用以保證消息的一致性。由于受到Future機(jī)制的限制,在Future模式下只支持1∶1的生產(chǎn)者和消費(fèi)者通信模式。
圖2 Future消息模型的詳細(xì)結(jié)構(gòu)和處理過程
消息過程由DomainObject發(fā)起,通過調(diào)用DomainEvents(消息總線)創(chuàng)建一個DomainMessage對象后,DomainObject將事件源(EventSource)傳遞給DomainMessage(事件源即DomainObject傳遞給MessageListener的信息);接著DomainEvents會通知消息攔截器(MessageInterceptor)對DomainMessage的ResultEvent進(jìn)行初始化,完成消息創(chuàng)建過程;然后MessageInterceptor調(diào)用EventMessageFirer發(fā)送消息,EventMessageFirer將Future消息的發(fā)送委托給FutureFirer,其根據(jù)消息配置從IoC容器中獲取MessageListener組件后和DomainMessage一起放入ChannelExecutor執(zhí)行。
對于同步消息,ChannelExecutor使用同步組件在當(dāng)前線程中運(yùn)行MessageListener,對于異步消息,ChannelExecutor會將MessageListener放入ThreadPool中執(zhí)行。MessageListener可以在業(yè)務(wù)完成以后對DomainMessage設(shè)置結(jié)果對象(EventResult),最后作為消息生產(chǎn)者的DomainObject可以通過DomainMessage的getEventResult方法獲取MessageListener處理結(jié)果,該方法可以被多次調(diào)用,并返回同一結(jié)果。在該模式下,DomainMessage的FutureTask會保證結(jié)果的可用性和一致性。
2.2 Disruptor消息模型設(shè)計
Disruptor消息模型基于并發(fā)編程框架Disruptor實(shí)現(xiàn)[5?6,8],采用全異步模式實(shí)現(xiàn),核心結(jié)構(gòu)由一個輸入?yún)^(qū)域(Input Disruptor)和一個輸出集合(Output Disruptors)組成。Disruptor模式支持1∶N消息模式。Input Disruptor的環(huán)形緩沖區(qū)(RingBuffr)可以同時運(yùn)行多個消息處理器(MessageListener),同時每一個DomainMessage都擁有自己的Output Disruptor,可以獨(dú)立的設(shè)置處理結(jié)果,互不干擾,Disruptor消息模型的詳細(xì)結(jié)構(gòu)和處理過程如圖3所示。圖3在Disruptor模式下DomainMessage由事件源(EventSource)和Disruptor結(jié)果事件(DisruptorResultEvent)組成,結(jié)果事件中包含事件元數(shù)據(jù)配置和一個事件處理器(ValueEventProcessor)對象,事件處理器用于讀/寫結(jié)果輸出區(qū)域的RingBuffer。Disruptor模式下支持1∶N的生產(chǎn)者和消費(fèi)者主體訂閱模式(Topic Subscription)。與Future模式的流程類似。
圖3 Disruptor消息模型的詳細(xì)結(jié)構(gòu)和處理過程
Takia管理RingBuffer的策略是為每一個Topic創(chuàng)建一個Input Disruptor實(shí)例,而為每一個DomainMessage對象創(chuàng)建一個獨(dú)立的Output Disruptor實(shí)例,所以一個Input Disruptor會對應(yīng)多個Output Disruptors,他們之間完全獨(dú)立互不影響。為了在性能和資源占用上取得平衡,Takia默認(rèn)使用Disruptor的Blocking等待策略。
3 基于消息的領(lǐng)域模型實(shí)現(xiàn)
與傳統(tǒng)的分層式構(gòu)架體系不同,Takia采用以領(lǐng)域?yàn)楹诵牡妮椛湫徒Y(jié)構(gòu)??蛻舳说恼埱蟮竭_(dá)服務(wù)器以后,MVC控制器作為DDD的Facade使用,用于從緩存中獲取領(lǐng)域?qū)嶓w對象或者構(gòu)建新的領(lǐng)域?qū)ο蟮?;業(yè)務(wù)邏輯不再通過直接的服務(wù)調(diào)用,而是通過領(lǐng)域?qū)ο笾g以消息的方式相互作用來完成。一個領(lǐng)域通過領(lǐng)域服務(wù)對象來組織整體業(yè)務(wù),領(lǐng)域也有能力實(shí)現(xiàn)自身的領(lǐng)域服務(wù),這些交互均通過領(lǐng)域事件來觸發(fā),信息通過領(lǐng)域消息來傳遞。Takia對領(lǐng)域緩存和領(lǐng)域消息均采用Spring AOP和Auto?Proxy的透明機(jī)制實(shí)現(xiàn),對業(yè)務(wù)組件零入侵,基本對現(xiàn)有開發(fā)方式不造成影響。Takia DDD的編程模型設(shè)計[5]如圖4所示。
圖4 Takia DDD的編程模型設(shè)計
由圖4可知,Takia DDD替換了Spring原有的業(yè)務(wù)層和持久層,采用以領(lǐng)域容器為核心的輻射結(jié)構(gòu)。在該模型中,SpringUI和MVC組件充當(dāng)Facade使用,當(dāng)截獲用戶請求以后,會對請求進(jìn)行解析和封裝,形成DDD的語義形式[7],傳遞給Takia DDD。Takia首先從領(lǐng)域模型容器(ModelContainer)中提取該請求所對應(yīng)聚合根(Aggregation Root)對象,通過對象圖遍歷(Object Graph Iteration)找到請求所需業(yè)務(wù)實(shí)體(Entity)對象,模型容器中的實(shí)體對象均為富對象模型(RichModel)。在業(yè)務(wù)處理過程中,實(shí)體可直接調(diào)用自己的相關(guān)方法完成業(yè)務(wù)處理,將結(jié)果返回表現(xiàn)層組件,然后返回給客戶端,整個處理過程結(jié)束。該過程存在的問題與解決方案:
(1) 領(lǐng)域?qū)ο笈cORM對象的生命周期(LifeCycle?LC)不匹配問題。在ORM框架體系下,對象的生命周期與會話同步,一個會話只會在一次請求的過程中提供服務(wù)。會話關(guān)閉后,ORM對象將會被終結(jié),隨著事務(wù)提交,將對象數(shù)據(jù)從內(nèi)存更新到數(shù)據(jù)庫,這種模型也是造成系統(tǒng)對數(shù)據(jù)庫產(chǎn)生依賴的主要原因;與此相反,DDD的對象模型是內(nèi)存駐留(In Memory)模型,理論上只要該領(lǐng)域沒有被銷毀,都應(yīng)該在內(nèi)存中存在。當(dāng)然在實(shí)際項(xiàng)目開發(fā)過程中,由于內(nèi)存始終是有限的,所以在實(shí)際項(xiàng)目開發(fā)過程中一般采用內(nèi)存領(lǐng)域緩存配合模型加載器(ModelLoader)或者AOP緩存攔截器(CacheInterceptor)的方式來實(shí)現(xiàn)面向內(nèi)存的領(lǐng)域體系。
(2) 領(lǐng)域?qū)嶓w對象與服務(wù),倉儲等組件的交互問題。在JavaEE的框架體系中,基本采用直接耦合方式,即直接通過IoC容器配合Interceptor實(shí)現(xiàn)對領(lǐng)域?qū)ο蟮囊蕾嚱M件進(jìn)行注入,讓領(lǐng)域支持業(yè)務(wù)功能。該方式比較直觀,理解也比較容易,但是缺點(diǎn)是會對領(lǐng)域模型造成污染。隨著業(yè)務(wù)增大,使得領(lǐng)域?qū)ο笞兊玫挠纺[、領(lǐng)域維護(hù)難度迅速增加;在目前的一些基于動態(tài)語言(Ror,Grails)的構(gòu)架中,通過語言的動態(tài)特性讓領(lǐng)域?qū)ο髣討B(tài)地支持業(yè)務(wù)功能,甚至Java現(xiàn)在也有框架利用Aspectj實(shí)現(xiàn)了類似的效果(如Spring Roo)。
該方法雖然降低了領(lǐng)域?qū)ο蟮奈廴境潭龋瞧渚幊棠P团c直接組件注入完全相同,沒有從本質(zhì)上改變業(yè)務(wù)的處理方式,對業(yè)務(wù)系統(tǒng)的性能提升沒有任何作用。而本文采用的是事件驅(qū)動的思想來構(gòu)建Takia,組件之間的信息傳遞通過消息總線來完成。其中同步消息跟目前的編程模型一致,而異步消息通過多線程和并發(fā)隊(duì)列框架,消除了請求處理的阻塞,請求和處理分別在不同的模塊中進(jìn)行處理,只有在必要的時候進(jìn)行同步,大大提高了業(yè)務(wù)系統(tǒng)的處理性能。從圖4可以看出,在Takia中領(lǐng)域組件和業(yè)務(wù)組件之間通過本地DomainMessage消息傳遞信息,沒有任何其他耦合關(guān)系;同時Takia通過消息翻譯器(MessageTranslator)支持本地消息同分布式JMS消息系統(tǒng)的無縫切換。
4 結(jié) 語
針對目前JavaEE的分層構(gòu)架出現(xiàn)的領(lǐng)域失配、性能低下和DDD理論與實(shí)踐之間的不兼容等問題,本文在Evans DDD理論和Spring框架的基礎(chǔ)上,實(shí)現(xiàn)了一個基于領(lǐng)域消息驅(qū)動和內(nèi)存建模的DDD插件Takia。在不影響目前系統(tǒng)架構(gòu)的基礎(chǔ)上,使項(xiàng)目實(shí)施完全兼容DDD設(shè)計,同時基于消息的通信機(jī)制也能更有效地解耦系統(tǒng)模塊,并大大提高了系統(tǒng)并發(fā)性能,為JavaEE的企業(yè)信息系統(tǒng)建模提供了一種全新的思維方式。在項(xiàng)目實(shí)踐中證明,Takia比傳統(tǒng)的分層模型更加合理高效。
參考文獻(xiàn)
[1] 杜佳.對象/關(guān)系映射技術(shù)在信息系統(tǒng)開發(fā)中的應(yīng)用研究[D].西安:西安電子科技大學(xué),2011.
[2] Eric Evans.領(lǐng)域驅(qū)動設(shè)計[M].北京:清華大學(xué)出版社,2006.
[3] 秦秀磊,張文博,魏峻,等.云計算環(huán)境下分布式緩存技術(shù)的現(xiàn)狀與挑戰(zhàn)[J].軟件學(xué)報,2013(1):50?66.
[4] 陳濤,黃強(qiáng),倪少權(quán).鐵水聯(lián)運(yùn)信息整合的框架研究方案[M].北京:鐵道部,2011.
[5] 黃強(qiáng),錢文輝.Takia?DDD構(gòu)架及詳細(xì)設(shè)計說明書[M].上海:上海瑞瀚科技有限公司,2012.
[6] 黃強(qiáng),王薇,倪少權(quán).基于SOA和DDD的鐵水聯(lián)運(yùn)信息平臺構(gòu)架設(shè)計[J].計算機(jī)應(yīng)用與軟件,2013(6):124?126.
[7] Bluishglc.領(lǐng)域驅(qū)動設(shè)計(Domain Driven Design)參考架構(gòu)詳解[EB/OL].[2011?08?12].http://blog.csdn.net/bluishglc/article/de
tails/6681253.
[8] 丁濤.基于領(lǐng)域驅(qū)動設(shè)計的物流平臺系統(tǒng)實(shí)現(xiàn)[D].成都:電子科技大學(xué),2010.