前幾天給部門(mén)內(nèi)部做了一個(gè)DDD方面的培訓(xùn),這篇文章就記錄一下培訓(xùn)的主要內(nèi)容。
一 軟件的目標(biāo)是什么
軟件的目標(biāo)是快速地響應(yīng)客戶的需求變更,傳統(tǒng)的軟件開(kāi)發(fā)方式割裂了軟件的功能性需求和非功能性需求,首先業(yè)務(wù)人員分析好需求以后,拿給開(kāi)發(fā)人員進(jìn)行開(kāi)發(fā),這樣就使得軟件的功能性需求是依賴于某一種技術(shù)了,甚至有時(shí)候還會(huì)造成軟件系統(tǒng)離開(kāi)一兩個(gè)開(kāi)發(fā)人員就不能維護(hù)了,這其實(shí)都是將功能性需求和非功能性需求分離造成的后果。
采用領(lǐng)域驅(qū)動(dòng)的開(kāi)發(fā)方式,最終系統(tǒng)形成了一個(gè)通用的模型,這個(gè)模型是完全面向業(yè)務(wù)的,這個(gè)模型是業(yè)務(wù)人員和開(kāi)發(fā)人員都能容易理解的,同時(shí)這個(gè)模型也是如實(shí)的反映了領(lǐng)域?qū)嵸|(zhì)的,這樣以來(lái)軟件不是依賴于某種技術(shù),同一個(gè)模型可以用不同的技術(shù)來(lái)實(shí)現(xiàn)。與此同時(shí),采用領(lǐng)域模型以后,領(lǐng)域模型是一個(gè)對(duì)象模型,而這個(gè)對(duì)象模型是容易理解,容易維護(hù),容易復(fù)用,同時(shí)加入分布式緩存系統(tǒng)以后,對(duì)象模型是具有伸縮性的,因此領(lǐng)域模型在分析之初就將功能性需求和非功能性需求統(tǒng)一在了一起。采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)以后,軟件系統(tǒng)的功能性需求和非功能性需求完美的統(tǒng)一了,那么具體都有哪些非功能性的需求呢?
1 Extendability(擴(kuò)展性)
任何事物都處于一種發(fā)展變化當(dāng)中,軟件當(dāng)然也不例外,因此一個(gè)軟件系統(tǒng)必須要良好的可擴(kuò)展性,當(dāng)新的需求出現(xiàn)了,或者需求發(fā)生變化了,軟件如何能跟的上變化,如何能更快的加入新的功能,這些都是一個(gè)良好設(shè)計(jì)的軟件應(yīng)該具有的性質(zhì)。
2 Maintainability (維護(hù)性)
從哲學(xué)的角度來(lái)說(shuō),任何一種事物都是有生命的,軟件也不例外,在軟件的生命周期當(dāng)中,難免會(huì)出現(xiàn)要對(duì)軟件進(jìn)行維護(hù),而一些軟件系統(tǒng)由于文檔,代碼,注釋等等的原因,造成了軟件的維護(hù)下很差,維護(hù)成本很高,因此一個(gè)好的軟件系統(tǒng)必須要要具有良好的維護(hù)性。
3 Reuseability (復(fù)用性)
復(fù)用的概念可以說(shuō)已經(jīng)充斥在我們每個(gè)人的日常的生活當(dāng)中,同樣的軟件系統(tǒng)也應(yīng)該有復(fù)用性,一個(gè)設(shè)計(jì)良好的軟件系統(tǒng),它的內(nèi)部各種組件都是良好復(fù)用的,在需要一些功能的時(shí)候,可以通過(guò)已經(jīng)存在的組件來(lái)構(gòu)造,而不是每個(gè)功能都重頭來(lái)做一遍,這樣不僅增大了開(kāi)發(fā)成本,減低了開(kāi)發(fā)的效率,同時(shí)這個(gè)軟件系統(tǒng)的復(fù)用性就降得很低。
4 Scalability(伸縮性){垂直,水平}
軟件的可伸縮性是指在軟件系統(tǒng)負(fù)載變大的時(shí)候,軟件系統(tǒng)只需要增加更多的資源就可以應(yīng)對(duì)更大的負(fù)載,響應(yīng)更多用戶的請(qǐng)求。而軟件的伸縮有方向性,通常有橫向的和縱向的,橫向就是指水平的伸縮性,在負(fù)載增多的時(shí)候,我們?cè)黾痈嗟倪壿媶卧?,讓這些邏輯單元就像是同一個(gè)單元一樣,比如我們?cè)黾痈嗟?span style="font-family: times new roman">Jboss的實(shí)例,增加多個(gè)PC server等。而縱向來(lái)說(shuō),就是指垂直伸縮性,垂直伸縮式指對(duì)同一個(gè)邏輯單元進(jìn)行增強(qiáng),比如增加CPU,增加內(nèi)存,增加更快速的磁盤(pán)等等。
在軟件的的伸縮性中,垂直伸縮性往往是受限制比較大的,并且成本也比較高的,一個(gè)普通的Server,你不可能無(wú)限的增加CPU,增加內(nèi)存等,因此總是有個(gè)限制,而水平伸縮,限制就會(huì)小很多。但是如何設(shè)計(jì)我們的軟件系統(tǒng)使其更加具有伸縮性,這也是一個(gè)大的挑戰(zhàn)。而采用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和緩存的方式,就可以提高軟件的可伸縮性,更準(zhǔn)確來(lái)說(shuō)就是提高軟件系統(tǒng)的水平伸縮性。
5 Performance(性能){多快,多大}
通常我們?cè)诶斫庖粋€(gè)軟件系統(tǒng)的性能的時(shí)候,我們第一時(shí)間都會(huì)想到,這個(gè)軟件系統(tǒng)快不快,好像一個(gè)軟件系統(tǒng)只要速度快就是性能好,其實(shí)這樣理解軟件系統(tǒng)的性能,存在一定的偏差,速度快只是性能的“多快”的方面,性能還有很重要的一個(gè)方面,那就是“多大”,軟件系統(tǒng)能支持多少用戶,軟件系統(tǒng)在支持多少用戶量的時(shí)候還能保持某一個(gè)響應(yīng)的速度,這就是性能的“多大”的方面。因此在考慮系統(tǒng)的性能的時(shí)候,需要從“多快”和“多大”兩個(gè)方面來(lái)考慮。
二 Domain Driven Design(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))
1 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的概念
1.1 What is the domain model?
首先軟件是什么?在人們的腦海中,軟件好像就是一種計(jì)算的工具,但是這是軟件剛剛出現(xiàn)的時(shí)候的概念?,F(xiàn)在的軟件已經(jīng)不僅僅是一種計(jì)算工具,它代表的是一種針對(duì)某一個(gè)領(lǐng)域的解決方案,軟件可以幫助某一個(gè)領(lǐng)域來(lái)完成特定的工作,軟件可以幫助我們處理現(xiàn)代生活中復(fù)雜的工作。
理解了軟件是什么以后,我們需要清楚一個(gè)軟件的核心是什么?也許有人會(huì)說(shuō)軟件的核心是實(shí)現(xiàn)軟件的技術(shù),是的,我也承認(rèn),軟件必須通過(guò)某一種技術(shù)來(lái)實(shí)現(xiàn),但是我這里想說(shuō)的軟件的核心應(yīng)該是一個(gè)模型,是一個(gè)與具體技術(shù)無(wú)關(guān)的模型,因?yàn)榧夹g(shù)的發(fā)展日新月異,并且我們的客戶也是看不到你到底用了哪種技術(shù)來(lái)構(gòu)建軟件的,客戶關(guān)心的是你有沒(méi)有真正的實(shí)現(xiàn)軟件的需求,你的軟件有沒(méi)有如實(shí)的完成了符合某一個(gè)特定領(lǐng)域的工作。而生活中某一個(gè)領(lǐng)域相對(duì)來(lái)說(shuō)是穩(wěn)定的,從而某一個(gè)領(lǐng)域所對(duì)應(yīng)的模型也應(yīng)該是相對(duì)穩(wěn)定的。
理解了軟件的核心是一個(gè)忠實(shí)反映軟件所解決問(wèn)題的領(lǐng)域模型后,我們?cè)賮?lái)說(shuō)說(shuō)什么是領(lǐng)域模型。軟件的領(lǐng)域模型可以通過(guò)好多種方式來(lái)描述,而在面向?qū)ο蟮募夹g(shù)變得越來(lái)越流行的當(dāng)今社會(huì),領(lǐng)域模型最合適的描述方式就是通過(guò)一個(gè)對(duì)象模型來(lái)描述領(lǐng)域模型。因此就目前來(lái)說(shuō),領(lǐng)域模型可以理解為反映一個(gè)領(lǐng)域?qū)嵸|(zhì)的對(duì)象模型。
因此軟件設(shè)計(jì)是一個(gè)藝術(shù)的過(guò)程,軟件設(shè)計(jì)不能從數(shù)學(xué)的角度去思考,軟件不能通過(guò)定理通過(guò)公式來(lái)表達(dá),軟件需要通過(guò)一個(gè)模型來(lái)表達(dá)。
1.2 Ubiquitous Language(通用語(yǔ)言)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)引入了Ubiquitous Language的概念,UL是業(yè)務(wù)專(zhuān)家或者領(lǐng)域?qū)<液烷_(kāi)發(fā)者采用的通用語(yǔ)言,在討論中,開(kāi)發(fā)者和領(lǐng)域?qū)<叶纪ㄟ^(guò)UL進(jìn)行討論,這樣就避免了領(lǐng)域?qū)<液烷_(kāi)發(fā)者用不同的術(shù)語(yǔ)描述的是同樣的概念,從而引起的混淆。
Ubiquitous Language更加側(cè)重于業(yè)務(wù)和領(lǐng)域方面的術(shù)語(yǔ),而不是技術(shù)術(shù)語(yǔ),我們作為開(kāi)發(fā)人員,經(jīng)常討論中會(huì)引入一些技術(shù)方面的術(shù)語(yǔ),這應(yīng)該是所有開(kāi)發(fā)者的通病,在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中,所有參與項(xiàng)目的人共用統(tǒng)一的通用語(yǔ)言。無(wú)論是BA,PL,PM,SE還是開(kāi)發(fā)人員,在討論的過(guò)程統(tǒng)一使用通用語(yǔ)言。
1.3 Bounded Context(邊界上下文)
在進(jìn)行DDD實(shí)踐的過(guò)程中,我們完全可以將系統(tǒng)所有的對(duì)象都建模為一個(gè)模型,這個(gè)模型中包含了系統(tǒng)中所有的對(duì)象,這樣做可以,但是這樣以來(lái)就會(huì)使得領(lǐng)域模型變得非常的大,同時(shí)也增加了領(lǐng)域模型維護(hù)和改進(jìn)的難度,因此需要一種機(jī)制來(lái)使得領(lǐng)域模型能劃分的更細(xì)一點(diǎn),這就是所謂的“Bounded Context"。
邊界上下文將領(lǐng)域模型劃分為一個(gè)邊界子領(lǐng)域,這樣使得大的領(lǐng)域模型劃分小的子領(lǐng)域,這樣在擴(kuò)展維護(hù)的時(shí)候或者在對(duì)領(lǐng)域模型進(jìn)行重構(gòu)的時(shí)候,影響就會(huì)變的小。拿大家熟悉的電子商務(wù)領(lǐng)域來(lái)說(shuō),在一個(gè)電子商務(wù)領(lǐng)域,整個(gè)領(lǐng)域模型是很龐大的,因此有必要將其劃分為小的子領(lǐng)域,比如在購(gòu)物的時(shí)候,我們有個(gè)Shopping的概念,在下訂單的時(shí)候有"Order"的概念,這些其實(shí)就是一個(gè)不同子邊界上下文,Shopping Context和Order Context.
2 為什么要引入領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)?
2.1 目前項(xiàng)目中存在的問(wèn)題
2.1.1 不注重軟件的生命周期
任何一種事物都是有生命的,這種樸素的哲學(xué)觀也可以映射到軟件系統(tǒng)當(dāng)中。系統(tǒng)初期也許負(fù)載量小,系統(tǒng)運(yùn)行良好,但是隨著用戶量的增大,系統(tǒng)的負(fù)載也變的越來(lái)越大,這個(gè)時(shí)候如果在系統(tǒng)設(shè)計(jì)初期沒(méi)有注重軟件的生命周期的話,那么系統(tǒng)就很容易隨著負(fù)載的增加而宕掉,同時(shí)軟件的追究不在于滿足當(dāng)前的客戶需求,軟件更應(yīng)該追求一如既往的滿足客戶的需求,如何使得軟件能夠在新的需求出現(xiàn)的時(shí)候快速的響應(yīng),并且以較小的成本來(lái)完成需求變更響應(yīng),這就要求在軟件在設(shè)計(jì)初期就考慮到軟件的生命周期。因此如果想讓我們的系統(tǒng)能平滑的應(yīng)對(duì)大負(fù)載,同時(shí)能快速的跟上需求變化,這個(gè)時(shí)候就需要合適的架構(gòu)和真正符合領(lǐng)域?qū)嵸|(zhì)的領(lǐng)域模型。
2.1.2 過(guò)分依賴數(shù)據(jù)庫(kù)編程
目前很多J2EE的系統(tǒng)都存在過(guò)分依賴數(shù)據(jù)這種現(xiàn)象,每次業(yè)務(wù)操作都是直接調(diào)用dao來(lái)完成,這樣其實(shí)和以前那種存儲(chǔ)過(guò)程是差不多的,通過(guò)這種方式開(kāi)發(fā),無(wú)形中給數(shù)據(jù)庫(kù)造成了相當(dāng)大的壓力,而項(xiàng)目伸縮性的最大的敵人也正是數(shù)據(jù)庫(kù)。過(guò)分依賴數(shù)據(jù)庫(kù)最終就造成整個(gè)系統(tǒng)壓力跑到了數(shù)據(jù)庫(kù)里面,隨著系統(tǒng)負(fù)載的不斷增加,數(shù)據(jù)庫(kù)的壓力將越來(lái)越大,最終數(shù)據(jù)庫(kù)因不堪重負(fù)而宕掉。因此如果過(guò)分依賴數(shù)據(jù)庫(kù),那么我還用那些中間件服務(wù)器做什么?還用Java做什么?所以一個(gè)系統(tǒng)要想有一個(gè)好的伸縮性,第一步就是要打破依賴數(shù)據(jù)庫(kù)這個(gè)盒子,打破數(shù)據(jù)庫(kù)這個(gè)盒子的目前最合適的方式就是采用領(lǐng)域模型的方式。
2.1.3 面向過(guò)程思維
Java是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,那么是不是用了Java就表面是面向?qū)ο罅??完全不是一個(gè)概念!目前的很多項(xiàng)目中,其實(shí)都是面向過(guò)程式開(kāi)發(fā),大量的業(yè)務(wù)邏輯都在Service里實(shí)現(xiàn),而領(lǐng)域?qū)ο笃鋵?shí)完全都是貧血的,沒(méi)有行為,僅僅是一種數(shù)據(jù)容器,這樣以來(lái)每次業(yè)務(wù)操作都是action-->service-->dao,這其實(shí)就是一種面向過(guò)程的思維,專(zhuān)業(yè)一點(diǎn),這說(shuō)白了就是POEAA中的事務(wù)腳本模式,這種方式只適用于小項(xiàng)目,大型項(xiàng)目就需要采用領(lǐng)域模型的方式進(jìn)行。
2.1.4 不能快速響應(yīng)需求變化
軟件的需求是多變的,如何應(yīng)對(duì)這種多變的需求,這對(duì)軟件開(kāi)發(fā)來(lái)說(shuō)是個(gè)大的挑戰(zhàn)。而如何應(yīng)對(duì)這種變化呢?我們?cè)诰唧w的開(kāi)發(fā)當(dāng)中就應(yīng)該采用敏捷迭代,持續(xù)重構(gòu)和改善的方式來(lái)進(jìn)行,而不能采用傳統(tǒng)軟件工程中“瀑布式的”這種軟件工程方法。在我們公司的價(jià)值觀里面有一條“擁抱變化,學(xué)習(xí)成長(zhǎng)”,這個(gè)擁抱變化的思想非常重要,它正面面對(duì)了一個(gè)軟件開(kāi)發(fā)當(dāng)中不可避免的問(wèn)題。
在采用了敏捷迭代,持續(xù)重構(gòu)、改善和擁抱變化的開(kāi)發(fā)方式和思想以后,相當(dāng)于我們確定了一個(gè)總體的軟件開(kāi)發(fā)的方式,但是這樣還不夠,到底我們迭代什么,持續(xù)重構(gòu)和改善什么還沒(méi)確定,這個(gè)時(shí)候就正是領(lǐng)域模型發(fā)揮作用的時(shí)候。在軟件需求發(fā)生變化的時(shí)候,我們積極主動(dòng)的去擁抱了需求的變化,而同時(shí)我們將這種變化如實(shí)的反映在領(lǐng)域模型當(dāng)中,這種改善和重構(gòu)是為以后更快速的開(kāi)發(fā)做準(zhǔn)備,因?yàn)殡S著項(xiàng)目的進(jìn)行,領(lǐng)域模型已經(jīng)能越來(lái)越真實(shí)的反映領(lǐng)域的實(shí)質(zhì),這樣就軟件開(kāi)發(fā)速度就會(huì)越來(lái)越快,因?yàn)楹芏喙δ茉陬I(lǐng)域模型里面已經(jīng)完成了,需要的時(shí)候只需要復(fù)用就可以了。
2.1.5 需求分析和設(shè)計(jì)不匹配
需求分析人員或者業(yè)務(wù)人員的職責(zé)重點(diǎn)就是從客戶那里挖掘出需求,真正理解客戶需要什么,客戶需要我們的系統(tǒng)是個(gè)什么樣子,等業(yè)務(wù)人員提煉需求以后,設(shè)計(jì)人員根據(jù)需求分析文檔進(jìn)行軟件的設(shè)計(jì),初看這也許沒(méi)什么問(wèn)題,但是深入的想下去,我們就會(huì)發(fā)現(xiàn)這個(gè)環(huán)節(jié)缺乏一個(gè)良性的互動(dòng),缺乏一個(gè)統(tǒng)一的語(yǔ)言。缺乏互動(dòng)和缺乏統(tǒng)一的語(yǔ)言最終就造成分析和設(shè)計(jì)脫節(jié),從而延緩了項(xiàng)目開(kāi)發(fā)進(jìn)度。
所以要想真正跨越分析和設(shè)計(jì)之間的鴻溝,目前領(lǐng)域建模是非常合適的方式。通過(guò)領(lǐng)域模型,分析人員和設(shè)計(jì)人員在一起討論,形成一個(gè)初步的領(lǐng)域模型,這個(gè)過(guò)程中,分析人員和業(yè)務(wù)人員就有一個(gè)統(tǒng)一的語(yǔ)言,這樣分析人員和設(shè)計(jì)人員也能更加的理解客戶的需求,同時(shí)形成的領(lǐng)域模型也能更忠實(shí)的反映領(lǐng)域的本質(zhì),這樣的領(lǐng)域模型的復(fù)用性是非常高的,這也就顯著的提高了項(xiàng)目開(kāi)發(fā)效率。
2.1.6 不重視對(duì)象的生命周期
Java是一門(mén)偉大的語(yǔ)言,它內(nèi)置了垃圾收集器機(jī)制,這樣是不是就說(shuō)明我們不需要關(guān)注內(nèi)存中對(duì)象的管理了呢?其實(shí)我們還是要關(guān)注對(duì)象的生命周期,系統(tǒng)中的一些對(duì)象如果用完了就扔給垃圾收集器,這樣勢(shì)必會(huì)造成垃圾收集器的頻繁啟動(dòng),而垃圾收集器的啟動(dòng)在采用不同的垃圾收集策略的情況下是具有非常不同的區(qū)別的。因此對(duì)象生命周期管理也是開(kāi)發(fā)一個(gè)優(yōu)良的面向?qū)ο蟮能浖到y(tǒng)很重要的一項(xiàng)任務(wù)。
3 領(lǐng)域模型和架構(gòu)的關(guān)系
在目前J2EE項(xiàng)目中,軟件架構(gòu)主要采用分層架構(gòu)的思想,而分層帶來(lái)的好處就是提高軟件的可擴(kuò)展性,可維護(hù)以及可伸縮性。J2EE項(xiàng)目傳統(tǒng)上劃分為3層:表現(xiàn)出,業(yè)務(wù)層,持久層。
3.1 DDD中分層標(biāo)準(zhǔn)
3.1.1 Presentation Layer(表現(xiàn)層)
表現(xiàn)層負(fù)責(zé)提供用戶的接口,它和傳統(tǒng)的表現(xiàn)層的概念是一致的。表現(xiàn)層僅僅是負(fù)責(zé)接受用戶的請(qǐng)求,然后調(diào)用應(yīng)用層層獲取領(lǐng)域?qū)ο髞?lái)渲染結(jié)果視圖,最終進(jìn)行視圖的展現(xiàn)。表現(xiàn)層主要采用MVC模式。
3.1.2 Application Layer (應(yīng)用層)
應(yīng)用層定義了軟件系統(tǒng)所能做的事情,但是它不負(fù)責(zé)怎么做,也就是說(shuō)應(yīng)用層只定義了"what to do",而不關(guān)注"How to do".應(yīng)用層負(fù)責(zé)調(diào)用具有豐富業(yè)務(wù)邏輯的領(lǐng)域?qū)ο髞?lái)完成某一次的業(yè)務(wù)操作。同時(shí)應(yīng)用層還需要負(fù)責(zé)提供與其它系統(tǒng)進(jìn)行交互的接口。
應(yīng)用層不負(fù)責(zé)保存與業(yè)務(wù)有關(guān)系的狀態(tài),它僅僅只是將工作委托給領(lǐng)域?qū)ο髞?lái)完成,雖然應(yīng)用層不包含業(yè)務(wù)規(guī)則和狀態(tài),但是應(yīng)用層可以包含操作過(guò)程的狀態(tài),比如事務(wù)狀態(tài)等。
3.1.3 Domain or Model Layer(領(lǐng)域或模型層)
領(lǐng)域?qū)邮窍到y(tǒng)的核心,領(lǐng)域?qū)訉?shí)現(xiàn)了軟件的核心的業(yè)務(wù)邏輯和業(yè)務(wù)規(guī)則,領(lǐng)域模型就屬于這一層。
領(lǐng)域?qū)泳唧w會(huì)包括很多重要的對(duì)象,這部分將在“領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的關(guān)鍵角色”部分說(shuō)明。
3.1.4 Infrastructure Layer(基礎(chǔ)結(jié)構(gòu)層)
Infrastructure Layer提供了系統(tǒng)技術(shù)性的支持,比如持久化訪問(wèn)數(shù)據(jù)庫(kù),消息發(fā)送,郵件發(fā)送等。
3.2 領(lǐng)域模型在架構(gòu)中的地位
架構(gòu)是整個(gè)系統(tǒng)骨架,架構(gòu)是一個(gè)水環(huán)境,而領(lǐng)域模型是魚(yú)。領(lǐng)域模型即獨(dú)立于架構(gòu)又服務(wù)于架構(gòu)的。獨(dú)立性體現(xiàn)在領(lǐng)域模型可以用于不同的架構(gòu)環(huán)境中,就好像把一條魚(yú)從一個(gè)水域移到另外一個(gè)水域,它照樣可以存活一樣,而服務(wù)于架構(gòu)體現(xiàn)在領(lǐng)域模型需要融入具體的架構(gòu)中,才能構(gòu)成完整的系統(tǒng)。
架構(gòu)關(guān)注與系統(tǒng)的整體的結(jié)構(gòu),這個(gè)結(jié)構(gòu)不僅會(huì)涉及到系統(tǒng)本身的業(yè)務(wù),比如系統(tǒng)主要有那些業(yè)務(wù)模塊構(gòu)成,而且也會(huì)涉及到具體的技術(shù)實(shí)現(xiàn),比如業(yè)務(wù)層是采用spring,EJB還是Jdon,持久層是采用hibernate,JPA,IBATIS還是JDBC。而領(lǐng)域模型是完全面向業(yè)務(wù)的,它不會(huì)與具體的技術(shù)耦合。因此領(lǐng)域模型和架構(gòu)的分離還體現(xiàn)了一種思想:分析和設(shè)計(jì)的時(shí)候分離,實(shí)現(xiàn)的時(shí)候粘合,而到真正運(yùn)行的時(shí)候完全統(tǒng)一的思想。(領(lǐng)域模型在分析和設(shè)計(jì)的時(shí)候是獨(dú)立于架構(gòu),而實(shí)現(xiàn)的時(shí)候會(huì)和具體的架構(gòu)進(jìn)行粘合,而真正運(yùn)行的時(shí)候會(huì)和具體的架構(gòu)進(jìn)行完全的統(tǒng)一),這樣EJB分布式組件當(dāng)初的思想(編碼時(shí)分離,部署時(shí)粘合,運(yùn)行時(shí)真正統(tǒng)一是一致的)。
領(lǐng)域模型是針對(duì)于某一個(gè)特定領(lǐng)域的,因此每一個(gè)不同的領(lǐng)域都會(huì)具有不同的領(lǐng)域模型,但是對(duì)于相同領(lǐng)域的不同項(xiàng)目,我們可以采用一套相同的領(lǐng)域模型來(lái)進(jìn)行開(kāi)發(fā)。而軟件架構(gòu)是可以在不同的領(lǐng)域進(jìn)行復(fù)用的,比如struts,spring等框架,以及分層的思想等,這些都是可以在不同的領(lǐng)域進(jìn)行復(fù)用,因此領(lǐng)域模型是面向領(lǐng)域的復(fù)用,是一種針對(duì)特定領(lǐng)域的復(fù)用,而軟件架構(gòu)是一種更加寬泛的,更加偏向于技術(shù)方面的復(fù)用。
4 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的關(guān)鍵角色
4.1 Entity(實(shí)體)
實(shí)體具有一個(gè)顯著的特征,那就是實(shí)體都是有(Identity)標(biāo)識(shí)的,我們判斷兩個(gè)實(shí)體到底是不是一樣的,我們只是根據(jù)實(shí)體的Identity,兩個(gè)實(shí)體即使其它的屬性都一樣,但是只要標(biāo)識(shí)不一樣,那么這兩個(gè)實(shí)體也是不一樣的,比如在軟件系統(tǒng)中,有兩個(gè)Customer對(duì)象,這個(gè)對(duì)象的其它屬性都一樣(名字,性別,年齡等),但是他們的Identity不一樣,那么這兩實(shí)體就是不一樣的。
DDD中的實(shí)體和我們一般的軟件系統(tǒng)中的實(shí)體有什么區(qū)別呢?那就是DDD中的Entity是具有豐富的業(yè)務(wù)行為的,是充血模型的,而不是貧血模型的,像一般的軟件系統(tǒng)中,實(shí)體往往都只是數(shù)據(jù)庫(kù)表中數(shù)據(jù)容器,沒(méi)有任何行為,所有的業(yè)務(wù)邏輯的實(shí)現(xiàn)都跑到了service層,而Service不能如實(shí)的映射到領(lǐng)域中,因此用它來(lái)表達(dá)業(yè)務(wù)也是不適合的。
充血模型和貧血模型,我們?cè)谂袛鄬?shí)體到底是充血模型還是貧血模型的時(shí)候,不能僅僅只從單一實(shí)體的行為上來(lái)看,要從一個(gè)整體的角度來(lái)看,單單看一個(gè)實(shí)體,它是無(wú)行為的,但是這個(gè)實(shí)體屬于一個(gè)聚合邊界的時(shí)候,整個(gè)這個(gè)邊界是充血的,因此我們判斷到底是充血還是貧血的時(shí)候,應(yīng)該有一個(gè)邊界的概念,只要邊界里面的實(shí)體對(duì)象整體上業(yè)務(wù)行為豐富就OK。
4.2 Value object (值對(duì)象)
J2EE中各種O的概念太多了,比如PO,VO等等,DDD中的值對(duì)象與以前的值對(duì)象是有區(qū)別的。在EJB2.X中有Entity bean的概念,因?yàn)?span style="font-family: times new roman">Entity bean也是以一種分布式組件,一次每次遠(yuǎn)程調(diào)用都是有開(kāi)銷(xiāo)的,因此SUN公司的工程師們創(chuàng)造了一個(gè)值對(duì)象的概念,值對(duì)象就是Entity bean的數(shù)據(jù),它僅僅起到了在系統(tǒng)各層之間傳遞數(shù)據(jù)的功能。
DDD中的VO有它自己的含義。DDD中的VO一般都是一些描述性的對(duì)象,通常都是對(duì)Entity的描述,比如一個(gè)SNS型網(wǎng)站,它有PersonalPage(個(gè)人主頁(yè))的概念,PersonalPage有一些狀態(tài),比如最近訪客列表,好友列表,新鮮事等等,這些其實(shí)都是一種狀態(tài)信息,我們就可以將其做為PersonalPage的狀態(tài),還比如論壇帖子,它也有一些狀態(tài)信息,比如帖子的回復(fù)有多少,最近一次回復(fù)是什么時(shí)間等,這些也可以封裝到一個(gè)VO里,當(dāng)做是帖子的狀態(tài)對(duì)象。
DDD中的VO還有一些不可變的對(duì)象,比如軟件系統(tǒng)中的Money,Address等對(duì)象,這些對(duì)象都只是根據(jù)值來(lái)確定的,只要兩個(gè)Money對(duì)象的值一樣,我們就可以在不同的實(shí)體里使用,而不需要區(qū)分這個(gè)Money到底是那個(gè)實(shí)體的Money.
4.3 Aggregate(聚合)
DDD中的聚合有點(diǎn)類(lèi)似UML中聚合的概念,聚合代表一些邏輯上聯(lián)系比較緊密的對(duì)象的集合,每一個(gè)聚合都有一個(gè)Aggregate Root(聚合根)對(duì)象,聚合根控制了對(duì)聚合內(nèi)部對(duì)象的訪問(wèn),聚合外部要想訪問(wèn)聚合內(nèi)部的對(duì)象,必須通過(guò)聚合根對(duì)象來(lái)訪問(wèn)。
拿Order和OrderLine來(lái)說(shuō),訂單Order是聚合根,而Orderline是聚合內(nèi)部的子對(duì)象,聚合外部要想獲取Orderline的信息必須通過(guò)Order來(lái)獲取,同時(shí)Orderline也是完全屬于Order的,Orderline不能獨(dú)立于Order而存在,系統(tǒng)中不能存在沒(méi)有Order的Orderline,因此一般在刪除Order的時(shí)候,Orderline也需要進(jìn)行刪除。
聚合還有一個(gè)重要的特性,那就是不變量的約束,聚合根對(duì)象要保證自己聚合內(nèi)部的不變量是一致的,聚合根提供給外界的方法都必須有前置條件和后驗(yàn)條件的約束,比如Order的總價(jià)格必須要和每個(gè)Orderline加起來(lái)的總價(jià)格是一致的,而不能發(fā)現(xiàn)不一致的狀態(tài),這個(gè)一致性的維護(hù)都要通過(guò)Order來(lái)進(jìn)行。
因此聚合最重要的兩個(gè)特點(diǎn)就是聚合提供了一種組織邏輯上聯(lián)系緊密對(duì)象的一種方式,同時(shí)聚合還要保證聚合內(nèi)部對(duì)象生命周期以及聚合不變量的約束。
4.4 Repository(資源庫(kù))
Repository(資源庫(kù))顧名思義,它代表的是放資源的一個(gè)倉(cāng)庫(kù),那么它里面到底放什么東西呢?這就是系統(tǒng)的領(lǐng)域模型對(duì)象。
Repository提供了訪問(wèn)領(lǐng)域模型對(duì)象接口,領(lǐng)域?qū)油ㄟ^(guò)Repository來(lái)獲取領(lǐng)域?qū)ο蟆?/span>
在傳統(tǒng)的開(kāi)發(fā)當(dāng)中,我們非常熟悉Dao的概念,DDD中的Repository和Dao是不同的概念,DDD的Repository是完全面向領(lǐng)域?qū)ο蟮?,領(lǐng)域?qū)訌?span style="font-family: times new roman">Repository獲得的對(duì)象是符合不變量約束的對(duì)象,往往這個(gè)對(duì)象就是Aggregate Root對(duì)象,當(dāng)系統(tǒng)從Repository拿出領(lǐng)域?qū)ο蟮臅r(shí)候,不用再考慮這個(gè)對(duì)象是不是完整的,它里面的子對(duì)象有沒(méi)有嵌入,它的不變量有沒(méi)有得到保證,這些在從Repository拿出領(lǐng)域?qū)ο笾埃?span style="font-family: times new roman">Repository都已經(jīng)幫我們做好了。
Repository還屏蔽了系統(tǒng)底層具體的持久化技術(shù),無(wú)論我們底層采用什么樣子的存儲(chǔ)方式,比如RDBMS,XML,File System,Repository都提供一致的接口給領(lǐng)域?qū)邮褂谩?/span>
4.5 Factory(工廠)
Factory和GOF設(shè)計(jì)模式中的工廠是類(lèi)似的,采用DDD后,系統(tǒng)會(huì)形成一個(gè)完整的領(lǐng)域模型,領(lǐng)域模型里面包含的豐富的領(lǐng)域?qū)ο?,這些領(lǐng)域?qū)ο笸及S富的行為,同時(shí)也包含自己的不變量的約束,因此我們可以通過(guò)Factory封裝領(lǐng)域模型對(duì)象創(chuàng)建過(guò)程,Factory封裝了領(lǐng)域?qū)ο蟮膭?chuàng)建邏輯,同時(shí)Factory還會(huì)保證領(lǐng)域?qū)ο髣?chuàng)建以后是完整的,是符合不變量約束的。
Factory的引入主要是為了控制領(lǐng)域?qū)ο蟮纳芷冢?span style="font-family: times new roman">Factory控制了領(lǐng)域?qū)ο笊芷诘拈_(kāi)始,而Repository控制了從創(chuàng)建以后到最后消亡的生命周期。
4.6 Service(服務(wù))
Service是一種大家比較熟悉的概念,在傳統(tǒng)的開(kāi)發(fā)方式中,我們的系統(tǒng)中業(yè)務(wù)邏輯的載體就是它了。但是在DDD中,Service的概念和傳統(tǒng)的概念是不同的。
DDD中的Service可以分為兩種類(lèi)型,一種是Application Service(應(yīng)用層服務(wù)),另外一種是Domain Service(領(lǐng)域?qū)臃?wù)).
Application Service(應(yīng)用層服務(wù))關(guān)注點(diǎn)不在于系統(tǒng)的業(yè)務(wù)邏輯,應(yīng)用層的關(guān)注點(diǎn)主要在于系統(tǒng)功能的定義,也就是說(shuō)應(yīng)用層服務(wù)一般定義了What to do,而不關(guān)注“how to do",應(yīng)用層服務(wù)要想完成某一次的業(yè)務(wù)操作,需要調(diào)用領(lǐng)域模型對(duì)象來(lái)完成。應(yīng)用服務(wù)還涉及到一些與系統(tǒng)級(jí)狀態(tài)有關(guān)系的信息,比如應(yīng)用層服務(wù)一般都會(huì)涉及到事務(wù)控制,安全訪問(wèn)控制以及日志記錄等功能。
Domain Service(領(lǐng)域?qū)臃?wù))是屬于領(lǐng)域模型中的,它關(guān)注與業(yè)務(wù)邏輯的實(shí)現(xiàn),在系統(tǒng)中有一些行為可能不屬于Entity(實(shí)體),Value Object(值對(duì)象),那么這些行為就要?jiǎng)澐值?span style="font-family: times new roman">Domain Service里面。
比如在電子商務(wù)系統(tǒng)中一般都有ShoppingCart(購(gòu)物車(chē))的概念,當(dāng)用戶需要查看本次購(gòu)物的總價(jià)格的時(shí)候,這個(gè)getShoppingCosting職責(zé)的實(shí)現(xiàn)就不能由ShoppingCart來(lái)實(shí)現(xiàn),而相應(yīng)的會(huì)由PriceService和ShopcostingService來(lái)實(shí)現(xiàn),因?yàn)檫@些行為會(huì)涉及到與系統(tǒng)其它部分或者外部系統(tǒng)的交互,比如PriceService會(huì)涉及到與外部?jī)r(jià)格系統(tǒng)的交互等等。
Domain Service使得Entity和Value object對(duì)象更加的高內(nèi)聚和低耦合,這其實(shí)也反映了面向?qū)ο蟮脑O(shè)計(jì)原則,為了讓Entity和Value只包含自己應(yīng)該包含的職責(zé),對(duì)于一些本不屬于自己的職責(zé),則有專(zhuān)門(mén)的Domain Service來(lái)實(shí)現(xiàn)。
4.7 Domain Event(領(lǐng)域事件)
Domain Event是一種使得領(lǐng)域模型更加高內(nèi)聚,松耦合的機(jī)制,通過(guò)引入領(lǐng)域事件,使得核心的領(lǐng)域模型和Service和Repository解耦,這樣使得領(lǐng)域模型能更加真實(shí)的反映領(lǐng)域的實(shí)質(zhì)。