領(lǐng)域建模技術(shù)概述_第1頁
領(lǐng)域建模技術(shù)概述_第2頁
領(lǐng)域建模技術(shù)概述_第3頁
領(lǐng)域建模技術(shù)概述_第4頁
已閱讀5頁,還剩7頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、 領(lǐng)域建模技術(shù)概述 什么時(shí)候需要領(lǐng)域建模軟件的世界里沒有銀彈,是用事務(wù)腳本還是領(lǐng)域模型沒有對錯(cuò)之分,關(guān)鍵看是否合適。就像自營和平臺(tái)哪個(gè)模式更好?答案是都很好,所以亞馬遜可以有三方入住,阿里也可以有自建倉嘛。實(shí)際上,CQRS就是對事務(wù)腳本和領(lǐng)域模型兩種模式的綜合,因?yàn)閷τ赒uery和報(bào)表的場景,使用領(lǐng)域模型往往會(huì)把簡單的事情弄復(fù)雜,此時(shí)完全可以用奧卡姆剃刀把領(lǐng)域?qū)犹甑?,直接訪問Infrastructure。我個(gè)人也是堅(jiān)決反對過度設(shè)計(jì)的,因此對于簡單業(yè)務(wù)場景,我強(qiáng)力建議還是使用事務(wù)腳本,其優(yōu)點(diǎn)是簡單、直觀、易上手。但對于復(fù)雜的業(yè)務(wù)場景,你再這么玩就不行了,因?yàn)橐坏I(yè)務(wù)變得復(fù)雜,事務(wù)腳本就很難應(yīng)對,

2、容易造成代碼的“一鍋粥”,系統(tǒng)的腐化速度和復(fù)雜性呈指數(shù)級上升。目前比較有效的治理辦法就是領(lǐng)域建模,因?yàn)轭I(lǐng)域模型是面向?qū)ο蟮?,在封裝業(yè)務(wù)邏輯的同時(shí),提升了對象的內(nèi)聚性和重用性,因?yàn)槭褂昧送ㄓ谜Z言(Ubiquitous Language),使得隱藏的業(yè)務(wù)邏輯得到顯性化表達(dá),使得復(fù)雜性治理成為可能。talk is cheap,直接上一個(gè)銀行轉(zhuǎn)賬的例子,對事務(wù)腳本和領(lǐng)域模型進(jìn)行比較,孰優(yōu)孰劣一目了然。銀行轉(zhuǎn)賬事務(wù)腳本實(shí)現(xiàn)在事務(wù)腳本的實(shí)現(xiàn)中,關(guān)于在兩個(gè)賬號之間轉(zhuǎn)賬的領(lǐng)域業(yè)務(wù)邏輯都被寫在了MoneyTransferService的實(shí)現(xiàn)里面了,而Account僅僅是getters和setters的數(shù)據(jù)結(jié)構(gòu),

3、也就是我們說的貧血模式publicclass MoneyTransferServiceTransactionScriptImpl implements MoneyTransferService private AccountDao accountDao; private BankingTransactionRepositorybankingTransactionRepository; . Override public BankingTransaction transfer(String fromAccountId, String toAccountId,double amount)Accou

4、nt fromAccount = accountDao.findById(fromAccountId);Account toAccount = accountDao.findById(toAccountId); . double newBalance =fromAccount.getBalance()- amount; switch(fromAccount.getOverdraftPolicy() case NEVER: if(newBalance0)thrownewDebitException(Insufficient funds); break; case ALLOWED: if(newB

5、alance-limit)thrownewDebitException(Overdraftlimit (of + limit +) exceeded: +newBalance); break; fromAccount.setBalance(newBalance);toAccount.setBalance(toAccount.getBalance()+ amount);BankingTransaction moneyTransferTransaction =new MoneyTranferTransaction(fromAccountId,toAccountId, amount);banking

6、TransactionRepository.addTransaction(moneyTransferTransaction); return moneyTransferTransaction; 銀行轉(zhuǎn)賬領(lǐng)域模型實(shí)現(xiàn)如果用DDD的方式實(shí)現(xiàn),Account實(shí)體除了賬號屬性之外,還包含了行為和業(yè)務(wù)邏輯,比如debit( )和credit( )方法。/ Entitypublicclass Account / Id private String id; privatedouble balance; private OverdraftPolicy overdraftPolicy; . publicdoub

7、lebalance()return balance; publicvoiddebit(double amount) this.overdraftPolicy.preDebit(this, amount); this.balance =this.balance - amount; this.overdraftPolicy.postDebit(this, amount); publicvoidcredit(double amount) this.balance =this.balance + amount; 而且透支策略O(shè)verdraftPolicy也不僅僅是一個(gè)Enum了,而是被抽象成包含了業(yè)務(wù)

8、規(guī)則并采用了策略模式的對象。publicinterfaceOverdraftPolicy voidpreDebit(Accountaccount,double amount); voidpostDebit(Accountaccount,double amount);publicclassNoOverdraftAllowed implements OverdraftPolicy publicvoidpreDebit(Account account,double amount) double newBalance = account.balance()- amount; if(newBalance

9、0) thrownewDebitException(Insufficient funds); publicvoidpostDebit(Accountaccount,double amount) publicclass LimitedOverdraftimplements OverdraftPolicy privatedouble limit; . publicvoidpreDebit(Accountaccount,double amount) double newBalance = account.balance()- amount; if(newBalance-limit) thrownew

10、DebitException(Overdraftlimit (of + limit +) exceeded: +newBalance); publicvoidpostDebit(Accountaccount,double amount) 而Domain Service只需要調(diào)用Domain Entity對象完成業(yè)務(wù)邏輯即可。publicclassMoneyTransferServiceDomainModelImpl implements MoneyTransferService private AccountRepository accountRepository; private Banki

11、ngTransactionRepositorybankingTransactionRepository; . Override public BankingTransaction transfer(String fromAccountId, String toAccountId,double amount)Account fromAccount = accountRepository.findById(fromAccountId);Account toAccount = accountRepository.findById(toAccountId); .fromAccount.debit(am

12、ount);toAccount.credit(amount);BankingTransaction moneyTransferTransaction =new MoneyTranferTransaction(fromAccountId,toAccountId, amount);bankingTransactionRepository.addTransaction(moneyTransferTransaction); return moneyTransferTransaction; 通過上面的DDD重構(gòu)后,原來在事務(wù)腳本中的邏輯,被分散到Domain Service,Domain Entity和

13、OverdraftPolicy三個(gè)滿足SOLID的對象中,在繼續(xù)閱讀之前,我建議可以自己先體會(huì)一下DDD的好處。領(lǐng)域建模的好處面向?qū)ο蠓庋b:Account的相關(guān)操作都封裝在Account Entity上,提高了內(nèi)聚性和可重用性。多態(tài):采用策略模式的OverdraftPolicy(多態(tài)的典型應(yīng)用)提高了代碼的可擴(kuò)展性。業(yè)務(wù)語義顯性化通用語言:“一個(gè)團(tuán)隊(duì),一種語言”,將模型作為語言的支柱。確保團(tuán)隊(duì)在內(nèi)部的所有交流中,代碼中,畫圖,寫東西,特別是講話的時(shí)候都要使用這種語言。例如賬號,轉(zhuǎn)賬,透支策略,這些都是非常重要的領(lǐng)域概念,如果這些命名都和我們?nèi)粘S懻撘约癙RD中的描述保持一致,將會(huì)極大提升代碼的

14、可讀性,減少認(rèn)知成本。說到這,稍微吐槽一下我們有些工程師的英語水平,有些神翻譯讓一些核心領(lǐng)域概念變得面目全非。顯性化:就是將隱式的業(yè)務(wù)邏輯從一推if-else里面抽取出來,用通用語言去命名、去寫代碼、去擴(kuò)展,讓其變成顯示概念,比如“透支策略”這個(gè)重要的業(yè)務(wù)概念,按照事務(wù)腳本的寫法,其含義完全淹沒在代碼邏輯中沒有突顯出來,看代碼的人自然也是一臉懵逼,而領(lǐng)域模型里面將其用策略模式抽象出來,不僅提高了代碼的可讀性,可擴(kuò)展性也好了很多。如何進(jìn)行領(lǐng)域建模極簡建模方法領(lǐng)域建模這個(gè)話題太大,關(guān)于此的長篇大論和書籍也很多,比如什么通過語法和句法深入分析法。我這個(gè)人懶,很多方法論的東西記不住也懶得記。就寫點(diǎn)自己

15、的體會(huì),我的方法很簡單就兩個(gè)步驟,首先從User Story找名詞和動(dòng)詞,然后用UML類圖畫出領(lǐng)域模型。比如讓你設(shè)計(jì)一個(gè)中介系統(tǒng),一個(gè)典型的User Story可能是“小明去找工作,中介說你留個(gè)電話,有工作機(jī)會(huì)我會(huì)通知你”,這里面的關(guān)鍵名詞很可能就是我們需要的領(lǐng)域?qū)ο?,小明是求職者,電話是求職者的屬性,中介包含了中介公司,中介員工兩個(gè)關(guān)鍵對象;工作機(jī)會(huì)肯定也是關(guān)鍵領(lǐng)域?qū)ο?;通知這個(gè)動(dòng)詞暗示我們這里用觀察者模式會(huì)比較合適。然后再梳理一下領(lǐng)域?qū)ο笾g的關(guān)系,一個(gè)求職者可以應(yīng)聘多個(gè)工作機(jī)會(huì),一個(gè)工作機(jī)會(huì)也可以被多個(gè)求職者應(yīng)聘,M2M的關(guān)系,中介公司可以包含多個(gè)員工,O2M的關(guān)系。對于這樣簡單的場景,

16、這個(gè)建模就差不多了。當(dāng)然我們的業(yè)務(wù)場景往往比這個(gè)要復(fù)雜,而且不是所有的名詞都是領(lǐng)域?qū)ο笠部赡苁菍傩?,也不是所有的?dòng)詞都是方法也可能是領(lǐng)域?qū)ο?,所以要具體問題具體對待,這個(gè)對待的過程需要我們有很好的業(yè)務(wù)理解力,抽象能力以及建模的經(jīng)驗(yàn)(知道為什么公司的job model里那么強(qiáng)調(diào)技術(shù)人員的業(yè)務(wù)理解力和抽象能力了吧),比如通常情況下,價(jià)格和庫存只是訂單和商品的一個(gè)屬性,但是在阿里系電商業(yè)務(wù)場景下,價(jià)格計(jì)算和庫存扣減的復(fù)雜程度可以讓你懷疑人生,因此作為電商中臺(tái),把價(jià)格和庫存單獨(dú)當(dāng)成一個(gè)域(Domain)去對待是很必要的。另外,建模不是一個(gè)一次性的工作,往往隨著業(yè)務(wù)的變化以及我們對業(yè)務(wù)的理解越來越深入才

17、能看清系統(tǒng)的全貌,所以迭代重構(gòu)是免不了的,也就是要Agile Modelling。模型統(tǒng)一和模型演化建模的過程很像盲人摸象,不同背景人用不同的視角看同一個(gè)東西,其理解也是不一樣的。比如兩個(gè)盲人都摸到大象鼻子,一個(gè)人認(rèn)為是像蛇(活的能動(dòng)),而另一個(gè)人認(rèn)為像消防水管(可以噴水),那么他們將很難集成。雙方都無法接受對方的模型,因?yàn)槟遣环献约旱捏w驗(yàn)。事實(shí)上,他們需要一個(gè)新的抽象,這個(gè)抽象需要把蛇的“活著的特性”與消防水管的“噴水功能”合并到一起,而這個(gè)抽象還應(yīng)該排除先前兩個(gè)模型中一些不確切的含義和屬性,比如毒牙,或者卷起來放到救火車上去的行為。這就是模型的統(tǒng)一。世界上唯一不變的就是變化,模型和代碼一

18、樣也需要不斷的重構(gòu)和精化,每一次的精化之后,開發(fā)人員應(yīng)該對領(lǐng)域知識有了更加清晰的認(rèn)識。這使得理解上的突破成為可能,之后,一系列快速的改變得到了更符合用戶需要并更加切合實(shí)際的模型。其功能性及說明性急速增強(qiáng),而復(fù)雜性卻隨之消失。這種突破需要我們對業(yè)務(wù)有更加深刻的領(lǐng)悟和思考,然后再加上重構(gòu)的勇氣和能力,勇氣是項(xiàng)目工期很緊你敢不敢重構(gòu),能力是你有沒有完備的CI保證你的重構(gòu)不破壞現(xiàn)有的業(yè)務(wù)邏輯。還是以開篇的轉(zhuǎn)賬來舉個(gè)例子,假如轉(zhuǎn)賬業(yè)務(wù)開始變的復(fù)雜,要支持現(xiàn)金,信用卡,支付寶,比特幣等多種通道,且沒種通道的約束不一樣,還要支持一對多的轉(zhuǎn)賬。那么你還是用一個(gè)transfer(fromAccount, toA

19、ccount)就不合適了,可能需要抽象出一個(gè)專門的領(lǐng)域?qū)ο骉ransaction,這樣才能更好的表達(dá)業(yè)務(wù),其演化過程如下:領(lǐng)域服務(wù)什么是領(lǐng)域服務(wù)有些領(lǐng)域中的動(dòng)作,它們是一些動(dòng)詞,看上去卻不屬于任何對象。它們代表了領(lǐng)域中的一個(gè)重要的行為,所以不能忽略它們或者簡單地把它們合并到某個(gè)實(shí)體或者值對象中。當(dāng)這樣的行為從領(lǐng)域中被識別出來時(shí),最佳實(shí)踐是將它聲明成一個(gè)服務(wù)。這樣的對象不再擁有內(nèi)置的狀態(tài)。它的作用僅僅是為領(lǐng)域提供相應(yīng)的功能。Service往往是以一個(gè)活動(dòng)來命名,而不是Entity來命名。例如開篇轉(zhuǎn)賬的例子,轉(zhuǎn)賬(transfer)這個(gè)行為是一個(gè)非常重要的領(lǐng)域概念,但是它是發(fā)生在兩個(gè)賬號之間的,歸

20、屬于賬號Entity并不合適,因?yàn)橐粋€(gè)賬號Entity沒有必要去關(guān)聯(lián)他需要轉(zhuǎn)賬的賬號Entity,這種情況下,使用MoneyTransferDomainService就比較合適了。識別領(lǐng)域服務(wù),主要看它是否滿足以下三個(gè)特征:1. 服務(wù)執(zhí)行的操作代表了一個(gè)領(lǐng)域概念,這個(gè)領(lǐng)域概念無法自然地隸屬于一個(gè)實(shí)體或者值對象。2. 被執(zhí)行的操作涉及到領(lǐng)域中的其他的對象。3. 操作是無狀態(tài)的。應(yīng)用服務(wù)和領(lǐng)域服務(wù)如何劃分在領(lǐng)域建模中,我們一般將系統(tǒng)劃分三個(gè)大的層次,即應(yīng)用層(ApplicationLayer),領(lǐng)域?qū)樱―omain Layer)和基礎(chǔ)實(shí)施層(Infrastructure Layer),關(guān)于這三個(gè)層

21、次的詳細(xì)內(nèi)容可以參考我的另一篇SOFA框架的分層設(shè)計(jì)??梢钥吹皆贏pp層和Domain層都有服務(wù)(Service),這兩個(gè)Service如何劃分呢,什么樣的功能應(yīng)該放在應(yīng)用層,什么樣的功能應(yīng)該放在領(lǐng)域?qū)幽兀繘Q定一個(gè)服務(wù)(Service)應(yīng)該歸屬于哪一層是很困難的。如果所執(zhí)行的操作概念上屬于應(yīng)用層,那么服務(wù)就應(yīng)該放到這個(gè)層。如果操作是關(guān)于領(lǐng)域?qū)ο蟮?,而且確實(shí)是與領(lǐng)域有關(guān)的、為領(lǐng)域的需要服務(wù),那么它就應(yīng)該屬于領(lǐng)域?qū)???偟膩碚f,涉及到重要領(lǐng)域概念的行為應(yīng)該放在Domain層,而其它非領(lǐng)域邏輯的技術(shù)代碼放在App層,例如參數(shù)的解析,上下文的組裝,調(diào)用領(lǐng)域服務(wù),消息發(fā)送等。還是銀行轉(zhuǎn)賬的case為例,下

22、圖給出了劃分的建議:業(yè)務(wù)可視化和可配置化好的領(lǐng)域建模可以降低應(yīng)用的復(fù)雜性,而可視化和可配置化主要是幫助大家(主要是非技術(shù)人員,比如產(chǎn)品,業(yè)務(wù)和客戶)直觀地了解系統(tǒng)和配置系統(tǒng)。要注意的是可視化和可配置化難免會(huì)給系統(tǒng)增加額外的復(fù)雜度,必須慎之又慎,最好是能使可視化和配置化的邏輯與業(yè)務(wù)邏輯盡量少的耦合,否則破壞了原有的架構(gòu),把事情搞的更復(fù)雜就得不償失了。在可擴(kuò)展設(shè)計(jì)中,我已經(jīng)介紹了我們SOFA架構(gòu)是如何通過擴(kuò)展點(diǎn)的設(shè)計(jì)來支撐不同業(yè)務(wù)差異化的需求的,那么可否跟進(jìn)一步,我們將領(lǐng)域的行為(也叫能力)和擴(kuò)展點(diǎn)用可視化的方式呈現(xiàn)出來,并對于一些不需要編碼實(shí)現(xiàn)的擴(kuò)展點(diǎn)用配置的方式去完成呢。當(dāng)然是可以的,比如還是開篇轉(zhuǎn)賬的例子,對于透支策略O(shè)verdraftPolicy這個(gè)業(yè)務(wù)擴(kuò)展點(diǎn),

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論