你是否還在為微服務(wù)應(yīng)該拆多小而爭(zhēng)論不休?到底如何才能設(shè)計(jì)出收放自如的微服務(wù)?怎樣才能保證業(yè)務(wù)領(lǐng)域模型與代碼模型的一致性?或許本文能幫你找到答案。
本文是基于DDD的微服務(wù)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)篇,通過(guò)借鑒領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想,指導(dǎo)微服務(wù)項(xiàng)目團(tuán)隊(duì)進(jìn)行設(shè)計(jì)和開(kāi)發(fā)(理論篇詳見(jiàn)《當(dāng)中臺(tái)遇上DDD,我們?cè)撊绾卧O(shè)計(jì)微服務(wù)》)。本文包括三部分內(nèi)容:第一部分講述領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)基本知識(shí),包括:分層架構(gòu)、服務(wù)視圖、數(shù)據(jù)視圖和領(lǐng)域事件發(fā)布和訂閱等;第二部分講述微服務(wù)設(shè)計(jì)方法、過(guò)程、模板、代碼目錄、設(shè)計(jì)原則等內(nèi)容;最后部分以一個(gè)項(xiàng)目為例講述基于DDD的微服務(wù)設(shè)計(jì)過(guò)程。
本文采用DDD(領(lǐng)域驅(qū)動(dòng)設(shè)計(jì))作為微服務(wù)設(shè)計(jì)指導(dǎo)思想,通過(guò)事件風(fēng)暴建立領(lǐng)域模型,合理劃分領(lǐng)域邏輯和物理邊界,建立領(lǐng)域?qū)ο蠹胺?wù)矩陣和服務(wù)架構(gòu)圖,定義符合DDD分層架構(gòu)思想的代碼結(jié)構(gòu)模型,保證業(yè)務(wù)模型與代碼模型的一致性。通過(guò)上述設(shè)計(jì)思想、方法和過(guò)程,指導(dǎo)團(tuán)隊(duì)按照DDD設(shè)計(jì)思想完成微服務(wù)設(shè)計(jì)和開(kāi)發(fā)。
通過(guò)領(lǐng)域模型和DDD的分層思想,屏蔽外部變化對(duì)領(lǐng)域邏輯的影響,確保交付的軟件產(chǎn)品是邊界清晰的微服務(wù),而不是內(nèi)部邊界依然混亂的小單體。在需求和設(shè)計(jì)變化時(shí),可以輕松的完成微服務(wù)的開(kāi)發(fā)、拆分和組合,確保微服務(wù)不易受外部變化的影響,并穩(wěn)定運(yùn)行。
本文適用于按照DDD設(shè)計(jì)方法進(jìn)行微服務(wù)設(shè)計(jì)和開(kāi)發(fā)的項(xiàng)目及相關(guān)人員。
以下情況不適用:
1、“我們的目標(biāo)是按期蓋出一棟大樓來(lái),不要跟我提什么方法,有這啰嗦的時(shí)間,還不如抓緊點(diǎn)時(shí)間搬磚,把樓給我快點(diǎn)蓋好!”。
2、“我的工作就是讓軟件運(yùn)行起來(lái),能工作一切就OK!我不需要那么多約束,什么設(shè)計(jì)方法、擴(kuò)展性、業(yè)務(wù)變化、領(lǐng)域模型、響應(yīng)能力與我無(wú)關(guān)。別耽誤工期啦!先上線再說(shuō)!”。
3、“好的軟件是自己演進(jìn)出來(lái)的,我們不需要設(shè)計(jì)!”。
哈哈,開(kāi)個(gè)玩笑啦!其實(shí)設(shè)計(jì)不會(huì)花太多時(shí)間的!
不耽誤大家時(shí)間了,言歸正傳。
DDD分層架構(gòu)包括:展現(xiàn)層、應(yīng)用層、領(lǐng)域?qū)雍突A(chǔ)層。
DDD分層架構(gòu)各層職能如下:
1、 展現(xiàn)層
展現(xiàn)層負(fù)責(zé)向用戶(hù)顯示信息和解釋用戶(hù)指令。
2、 應(yīng)用層
應(yīng)用層是很薄的一層,主要面向用戶(hù)用例操作,協(xié)調(diào)和指揮領(lǐng)域?qū)ο髞?lái)完成業(yè)務(wù)邏輯。應(yīng)用層也是與其他系統(tǒng)的應(yīng)用層進(jìn)行交互的必要渠道。應(yīng)用層服務(wù)盡量簡(jiǎn)單,它不包含業(yè)務(wù)規(guī)則或知識(shí),只為下一層的領(lǐng)域?qū)ο髤f(xié)調(diào)任務(wù),使它們互相協(xié)作。應(yīng)用層還可進(jìn)行安全認(rèn)證、權(quán)限校驗(yàn)、分布式和持久化事務(wù)控制或向外部應(yīng)用發(fā)送基于事件的消息等。
3、 領(lǐng)域?qū)?/p>
領(lǐng)域?qū)邮擒浖暮诵乃冢鼘?shí)現(xiàn)全部業(yè)務(wù)邏輯并且通過(guò)各種校驗(yàn)手段保證業(yè)務(wù)正確性。它包含業(yè)務(wù)所涉及的領(lǐng)域?qū)ο螅▽?shí)體、值對(duì)象)、領(lǐng)域服務(wù)以及它們之間的關(guān)系。它負(fù)責(zé)表達(dá)業(yè)務(wù)概念、業(yè)務(wù)狀態(tài)以及業(yè)務(wù)規(guī)則,具體表現(xiàn)形式就是領(lǐng)域模型。
4、 基礎(chǔ)層
基礎(chǔ)層為各層提供通用的技術(shù)能力,包括:為應(yīng)用層傳遞消息、提供API管理,為領(lǐng)域?qū)犹峁?shù)據(jù)庫(kù)持久化機(jī)制。它還能通過(guò)技術(shù)框架來(lái)支持各層之間的交互。
微服務(wù)內(nèi)有Facade接口、應(yīng)用服務(wù)、領(lǐng)域服務(wù)和基礎(chǔ)服務(wù),各層服務(wù)協(xié)同配合,為外部提供服務(wù)。
1、 接口服務(wù)
接口服務(wù)位于用戶(hù)接口層,用于處理用戶(hù)發(fā)送的Restful請(qǐng)求和解析用戶(hù)輸入的配置文件等,并將信息傳遞給應(yīng)用層。
2、 應(yīng)用服務(wù)
應(yīng)用服務(wù)位于應(yīng)用層。用來(lái)表述應(yīng)用和用戶(hù)行為,負(fù)責(zé)服務(wù)的組合、編排和轉(zhuǎn)發(fā),負(fù)責(zé)處理業(yè)務(wù)用例的執(zhí)行順序以及結(jié)果的拼裝。
應(yīng)用層的服務(wù)包括應(yīng)用服務(wù)和領(lǐng)域事件相關(guān)服務(wù)。
應(yīng)用服務(wù)可對(duì)微服務(wù)內(nèi)的領(lǐng)域服務(wù)以及微服務(wù)外的應(yīng)用服務(wù)進(jìn)行組合和編排,或者對(duì)基礎(chǔ)層如文件、緩存等數(shù)據(jù)直接操作形成應(yīng)用服務(wù),對(duì)外提供粗粒度的服務(wù)。
領(lǐng)域事件服務(wù)包括兩類(lèi):領(lǐng)域事件的發(fā)布和訂閱。通過(guò)事件總線和消息隊(duì)列實(shí)現(xiàn)異步數(shù)據(jù)傳輸,實(shí)現(xiàn)微服務(wù)之間的解耦。
3、 領(lǐng)域服務(wù)
領(lǐng)域服務(wù)位于領(lǐng)域?qū)樱瑸橥瓿深I(lǐng)域中跨實(shí)體或值對(duì)象的操作轉(zhuǎn)換而封裝的服務(wù),領(lǐng)域服務(wù)以與實(shí)體和值對(duì)象相同的方式參與實(shí)施過(guò)程。
領(lǐng)域服務(wù)對(duì)同一個(gè)實(shí)體的一個(gè)或多個(gè)方法進(jìn)行組合和封裝,或?qū)Χ鄠€(gè)不同實(shí)體的操作進(jìn)行組合或編排,對(duì)外暴露成領(lǐng)域服務(wù)。領(lǐng)域服務(wù)封裝了核心的業(yè)務(wù)邏輯。實(shí)體自身的行為在實(shí)體類(lèi)內(nèi)部實(shí)現(xiàn),向上封裝成領(lǐng)域服務(wù)暴露。
為隱藏領(lǐng)域?qū)拥臉I(yè)務(wù)邏輯實(shí)現(xiàn),所有領(lǐng)域方法和服務(wù)等均須通過(guò)領(lǐng)域服務(wù)對(duì)外暴露。
為實(shí)現(xiàn)微服務(wù)內(nèi)聚合之間的解耦,原則上禁止跨聚合的領(lǐng)域服務(wù)調(diào)用和跨聚合的數(shù)據(jù)相互關(guān)聯(lián)。
4、 基礎(chǔ)服務(wù)
基礎(chǔ)服務(wù)位于基礎(chǔ)層。為各層提供資源服務(wù)(如數(shù)據(jù)庫(kù)、緩存等),實(shí)現(xiàn)各層的解耦,降低外部資源變化對(duì)業(yè)務(wù)邏輯的影響。
基礎(chǔ)服務(wù)主要為倉(cāng)儲(chǔ)服務(wù),通過(guò)依賴(lài)反轉(zhuǎn)的方式為各層提供基礎(chǔ)資源服務(wù),領(lǐng)域服務(wù)和應(yīng)用服務(wù)調(diào)用倉(cāng)儲(chǔ)服務(wù)接口,利用倉(cāng)儲(chǔ)實(shí)現(xiàn)持久化數(shù)據(jù)對(duì)象或直接訪問(wèn)基礎(chǔ)資源。
1. 前端應(yīng)用與微服務(wù)
微服務(wù)中的應(yīng)用服務(wù)通過(guò)用戶(hù)接口層組裝和數(shù)據(jù)轉(zhuǎn)換后,發(fā)布在API網(wǎng)關(guān),為前端應(yīng)用提供數(shù)據(jù)展示服務(wù)。
2. 微服務(wù)與外部應(yīng)用
跨微服務(wù)數(shù)據(jù)處理時(shí),對(duì)實(shí)時(shí)性要求高的場(chǎng)景,可選擇直接調(diào)用應(yīng)用服務(wù)的方式(新增和修改類(lèi)型操作需關(guān)注事務(wù)一致性)。對(duì)實(shí)時(shí)性要求不高的場(chǎng)景,可選擇異步化的領(lǐng)域事件驅(qū)動(dòng)機(jī)制(最終數(shù)據(jù)一致性)。
DDD分層架構(gòu)中數(shù)據(jù)對(duì)象轉(zhuǎn)換的過(guò)程如下圖。
應(yīng)用服務(wù)通過(guò)數(shù)據(jù)傳輸對(duì)象(DTO)完成外部數(shù)據(jù)交換。領(lǐng)域?qū)油ㄟ^(guò)領(lǐng)域?qū)ο螅―O)作為領(lǐng)域?qū)嶓w和值對(duì)象的數(shù)據(jù)和行為載體。基礎(chǔ)層利用持久化對(duì)象(PO)完成數(shù)據(jù)庫(kù)的交換。
DTO與VO通過(guò)Restful協(xié)議實(shí)現(xiàn)JSON格式和對(duì)象轉(zhuǎn)換。
前端應(yīng)用與應(yīng)用層之間DTO與DO的轉(zhuǎn)換發(fā)生在用戶(hù)接口層。如微服務(wù)內(nèi)應(yīng)用服務(wù)需調(diào)用外部微服務(wù)的應(yīng)用服務(wù),則DTO的組裝和DTO與DO的轉(zhuǎn)換發(fā)生在應(yīng)用層。
領(lǐng)域?qū)覦O與PO的轉(zhuǎn)換發(fā)生在基礎(chǔ)層。
領(lǐng)域事件是領(lǐng)域模型中非常重要的部分,用來(lái)表示領(lǐng)域中發(fā)生的事件。一個(gè)領(lǐng)域事件將導(dǎo)致進(jìn)一步的業(yè)務(wù)操作,有助于形成完整的業(yè)務(wù)閉環(huán)。領(lǐng)域事件主要用于解耦微服務(wù),各個(gè)微服務(wù)之間不再是強(qiáng)一致性,而是基于事件的最終一致性。
微服務(wù)內(nèi)的領(lǐng)域事件可以通過(guò)事件總線或利用應(yīng)用服務(wù)實(shí)現(xiàn)不同聚合之間的業(yè)務(wù)協(xié)同。當(dāng)微服務(wù)內(nèi)發(fā)生領(lǐng)域事件時(shí),由于大部分事件的集成發(fā)生在同一個(gè)線程內(nèi),不一定需要引入消息中間件。但一個(gè)事件如果同時(shí)更新多個(gè)聚合數(shù)據(jù),按照DDD“一個(gè)事務(wù)只更新一個(gè)聚合根”的原則,可以考慮引入消息中間件,通過(guò)異步化的方式,對(duì)微服務(wù)內(nèi)不同的聚合根采用不同的事務(wù)。
微服務(wù)之間的數(shù)據(jù)交互方式通常有兩種:應(yīng)用服務(wù)調(diào)用和領(lǐng)域事件驅(qū)動(dòng)機(jī)制。
領(lǐng)域事件驅(qū)動(dòng)機(jī)制更多的用于不同微服務(wù)之間的集成,實(shí)現(xiàn)微服務(wù)之間的解耦。事件庫(kù)(表)可以用于微服務(wù)之間的數(shù)據(jù)對(duì)賬,在應(yīng)用、網(wǎng)絡(luò)等出現(xiàn)問(wèn)題后,可以實(shí)現(xiàn)源和目的端的數(shù)據(jù)比對(duì),在數(shù)據(jù)暫時(shí)不一致的情況下仍可根據(jù)這些數(shù)據(jù)完成后續(xù)業(yè)務(wù)處理流程,保證微服務(wù)之間數(shù)據(jù)的最終一致性。
應(yīng)用服務(wù)調(diào)用方式通常應(yīng)用于實(shí)時(shí)性要求高的業(yè)務(wù)場(chǎng)景,但一旦涉及到跨微服務(wù)的數(shù)據(jù)修改,將會(huì)增加分布式事務(wù)控制成本,影響系統(tǒng)性能,微服務(wù)之間的耦合度也會(huì)變高。
事件總線位于基礎(chǔ)層,為應(yīng)用層和領(lǐng)域?qū)臃?wù)提供事件消息接收和分發(fā)等服務(wù)。其大致流程如下:
1. 服務(wù)觸發(fā)并發(fā)布事件。
2. 事件總線事件分發(fā)。
1) 如果是微服務(wù)內(nèi)的訂閱者(微服務(wù)內(nèi)的其它聚合),則直接分發(fā)到指定訂閱者。
2) 如果是微服務(wù)外的訂閱者,則事件消息先保存到事件庫(kù)(表)并異步發(fā)送到消息中間件。
3) 如果同時(shí)存在微服務(wù)內(nèi)和外訂閱者,則分發(fā)到內(nèi)部訂閱者,并將事件消息保存到事件庫(kù)(表)并異步發(fā)送到消息中間件。
為了保證事務(wù)的一致性,事件表可以共享業(yè)務(wù)數(shù)據(jù)庫(kù)。也可以采用多個(gè)微服務(wù)共享事件庫(kù)的方式。當(dāng)業(yè)務(wù)操作和事件發(fā)布操作跨數(shù)據(jù)庫(kù)時(shí),須保證業(yè)務(wù)操作和事件發(fā)布操作數(shù)據(jù)的強(qiáng)一致性。
事件數(shù)據(jù)的持久化存儲(chǔ)可以有兩種方案,在項(xiàng)目實(shí)施過(guò)程中根據(jù)具體場(chǎng)景選擇最佳方案。
1、事件數(shù)據(jù)保存到微服務(wù)所在業(yè)務(wù)數(shù)據(jù)庫(kù)的事件表中,利用本地事務(wù)保證業(yè)務(wù)操作和事件發(fā)布操作的強(qiáng)一致性。
2、事件數(shù)據(jù)保存到多個(gè)微服務(wù)共享的事件庫(kù)中。需要注意的一點(diǎn)是:這時(shí)業(yè)務(wù)操作和事件發(fā)布操作會(huì)跨數(shù)據(jù)庫(kù)操作,須保證事務(wù)的強(qiáng)一致性(如分布式事務(wù)機(jī)制)。
事件數(shù)據(jù)的持久化可以保證數(shù)據(jù)的完整性,基于這些數(shù)據(jù)可以完成跨微服務(wù)數(shù)據(jù)的對(duì)賬操作。
本階段主要完成領(lǐng)域模型設(shè)計(jì)。
基于DDD的微服務(wù)設(shè)計(jì)通常采用事件風(fēng)暴方法。通過(guò)事件風(fēng)暴完成領(lǐng)域模型設(shè)計(jì),劃分出微服務(wù)邏輯邊界和物理邊界,定義領(lǐng)域模型中的領(lǐng)域?qū)ο?,指?dǎo)微服務(wù)設(shè)計(jì)和開(kāi)發(fā)。
事件風(fēng)暴通常包括產(chǎn)品愿景、場(chǎng)景分析、領(lǐng)域建模和領(lǐng)域?qū)ο蠛头?wù)矩陣等過(guò)程。本文不對(duì)事件風(fēng)暴詳細(xì)方法做深入描述,如感興趣可查閱相關(guān)資料。
1、產(chǎn)品愿景
產(chǎn)品愿景是對(duì)產(chǎn)品的頂層價(jià)值設(shè)計(jì),對(duì)產(chǎn)品目標(biāo)用戶(hù)、核心價(jià)值、差異化競(jìng)爭(zhēng)點(diǎn)等信息達(dá)成一致,避免產(chǎn)品偏離方向。
建議參與角色:業(yè)務(wù)需求方、產(chǎn)品經(jīng)理和開(kāi)發(fā)組長(zhǎng)。
2、場(chǎng)景分析
場(chǎng)景分析是從用戶(hù)視角出發(fā),探索業(yè)務(wù)領(lǐng)域中的典型場(chǎng)景,產(chǎn)出領(lǐng)域中需要支撐的場(chǎng)景分類(lèi)、用例操作以及不同子域之間的依賴(lài)關(guān)系,用以支撐領(lǐng)域建模。
建議參與角色:產(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、開(kāi)發(fā)組長(zhǎng)和測(cè)試組長(zhǎng)。
3、領(lǐng)域建模
領(lǐng)域建模是通過(guò)對(duì)業(yè)務(wù)和問(wèn)題域進(jìn)行分析,建立領(lǐng)域模型,向上通過(guò)限界上下文指導(dǎo)微服務(wù)邊界設(shè)計(jì),向下通過(guò)聚合指導(dǎo)實(shí)體的對(duì)象設(shè)計(jì)。
建議參與角色:領(lǐng)域?qū)<?、產(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、開(kāi)發(fā)組長(zhǎng)和測(cè)試組長(zhǎng)。
4、微服務(wù)拆分和設(shè)計(jì)
結(jié)合業(yè)務(wù)限界上下文與技術(shù)因素,對(duì)服務(wù)的粒度、分層、邊界劃分、依賴(lài)關(guān)系和集成關(guān)系進(jìn)行梳理,完成微服務(wù)拆分和設(shè)計(jì)。
微服務(wù)設(shè)計(jì)應(yīng)綜合考慮業(yè)務(wù)職責(zé)單一、敏態(tài)與穩(wěn)態(tài)業(yè)務(wù)分離、非功能性需求(如彈性伸縮要求、安全性等要求)、團(tuán)隊(duì)組織和溝通效率、軟件包大小以及技術(shù)異構(gòu)等因素。
建議參與角色:產(chǎn)品經(jīng)理、需求分析人員、架構(gòu)師、開(kāi)發(fā)組長(zhǎng)和測(cè)試組長(zhǎng)。
本階段完成領(lǐng)域?qū)ο蠹胺?wù)矩陣文檔以及微服務(wù)代碼模型設(shè)計(jì)。
1、領(lǐng)域?qū)ο蠹胺?wù)矩陣
在微服務(wù)拆分完成后,根據(jù)事件風(fēng)暴過(guò)程領(lǐng)域?qū)ο蠛完P(guān)系,對(duì)微服務(wù)內(nèi)聚合、實(shí)體、值對(duì)象、倉(cāng)儲(chǔ)、事件、應(yīng)用服務(wù)、領(lǐng)域服務(wù)等領(lǐng)域?qū)ο笠约案鲗?duì)象之間的依賴(lài)關(guān)系進(jìn)行梳理,確定各對(duì)象在分層架構(gòu)中的位置和依賴(lài)關(guān)系,建立領(lǐng)域?qū)ο蠓謱蛹軜?gòu)視圖,為每個(gè)領(lǐng)域?qū)ο蠼⑴c代碼模型對(duì)象的一一映射。
建議參與角色:架構(gòu)師和開(kāi)發(fā)組長(zhǎng)。
2、微服務(wù)代碼模型
根據(jù)領(lǐng)域?qū)ο笤贒DD分層架構(gòu)中所在的層、領(lǐng)域類(lèi)型、與代碼對(duì)象的映射關(guān)系,定義領(lǐng)域?qū)ο笤谖⒎?wù)代碼模型中的包、類(lèi)和方法名稱(chēng)等,設(shè)計(jì)微服務(wù)工程的代碼層級(jí)和代碼結(jié)構(gòu),明確各層間的調(diào)用關(guān)系。
建議參與角色:架構(gòu)師和開(kāi)發(fā)組長(zhǎng)。
領(lǐng)域?qū)ο蠹胺?wù)矩陣主要用來(lái)記錄事件風(fēng)暴和微服務(wù)設(shè)計(jì)過(guò)程中產(chǎn)出的領(lǐng)域?qū)ο髮傩?,如:各領(lǐng)域?qū)ο笤贒DD分層架構(gòu)中的位置、屬性、依賴(lài)關(guān)系以及與代碼對(duì)象的映射關(guān)系等。通過(guò)建立領(lǐng)域?qū)ο笈c代碼對(duì)象的映射關(guān)系,可指導(dǎo)軟件開(kāi)發(fā)人員準(zhǔn)確無(wú)誤的按照設(shè)計(jì)文檔完成微服務(wù)開(kāi)發(fā)。
以下為領(lǐng)域?qū)ο蠹胺?wù)矩陣樣例(部分?jǐn)?shù)據(jù),僅供參考)。
各欄說(shuō)明如下:
層:定義領(lǐng)域?qū)ο笪挥贒DD分層架構(gòu)中的哪一層。如:接口層、應(yīng)用層、領(lǐng)域?qū)右约盎A(chǔ)層等。
聚合:在事件風(fēng)暴過(guò)程中將關(guān)聯(lián)緊密的實(shí)體和值對(duì)象等組合形成聚合。本欄說(shuō)明聚合名稱(chēng)。
領(lǐng)域?qū)ο竺Q(chēng):領(lǐng)域模型中領(lǐng)域?qū)ο蟮木唧w名稱(chēng)。如:“請(qǐng)假審批已通過(guò)”是類(lèi)型為“事件”的領(lǐng)域?qū)ο?;“?qǐng)假單”是領(lǐng)域類(lèi)型為“實(shí)體”的領(lǐng)域?qū)ο蟆?/p>
領(lǐng)域類(lèi)型:在領(lǐng)域模型中根據(jù)DDD知識(shí)域定義的領(lǐng)域?qū)ο蟮念?lèi)型,如:限界上下文、聚合、聚合根(實(shí)體)、實(shí)體、值對(duì)象、事件、命令、應(yīng)用服務(wù)、領(lǐng)域服務(wù)和倉(cāng)儲(chǔ)服務(wù)等。
依賴(lài)對(duì)象名稱(chēng):根據(jù)業(yè)務(wù)對(duì)象依賴(lài)或分層調(diào)用依賴(lài)關(guān)系建立的領(lǐng)域?qū)ο蟮囊蕾?lài)關(guān)系(如服務(wù)調(diào)用依賴(lài)、關(guān)聯(lián)對(duì)象聚合等)。本欄說(shuō)明領(lǐng)域?qū)ο笮枰蕾?lài)的其他領(lǐng)域?qū)ο?,如上層服?wù)在組合和編排過(guò)程中對(duì)下層服務(wù)的調(diào)用依賴(lài)、實(shí)體之間或者實(shí)體與值對(duì)象在聚合內(nèi)的依賴(lài)等。
包名:代碼模型中的包名,本欄說(shuō)明領(lǐng)域?qū)ο笏诘能浖?/p>
類(lèi)名:代碼模型中的類(lèi)名,本欄說(shuō)明領(lǐng)域?qū)ο蟮念?lèi)名。
方法名:代碼模型中的方法名,本欄說(shuō)明領(lǐng)域?qū)ο髮?shí)現(xiàn)或操作的方法名。
微服務(wù)代碼模型最終結(jié)果來(lái)源于領(lǐng)域?qū)ο蠹胺?wù)矩陣。在代碼模型設(shè)計(jì)時(shí)須建立領(lǐng)域?qū)ο蠛痛a對(duì)象的一一映射,保證業(yè)務(wù)模型與代碼模型的一致性,即使不熟悉業(yè)務(wù)的開(kāi)發(fā)人員或者不熟悉代碼的業(yè)務(wù)人員也可以很快定位到代碼位置。
基于DDD的代碼模型包括interfaces、application、domain和infrastructure四個(gè)目錄。
Interfaces(用戶(hù)接口層):本目錄主要存放用戶(hù)接口層代碼。前端應(yīng)用通過(guò)本層向應(yīng)用服務(wù)獲取展現(xiàn)所需的數(shù)據(jù)。本層主要用于處理用戶(hù)發(fā)送的Restful請(qǐng)求和解析用戶(hù)輸入的配置文件等,并將信息傳遞給Application層。主要代碼形態(tài)是數(shù)據(jù)組裝以及Facade接口等。
Application(應(yīng)用層):本目錄主要存放應(yīng)用層代碼。應(yīng)用服務(wù)代碼基于微服務(wù)內(nèi)的領(lǐng)域服務(wù)或微服務(wù)外的應(yīng)用服務(wù)完成服務(wù)編排和組合。為用戶(hù)接口層提供各種應(yīng)用數(shù)據(jù)展現(xiàn)支持。主要代碼形態(tài)是應(yīng)用服務(wù)和領(lǐng)域事件等。
Domain(領(lǐng)域?qū)樱罕灸夸浿饕娣蓬I(lǐng)域?qū)哟a。本層代碼主要實(shí)現(xiàn)核心領(lǐng)域邏輯,其主要代碼形態(tài)是實(shí)體類(lèi)方法和領(lǐng)域服務(wù)等。
Infrastructure(基礎(chǔ)層):本目錄存放基礎(chǔ)層代碼,為其它各層提供通用技術(shù)能力、三方軟件包、配置和基礎(chǔ)資源服務(wù)等。
用戶(hù)接口層代碼模型目錄包括:assembler、dto和facade。
Assembler:實(shí)現(xiàn)DTO與領(lǐng)域?qū)ο笾g的相互轉(zhuǎn)換和數(shù)據(jù)交換。理論上Assembler總是與DTO一同被使用。
Dto:數(shù)據(jù)傳輸?shù)妮d體,內(nèi)部不存在任何業(yè)務(wù)邏輯,通過(guò)DTO把內(nèi)部的領(lǐng)域?qū)ο笈c外界隔離。
Facade:提供較粗粒度的調(diào)用接口,將用戶(hù)請(qǐng)求委派給一個(gè)或多個(gè)應(yīng)用服務(wù)進(jìn)行處理。
應(yīng)用層代碼模型目錄包括:event和service。
Event(事件):事件目錄包括兩個(gè)子目錄:publish和subscribe。publish目錄主要存放微服務(wù)內(nèi)領(lǐng)域事件發(fā)布相關(guān)代碼。subscribe目錄主要存放微服務(wù)內(nèi)聚合之間或外部微服務(wù)領(lǐng)域事件訂閱處理相關(guān)代碼。為了實(shí)現(xiàn)領(lǐng)域事件的統(tǒng)一管理,微服務(wù)內(nèi)所有領(lǐng)域事件(包括應(yīng)用層和領(lǐng)域?qū)邮录┑陌l(fā)布和訂閱處理都統(tǒng)一放在應(yīng)用層。
Service(應(yīng)用服務(wù)):這里的服務(wù)是應(yīng)用服務(wù)。應(yīng)用服務(wù)對(duì)多個(gè)領(lǐng)域服務(wù)或外部應(yīng)用服務(wù)進(jìn)行封裝、編排和組合,對(duì)外提供粗粒度的服務(wù)。
微服務(wù)領(lǐng)域?qū)影ㄒ粋€(gè)或多個(gè)聚合代碼包。標(biāo)準(zhǔn)的聚合代碼模型包括:entity、repository和service三個(gè)子目錄。
Aggregate(聚合):聚合代碼包的根目錄,實(shí)際項(xiàng)目中以實(shí)際業(yè)務(wù)屬性的名稱(chēng)來(lái)命名。聚合定義了領(lǐng)域?qū)ο笾g的關(guān)系和邊界,實(shí)現(xiàn)領(lǐng)域模型的內(nèi)聚。
Entity(實(shí)體):存放實(shí)體(含聚合根、實(shí)體和值對(duì)象)相關(guān)代碼。同一實(shí)體所有相關(guān)的代碼(含對(duì)同一實(shí)體類(lèi)多個(gè)對(duì)象操作的方法,如對(duì)多個(gè)對(duì)象的count等)都放在一個(gè)實(shí)體類(lèi)中。
Service(領(lǐng)域服務(wù)):存放對(duì)多個(gè)不同實(shí)體對(duì)象操作的領(lǐng)域服務(wù)代碼。這部分代碼以領(lǐng)域服務(wù)的形式存在,在設(shè)計(jì)時(shí)一個(gè)領(lǐng)域服務(wù)對(duì)應(yīng)一個(gè)類(lèi)。
Repository(倉(cāng)儲(chǔ)):存放聚合對(duì)應(yīng)的查詢(xún)或持久化領(lǐng)域?qū)ο蟮拇a,通常包括倉(cāng)儲(chǔ)接口和倉(cāng)儲(chǔ)實(shí)現(xiàn)方法。為了方便聚合的拆分和組合,我們?cè)O(shè)定一個(gè)原則:一個(gè)聚合對(duì)應(yīng)一個(gè)倉(cāng)儲(chǔ)。
特別說(shuō)明:按照DDD分層原則,倉(cāng)儲(chǔ)實(shí)現(xiàn)本應(yīng)屬于基礎(chǔ)層代碼,但為了微服務(wù)代碼拆分和重組的便利性,我們把聚合的倉(cāng)儲(chǔ)實(shí)現(xiàn)代碼放到了領(lǐng)域?qū)訉?duì)應(yīng)的聚合代碼包內(nèi)。如果需求或者設(shè)計(jì)發(fā)生變化導(dǎo)致聚合需要拆分或重新組合時(shí),我們可以聚合代碼包為單位,輕松實(shí)現(xiàn)微服務(wù)聚合的拆分和組合。
基礎(chǔ)層代碼模型包括:config和util兩個(gè)子目錄。
Config:主要存放配置相關(guān)代碼。
Util:主要存放平臺(tái)、開(kāi)發(fā)框架、消息、數(shù)據(jù)庫(kù)、緩存、文件、總線、網(wǎng)關(guān)、第三方類(lèi)庫(kù)、通用算法等基礎(chǔ)代碼,可為不同的資源類(lèi)別建立不同的子目錄。
微服務(wù)總目錄結(jié)構(gòu)如下:
微服務(wù)設(shè)計(jì)原則中如高內(nèi)聚低耦合、復(fù)用、單一職責(zé)等原則在此就不贅述了,這里主要強(qiáng)調(diào)以下幾條:
第一條:“要領(lǐng)域驅(qū)動(dòng)設(shè)計(jì),而不是數(shù)據(jù)驅(qū)動(dòng)設(shè)計(jì),也不是界面驅(qū)動(dòng)設(shè)計(jì)”。
微服務(wù)設(shè)計(jì)首先應(yīng)建立領(lǐng)域模型,確定邏輯和物理邊界后,然后才進(jìn)行微服務(wù)邊界拆分,而不是一上來(lái)就定義數(shù)據(jù)庫(kù)表結(jié)構(gòu),也不是界面需要什么,就去調(diào)整領(lǐng)域邏輯代碼。
領(lǐng)域模型和領(lǐng)域服務(wù)應(yīng)具有高度通用性,通過(guò)接口層和應(yīng)用層屏蔽外部變化對(duì)業(yè)務(wù)邏輯的影響,保證核心業(yè)務(wù)功能的穩(wěn)定性。
第二條:“要邊界清晰的微服務(wù),而不是泥球小單體”。
微服務(wù)完成開(kāi)發(fā)后其功能和代碼也不是一成不變的。隨著需求或設(shè)計(jì)變化,微服務(wù)內(nèi)的代碼也會(huì)分分合合。邏輯邊界清晰的微服務(wù),可快速實(shí)現(xiàn)微服務(wù)代碼的拆分和組合。DDD思想中的邏輯邊界和分層設(shè)計(jì)也是為微服務(wù)各種可能的分分合合做準(zhǔn)備的。
微服務(wù)內(nèi)聚合與聚合之間的領(lǐng)域服務(wù)以及數(shù)據(jù)原則上禁止相互產(chǎn)生依賴(lài)。如有必要可通過(guò)上層的應(yīng)用服務(wù)編排或者事件驅(qū)動(dòng)機(jī)制實(shí)現(xiàn)聚合之間的解耦,以利于聚合之間的組合和拆分。
第三條:“要職能清晰的分層,而不是什么都放的大籮筐”。
分層架構(gòu)中各層職能定位清晰,且都只能與其下方的層發(fā)生依賴(lài),也就是說(shuō)只能從外層調(diào)用內(nèi)層服務(wù),內(nèi)層服務(wù)通過(guò)封裝、組合或編排對(duì)外逐層暴露,服務(wù)粒度由細(xì)到粗。
應(yīng)用層負(fù)責(zé)服務(wù)的編排和組合,領(lǐng)域?qū)迂?fù)責(zé)領(lǐng)域業(yè)務(wù)邏輯的實(shí)現(xiàn),基礎(chǔ)層為各層提供資源服務(wù)。
第四條:“要做自己能hold住的微服務(wù),而不是過(guò)度拆分的微服務(wù)”。
微服務(wù)的過(guò)度拆分必然會(huì)帶來(lái)軟件維護(hù)成本的上升,如:集成成本、運(yùn)維成本以及監(jiān)控和定位問(wèn)題的成本。企業(yè)轉(zhuǎn)型過(guò)程中很難短時(shí)間內(nèi)提升這些能力,如果項(xiàng)目團(tuán)隊(duì)不具備這些能力,將很難hold住這些過(guò)細(xì)的微服務(wù)。而如果我們?cè)谖⒎?wù)設(shè)計(jì)之初就已經(jīng)定義好了微服務(wù)內(nèi)的邏輯邊界,項(xiàng)目初期我們可以盡可能少的拆分出過(guò)細(xì)的微服務(wù),隨著技術(shù)的積累和時(shí)間的推移,當(dāng)我們具有這些能力后,由于微服務(wù)內(nèi)有清晰的邏輯邊界,這時(shí)就可以隨時(shí)根據(jù)需要輕松的拆分或組合出新的微服務(wù)。
微服務(wù)的設(shè)計(jì)先從領(lǐng)域建模開(kāi)始,領(lǐng)域模型是微服務(wù)設(shè)計(jì)的核心,微服務(wù)是領(lǐng)域建模的結(jié)果。在微服務(wù)設(shè)計(jì)之前,請(qǐng)先判斷你的業(yè)務(wù)是否聚焦在領(lǐng)域和領(lǐng)域邏輯。
實(shí)際在做系統(tǒng)設(shè)計(jì)時(shí)我們可能面臨各種不同的情形,如從傳統(tǒng)單體拆分為多個(gè)微服務(wù),也可能是一個(gè)全新領(lǐng)域的微服務(wù)設(shè)計(jì)(如創(chuàng)業(yè)中的應(yīng)用),抑或是將一個(gè)單體中面臨問(wèn)題或性能瓶頸的模塊拆分為微服務(wù)而其余功能仍為單體的情況。
下面分幾類(lèi)不同場(chǎng)景說(shuō)明如何進(jìn)行微服務(wù)和領(lǐng)域模型設(shè)計(jì)。
新建系統(tǒng)會(huì)遇到復(fù)雜和簡(jiǎn)單領(lǐng)域兩種場(chǎng)景,兩者的領(lǐng)域建模過(guò)程也會(huì)有所差別。
1、 簡(jiǎn)單領(lǐng)域的建模
對(duì)于簡(jiǎn)單的業(yè)務(wù)領(lǐng)域,一個(gè)領(lǐng)域可能就是一個(gè)小的子域。領(lǐng)域建模過(guò)程相對(duì)簡(jiǎn)單,根據(jù)事件風(fēng)暴可以分解出事件、命令、實(shí)體、聚合和限界上下文等,根據(jù)領(lǐng)域模型和微服務(wù)拆分原則設(shè)計(jì)出微服務(wù)即可。
2、 復(fù)雜領(lǐng)域的建模
對(duì)于復(fù)雜的業(yè)務(wù)領(lǐng)域,領(lǐng)域可能還需要拆分為子域,甚至子域還會(huì)進(jìn)一步拆分,如:保險(xiǎn)領(lǐng)域可以拆分為承保、理賠、收付費(fèi)和再保等子域,承保子域還可以再拆分為投保、保單管理等子子域。對(duì)于這種復(fù)雜的領(lǐng)域模型,是無(wú)法通過(guò)一個(gè)事件風(fēng)暴完成領(lǐng)域建模的,即使能完成,其工程量也是非常浩大,效果也不一定好。
對(duì)于這種復(fù)雜的領(lǐng)域,我們可以分三階段來(lái)完成領(lǐng)域模型和微服務(wù)設(shè)計(jì)。
1) 拆分子域建立領(lǐng)域模型
根據(jù)業(yè)務(wù)特點(diǎn)考慮流程節(jié)點(diǎn)或功能模塊等邊界因素(微服務(wù)最終的拆分結(jié)果很多時(shí)候跟這些邊界因素有一定的相關(guān)性),按領(lǐng)域逐級(jí)分解為大小合適的子域,針對(duì)子域進(jìn)行事件風(fēng)暴,記錄領(lǐng)域?qū)ο蟆⒕酆虾拖藿缟舷挛?,初步確定各級(jí)子域的領(lǐng)域模型。
2) 領(lǐng)域模型微調(diào)
梳理領(lǐng)域內(nèi)所有子域的領(lǐng)域模型,對(duì)各子域模型進(jìn)行微調(diào),這個(gè)過(guò)程重點(diǎn)考慮不同限界上下文內(nèi)聚合的重新組合,同步需要考慮子域、限界上下文以及聚合之間的邊界、服務(wù)以及事件之間的依賴(lài)關(guān)系,確定最終的領(lǐng)域模型。
3) 微服務(wù)設(shè)計(jì)和拆分
根據(jù)領(lǐng)域的限界上下文和微服務(wù)的拆分原則,完成微服務(wù)的拆分和設(shè)計(jì)。
如果一個(gè)單體遺留系統(tǒng),只是將面臨問(wèn)題或性能瓶頸的模塊拆分為微服務(wù),而其余功能仍為單體。我們只需要將這些特定功能領(lǐng)域理解為一個(gè)簡(jiǎn)單的子領(lǐng)域,按照簡(jiǎn)單領(lǐng)域建模方式進(jìn)行領(lǐng)域模型的設(shè)計(jì)即可。但在新微服務(wù)設(shè)計(jì)中需要考慮新老系統(tǒng)之間的服務(wù)協(xié)議,必要時(shí)引入防腐層。
雖然有些業(yè)務(wù)領(lǐng)域在事件風(fēng)暴后發(fā)現(xiàn)無(wú)法建立領(lǐng)域模型,如數(shù)據(jù)處理或分析類(lèi)場(chǎng)景,但本文所述的分層架構(gòu)模型、服務(wù)之間規(guī)約和代碼目錄結(jié)構(gòu)在微服務(wù)設(shè)計(jì)和開(kāi)發(fā)中仍然是通用的。
為了更好的理解DDD的設(shè)計(jì)思想和過(guò)程,我們用一個(gè)場(chǎng)景簡(jiǎn)單但基本涵蓋DDD設(shè)計(jì)思想的項(xiàng)目來(lái)說(shuō)明微服務(wù)設(shè)計(jì)和開(kāi)發(fā)過(guò)程。
項(xiàng)目主要目標(biāo)是實(shí)現(xiàn)在線請(qǐng)假和考勤管理。基本功能包括:請(qǐng)假、考勤以及人員管理等。
請(qǐng)假:請(qǐng)假人填寫(xiě)請(qǐng)假單提交審批,根據(jù)請(qǐng)假人身份和請(qǐng)假天數(shù)進(jìn)行校驗(yàn),根據(jù)審批規(guī)則逐級(jí)遞交審批,核批通過(guò)則完成審批。
考勤:根據(jù)考勤規(guī)則,剔除請(qǐng)假數(shù)據(jù)后,對(duì)員工考勤數(shù)據(jù)進(jìn)行校驗(yàn),輸出考勤統(tǒng)計(jì)表。
人員管理:維護(hù)人員基本信息和上下級(jí)關(guān)系。
由于項(xiàng)目目標(biāo)基本明確,我們?cè)谑录L(fēng)暴過(guò)程中裁剪了產(chǎn)品愿景,直接從用戶(hù)旅程和場(chǎng)景分析開(kāi)始。
1. 場(chǎng)景分析
場(chǎng)景分析是一個(gè)發(fā)散的過(guò)程。根據(jù)不同角色的旅程和場(chǎng)景分析,盡可能全面的梳理從前端操作到后端業(yè)務(wù)邏輯發(fā)生的所有操作、命令、領(lǐng)域事件以及外部依賴(lài)關(guān)系等信息(如下圖),如:請(qǐng)假人員會(huì)執(zhí)行創(chuàng)建請(qǐng)假信息操作命令,審批人員會(huì)執(zhí)行審批操作,請(qǐng)假審批通過(guò)后會(huì)產(chǎn)生領(lǐng)域事件,通知郵件系統(tǒng)反饋請(qǐng)假人員結(jié)果,并將請(qǐng)假數(shù)據(jù)發(fā)送到考勤以便核銷(xiāo)等。在記錄這些領(lǐng)域?qū)ο蟮耐瑫r(shí),我們也會(huì)標(biāo)記各對(duì)象在DDD中的層和對(duì)象類(lèi)型等屬性,如:應(yīng)用服務(wù)、領(lǐng)域服務(wù)、事件和命令等類(lèi)型。
2. 領(lǐng)域建模
領(lǐng)域建模是一個(gè)收斂的過(guò)程。這個(gè)收斂過(guò)程分三步:第一步根據(jù)場(chǎng)景分析中的操作集合定義領(lǐng)域?qū)嶓w;第二步根據(jù)領(lǐng)域?qū)嶓w業(yè)務(wù)關(guān)聯(lián)性,定義聚合;第三步根據(jù)業(yè)務(wù)及語(yǔ)義邊界等因素,定義限界上下文。
1) 定義領(lǐng)域?qū)嶓w
在場(chǎng)景分析過(guò)程中梳理完操作、命令、領(lǐng)域事件以及外部依賴(lài)關(guān)系等領(lǐng)域?qū)ο蠛?。分析這些操作應(yīng)由什么實(shí)體發(fā)起或產(chǎn)生,從而定義領(lǐng)域?qū)嶓w對(duì)象,并將這些操作與實(shí)體進(jìn)行關(guān)聯(lián)。
在請(qǐng)假場(chǎng)景中,經(jīng)分析需要有請(qǐng)假單實(shí)體對(duì)象,請(qǐng)假單實(shí)體有創(chuàng)建請(qǐng)假信息以及修改請(qǐng)假信息等操作。
2) 定義聚合
將業(yè)務(wù)緊密相關(guān)的實(shí)體進(jìn)行組合形成聚合,同時(shí)確定聚合中的聚合根、值對(duì)象和實(shí)體。經(jīng)分析項(xiàng)目最終形成三個(gè)聚合:人員管理、請(qǐng)假和考勤。在請(qǐng)假聚合中有請(qǐng)假單、審批軌跡、審批規(guī)則等實(shí)體,其中請(qǐng)假單是聚合根,審批軌跡是請(qǐng)假單的值對(duì)象,審批規(guī)則是輔助實(shí)體。
聚合內(nèi)須保證業(yè)務(wù)操作的事務(wù)性,高度內(nèi)聚的實(shí)體對(duì)象可自包含完成本領(lǐng)域功能。聚合是可拆分為微服務(wù)的最小單元。在同一限界上下文內(nèi)多個(gè)聚合可以組合為一個(gè)微服務(wù)。如有必要,也可以將某一個(gè)聚合獨(dú)立為微服務(wù)。
3) 定義限界上下文
根據(jù)領(lǐng)域及語(yǔ)義邊界等因素確定限界上下文,將同一個(gè)語(yǔ)義環(huán)境下的一個(gè)或者多個(gè)聚合放在一個(gè)限界上下文內(nèi)。由于人員管理與請(qǐng)假聚合兩者業(yè)務(wù)關(guān)聯(lián)緊密,共同完成人員請(qǐng)假功能,兩者一起構(gòu)成請(qǐng)假限界上下文,考勤聚合則單獨(dú)形成考勤限界上下文。
3. 微服務(wù)設(shè)計(jì)和拆分
理論上一個(gè)限界上下文可以設(shè)計(jì)為一個(gè)微服務(wù),但還需要綜合考慮多種外部因素,如:職責(zé)單一性、性能差異、版本發(fā)布頻率、團(tuán)隊(duì)溝通效率和技術(shù)異構(gòu)等要素。
由于本項(xiàng)目微服務(wù)設(shè)計(jì)受技術(shù)以及團(tuán)隊(duì)等因素影響相對(duì)較小,主要考慮職責(zé)單一性,因此根據(jù)限界上下文直接拆分為請(qǐng)假和考勤兩個(gè)微服務(wù)。其中請(qǐng)假微服務(wù)包含人員和請(qǐng)假兩個(gè)聚合,考勤微服務(wù)只包含考勤聚合。
將事件風(fēng)暴中產(chǎn)出的領(lǐng)域?qū)ο蟀凑崭髯运诘奈⒎?wù)進(jìn)行分類(lèi),定義每個(gè)領(lǐng)域?qū)ο笤谖⒎?wù)中的層、領(lǐng)域類(lèi)型和依賴(lài)的領(lǐng)域?qū)ο蟮取?/p>
這個(gè)步驟最關(guān)鍵的工作是確定實(shí)體、方法、服務(wù)等領(lǐng)域?qū)ο笤谖⒎?wù)分層架構(gòu)中的位置以及各對(duì)象之間的依賴(lài)關(guān)系,形成服務(wù)矩陣(如下表)。這個(gè)過(guò)程也將在事件風(fēng)暴數(shù)據(jù)的基礎(chǔ)上,進(jìn)一步細(xì)化領(lǐng)域?qū)ο笠约八鼈冎g關(guān)系,并補(bǔ)充事件風(fēng)暴中可能遺漏的細(xì)節(jié)。
確定完各領(lǐng)域?qū)ο蟮膶傩院?,按照代碼模型設(shè)計(jì)各個(gè)領(lǐng)域?qū)ο笤诖a模型中的代碼對(duì)象(包括代碼對(duì)象所在的:包名、類(lèi)名和方法名),建立領(lǐng)域?qū)ο笈c代碼對(duì)象的一一映射關(guān)系。根據(jù)這種映射關(guān)系,相關(guān)人員可快速定位到業(yè)務(wù)邏輯所在的代碼位置。
根據(jù)領(lǐng)域模型中領(lǐng)域?qū)ο髮傩砸约胺?wù)矩陣,畫(huà)出領(lǐng)域?qū)ο蠹胺?wù)架構(gòu)視圖(如下圖)。
這個(gè)視圖可以作為標(biāo)準(zhǔn)的DDD分層領(lǐng)域服務(wù)架構(gòu)視圖模型,應(yīng)用在不同的領(lǐng)域模型中。這個(gè)模型可以清晰的體現(xiàn)微服務(wù)內(nèi)實(shí)體、聚合之間的關(guān)系,各層服務(wù)之間的依賴(lài)關(guān)系以及應(yīng)用層服務(wù)組合和編排的關(guān)系,微服務(wù)之間的服務(wù)調(diào)用以及事件驅(qū)動(dòng)的前后處理邏輯關(guān)系。
在這個(gè)階段,前端的設(shè)計(jì)也可以同步進(jìn)行,在這里我們用到了微前端的設(shè)計(jì)理念,為請(qǐng)假和考勤微服務(wù)分別設(shè)計(jì)了請(qǐng)假和考勤微前端,基于微前端和微服務(wù),形成從前端到后端的業(yè)務(wù)邏輯自包含組件。兩個(gè)微前端之上有一個(gè)集成主頁(yè)面,可根據(jù)頁(yè)面流動(dòng)態(tài)加載請(qǐng)假和考勤的微前端頁(yè)面。(微前端技術(shù)詳見(jiàn):《中臺(tái)微服務(wù)了,那前端呢?》)。
根據(jù)DDD的代碼結(jié)構(gòu)模型和各領(lǐng)域?qū)ο笤谒诘陌㈩?lèi)和方法,定義出請(qǐng)假微服務(wù)的代碼結(jié)構(gòu)模型。
應(yīng)用層代碼結(jié)構(gòu)包括:應(yīng)用服務(wù)以及事件發(fā)布相關(guān)代碼(如下圖)。
領(lǐng)域?qū)哟a結(jié)構(gòu)包括一個(gè)或多個(gè)聚合的實(shí)體類(lèi)以及領(lǐng)域服務(wù)相關(guān)代碼(如下圖)。在本項(xiàng)目中請(qǐng)假微服務(wù)領(lǐng)域?qū)影苏?qǐng)假和人員兩個(gè)聚合。
領(lǐng)域模型中的一個(gè)聚合對(duì)應(yīng)一個(gè)聚合代碼包,如:人員和請(qǐng)假領(lǐng)域邏輯代碼都放在各自的聚合代碼包中,如隨著業(yè)務(wù)發(fā)展,人員管理功能需要從請(qǐng)假微服務(wù)中拆分出來(lái),我們只需要將人員聚合代碼包稍加改造并獨(dú)立部署即可快速發(fā)布為微服務(wù)。
在完成領(lǐng)域模型和代碼模型設(shè)計(jì)后,就可以開(kāi)始詳細(xì)設(shè)計(jì)了,詳細(xì)設(shè)計(jì)主要結(jié)合具體的業(yè)務(wù)功能來(lái)開(kāi)展,主要工作包括:系統(tǒng)界面、數(shù)據(jù)庫(kù)表以及字段、服務(wù)參數(shù)規(guī)約及功能等。
軟件開(kāi)發(fā)人員只需要按照設(shè)計(jì)文檔和功能要求,找到業(yè)務(wù)功能對(duì)應(yīng)的代碼位置,完成代碼開(kāi)發(fā)和服務(wù)編排即可。
完成代碼開(kāi)發(fā)后,由開(kāi)發(fā)人員編寫(xiě)單元測(cè)試用例,基于擋板模擬依賴(lài)對(duì)象完成跨服務(wù)的測(cè)試。單元測(cè)試完成后,在團(tuán)隊(duì)內(nèi)可進(jìn)一步完成微服務(wù)與相應(yīng)微前端的集成和測(cè)試,形成請(qǐng)假和考勤兩個(gè)業(yè)務(wù)組件。
前端主頁(yè)面完成請(qǐng)假和考勤微前端頁(yè)面集成和頁(yè)面流及組件基礎(chǔ)數(shù)據(jù)配置,主頁(yè)面可以按照頁(yè)面流程動(dòng)態(tài)加載請(qǐng)假和考勤微前端頁(yè)面。
最終部署的軟件包包括:請(qǐng)假和考勤兩個(gè)微服務(wù),請(qǐng)假和考勤兩個(gè)微前端,主頁(yè)面一個(gè)共計(jì)五個(gè)。這五個(gè)部署包獨(dú)立開(kāi)發(fā)、獨(dú)立運(yùn)行和獨(dú)立部署。
主頁(yè)面和微前端采用:Vue(前端框架),ElementUI(UI框架-PC),VUX(UI框架-移動(dòng)端)、MPVUE(UI框架-小程序)。
微服務(wù)開(kāi)發(fā)采用:Spring Cloud、Kafka等。
數(shù)據(jù)庫(kù)采用:PostgreSQL。
Event Storming(事件風(fēng)暴):事件風(fēng)暴是一項(xiàng)團(tuán)隊(duì)活動(dòng),旨在通過(guò)領(lǐng)域事件識(shí)別出聚合根,進(jìn)而劃分微服務(wù)的限界上下文。在活動(dòng)中,團(tuán)隊(duì)先通過(guò)頭腦風(fēng)暴的形式羅列出領(lǐng)域中所有的領(lǐng)域事件,整合之后形成最終的領(lǐng)域事件集合,然后對(duì)于每一個(gè)事件,標(biāo)注出導(dǎo)致該事件的命令(Command),再然后為每個(gè)事件標(biāo)注出命令發(fā)起方的角色,命令可以是用戶(hù)發(fā)起,也可以是第三方系統(tǒng)調(diào)用或者是定時(shí)器觸發(fā)等。最后對(duì)事件進(jìn)行分類(lèi)整理出聚合根以及限界上下文。
Entity(實(shí)體):每個(gè)實(shí)體是唯一的,并且可以相當(dāng)長(zhǎng)的一段時(shí)間內(nèi)持續(xù)地變化。我們可以對(duì)實(shí)體做多次修改,故一個(gè)實(shí)體對(duì)象可能和它先前的狀態(tài)大不相同。但是,由于它們擁有相同的身份標(biāo)識(shí),他們依然是同一個(gè)實(shí)體。例如一件商品在電商商品上下文中是一個(gè)實(shí)體,通過(guò)商品中臺(tái)唯一的商品id來(lái)標(biāo)示這個(gè)實(shí)體。
ValueObject(值對(duì)象):值對(duì)象用于度量和描述事物,當(dāng)你只關(guān)心某個(gè)對(duì)象的屬性時(shí),該對(duì)象便可作為一個(gè)值對(duì)象。實(shí)體與值對(duì)象的區(qū)別在于唯一的身份標(biāo)識(shí)和可變性。當(dāng)一個(gè)對(duì)象用于描述一個(gè)事物,但是又沒(méi)有唯一標(biāo)示,那么它就是一個(gè)值對(duì)象。例如商品中的商品類(lèi)別,類(lèi)別就沒(méi)有一個(gè)唯一標(biāo)識(shí),通過(guò)圖書(shū)、服裝等這些值就能明確表示這個(gè)商品類(lèi)別。
Aggregate(聚合):聚合是實(shí)體的升級(jí),是由一組與生俱來(lái)就密切相關(guān)實(shí)體和值對(duì)象組合而成的,整個(gè)組合的最上層實(shí)體就是聚合。
Bounded Context(限界上下文):用來(lái)封裝通用語(yǔ)言和領(lǐng)域?qū)ο螅瑸轭I(lǐng)域提供上下文語(yǔ)境,保證在領(lǐng)域之內(nèi)的一些術(shù)語(yǔ)、業(yè)務(wù)相關(guān)對(duì)象等(通用語(yǔ)言)有一個(gè)確切的含義,沒(méi)有二義性。使團(tuán)隊(duì)所有成員能夠明確地知道什么必須保持一致,什么必須獨(dú)立開(kāi)發(fā)。
聯(lián)系客服