領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)統(tǒng)一過程交付物
一、全局分析階段
1.業(yè)務(wù)流程
梳理問題空間的業(yè)務(wù)需求,獲得用泳道圖表現(xiàn)的業(yè)務(wù)流程:
2.業(yè)務(wù)服務(wù)
根據(jù)業(yè)務(wù)服務(wù)的定義分析業(yè)務(wù)流程,識(shí)別出業(yè)務(wù)服務(wù),并以業(yè)務(wù)服務(wù)圖(參考用例圖)形式表示:
說明:如果采用敏捷方式管理需求,可以將業(yè)務(wù)服務(wù)作為用戶故事的子任務(wù),它不包括前端的交互設(shè)計(jì)和開發(fā)內(nèi)容。
如果需要進(jìn)一步細(xì)化業(yè)務(wù)服務(wù),則需要按照如下格式編寫業(yè)務(wù)服務(wù)規(guī)約:
編寫業(yè)務(wù)服務(wù)規(guī)約時(shí),需要遵循統(tǒng)一語言。
以上內(nèi)容,可以構(gòu)成目標(biāo)系統(tǒng)的需求規(guī)格說明書。
二、架構(gòu)映射階段
1.系統(tǒng)上下文
系統(tǒng)上下文用于呈現(xiàn)目標(biāo)系統(tǒng)的系統(tǒng)邊界,明確目標(biāo)系統(tǒng)與角色、伴生系統(tǒng)之間的關(guān)系。可以通過改進(jìn)的系統(tǒng)上下文圖來表示:
改進(jìn)的系統(tǒng)上下文有效地利用了四個(gè)方位:
- 上方:代表北向,即調(diào)用目標(biāo)系統(tǒng)的伴生系統(tǒng)或模塊
- 下方:代表南向,即目標(biāo)系統(tǒng)調(diào)用的伴生系統(tǒng)或模塊
- 左方:代表使用目標(biāo)系統(tǒng)的所有角色
- 右方:代表互相調(diào)用的伴生系統(tǒng)或模塊
2.限界上下文
識(shí)別限界上下文
運(yùn)用服務(wù)風(fēng)暴法,識(shí)別限界上下文,建立業(yè)務(wù)服務(wù)與限界上下文的映射關(guān)系,并以下圖形式呈現(xiàn)出來:
圖中的菱形代表限界上下文,橢圓形代表業(yè)務(wù)服務(wù)。
確定上下文映射
針對每一個(gè)業(yè)務(wù)服務(wù),通過業(yè)務(wù)服務(wù)規(guī)約繪制服務(wù)序列圖,以確定限界上下文之間的協(xié)作關(guān)系,并驅(qū)動(dòng)出每個(gè)限界上下文的服務(wù)契約。繪制服務(wù)序列圖時(shí),根據(jù)業(yè)務(wù)服務(wù)規(guī)約“成功場景”部分的流程,確定每個(gè)流程步驟需要的領(lǐng)域知識(shí)和領(lǐng)域職責(zé)應(yīng)該由哪一個(gè)限界上下文負(fù)責(zé)。服務(wù)序列圖如下所示:
通過服務(wù)序列圖,既可以明確限界上下文之間的關(guān)系,又可以驅(qū)動(dòng)出每個(gè)限界上下文包括伴生系統(tǒng)的服務(wù)契約(API),同時(shí)還能夠確定協(xié)作模式,包括客戶方-供應(yīng)方模式和發(fā)布者-訂閱者模式。其中,查詢和命令方式屬于客戶方-供應(yīng)方模式,事件方式屬于發(fā)布者-訂閱者模式。服務(wù)契約可以通過下表格式表示:
服務(wù)契約的API定義也可以在Swagger中維護(hù)。
最后,可以通過如下圖示表示限界上下文:
與改進(jìn)的系統(tǒng)上下文圖相似,限界上下文圖也有效地利用了四個(gè)方位:
- 上方:代表北向服務(wù),為當(dāng)前限界上下文對外公開的服務(wù)接口
- 下方:代表南向服務(wù),為當(dāng)前限界上下文調(diào)用上游限界上下文或伴生系統(tǒng)的服務(wù)接口
- 左方:當(dāng)前限界上下文訂閱的事件
- 右方:當(dāng)前限界上下文發(fā)布的事件
限界上下文內(nèi)部可以呈現(xiàn)屬于當(dāng)前限界上下文領(lǐng)域模型的聚合,如果還未開展領(lǐng)域建模,可以為空。
菱形對稱架構(gòu)
限界上下文的內(nèi)部應(yīng)遵循如下圖所示的菱形對稱架構(gòu):
菱形對稱架構(gòu)的核心思想:
- 內(nèi)外分離:內(nèi)部的領(lǐng)域?qū)优c外部的網(wǎng)關(guān)層分離,保證業(yè)務(wù)和技術(shù)的正交性
- 南北對稱:南向網(wǎng)關(guān)采用抽象思想,隔離外部資源變化對內(nèi)部領(lǐng)域?qū)訋淼挠绊懀槐毕蚓W(wǎng)關(guān)采用封裝思想,通過定義遠(yuǎn)程服務(wù)和本地服務(wù)隔離內(nèi)部領(lǐng)域邏輯對外部調(diào)用者的影響
系統(tǒng)分層架構(gòu)
在目標(biāo)系統(tǒng)層面上,需要將各個(gè)限界上下文組織在如下圖所示的系統(tǒng)分層架構(gòu)中:
代碼模型
遵循菱形對稱架構(gòu),一個(gè)完整的代碼模型如下所示:
- valueaddedlayer
boundedcontext
- north
- remote
resource
controller
provider
subscriber
- local
- message
- domain
- aggregate
entity
valueobject
domainservice
- south
- port
repository
client
publisher
- adapter
repository
client
publisher
以上內(nèi)容構(gòu)成了目標(biāo)系統(tǒng)的架構(gòu)設(shè)計(jì)文檔。
三、領(lǐng)域建模階段
1.領(lǐng)域分析建模
領(lǐng)域建模階段是通過對業(yè)務(wù)服務(wù)規(guī)約進(jìn)行領(lǐng)域分析建模開始的。領(lǐng)域分析建模與具體的建模技術(shù)和設(shè)計(jì)方法沒有任何關(guān)系,只是從業(yè)務(wù)的角度通過提取領(lǐng)域概念獲得最終的領(lǐng)域分析模型。該方法為快速建模法,得到的模型如下圖所示:
圖中的灰色領(lǐng)域概念是通過動(dòng)詞建模法獲得的。整個(gè)領(lǐng)域分析模型需要分配給對應(yīng)的限界上下文。
2.領(lǐng)域設(shè)計(jì)建模
靜態(tài)設(shè)計(jì)模型
領(lǐng)域設(shè)計(jì)建模從下圖所示的領(lǐng)域分析模型開始:
識(shí)別實(shí)體和值對象:
確定實(shí)體之間的關(guān)系:
根據(jù)實(shí)體關(guān)系的強(qiáng)弱劃定聚合的邊界,獲得以聚合為中心的領(lǐng)域設(shè)計(jì)模型:
動(dòng)態(tài)設(shè)計(jì)模型
獲得動(dòng)態(tài)設(shè)計(jì)模型的過程如下圖所示:
分析業(yè)務(wù)服務(wù),獲得如下所示的業(yè)務(wù)服務(wù)規(guī)約:
服務(wù)編號:033
服務(wù)名:報(bào)名活動(dòng)
服務(wù)描述:
作為報(bào)名人
我想要報(bào)名活動(dòng)
以便于預(yù)留活動(dòng)報(bào)名資格
觸發(fā)事件:
報(bào)名人選擇自己想要報(bào)名的活動(dòng),點(diǎn)擊“報(bào)名”按鈕
基本流程:
- 檢查報(bào)名人是否有效
- 檢查報(bào)名通道是否已關(guān)閉
- 檢查該報(bào)名人是否已報(bào)名
- 完成報(bào)名預(yù)約
- 發(fā)送報(bào)名預(yù)約成功的通知
替換流程:
- a 若報(bào)名人無效,給出提示信息
- a 如果報(bào)名通道已關(guān)閉,給出提示信息
- a 如果已報(bào)名,給出提示信息
- a 如報(bào)名失敗,給出失敗原因
驗(yàn)收標(biāo)準(zhǔn):
- 報(bào)名人必須是活動(dòng)所屬部落的會(huì)員
- 報(bào)名達(dá)到截止日期或者報(bào)名人數(shù)已到達(dá)上限,則視為報(bào)名通道已關(guān)閉
- 報(bào)名人不能重復(fù)報(bào)名
- 完成報(bào)名后,報(bào)名狀態(tài)設(shè)置為“已預(yù)訂”
- 報(bào)名人接收到預(yù)約成功的通知
根據(jù)業(yè)務(wù)服務(wù)規(guī)約獲得如下所示的任務(wù)樹:
- 報(bào)名活動(dòng)
- 驗(yàn)證報(bào)名
- 驗(yàn)證報(bào)名人是否會(huì)員 --- 訪問部落上下文
- 確定報(bào)名通道是否已關(guān)閉
- 獲取報(bào)名通道
- 確定是否已關(guān)閉
- 驗(yàn)證報(bào)名單 ---- Repository
- 生成報(bào)名單 --- Repository
- 更新報(bào)名通道
- 加載報(bào)名通道
- 更新
- 保存報(bào)名通道
- 發(fā)送報(bào)名預(yù)約成功的通知 --- 通知上下文
分配職責(zé)給對應(yīng)的角色構(gòu)造型,形成序列圖腳本:
TicketController.enrollActivity(EnrollingRequest) {
TicketAppService.enrollActivity(EnrollingRequest) {
Ticket ticket = EnrollingRequest.to()
TicketService.enrollActivity(ticket) {
TicketService.validate(ticket) {
MemberClient.isMember(enrollerId, tribeId)
EnrollingChannelService.isClosed(activityId) {
EnrollingChannel channel = EnrollingChannelRepository.channelOf(activityId)
channel.isClosed()
}
TicketRepository.isExists(enrollerId, activityId, TicketStatus)
}
TicketRepository.add(ticket)
EnrollingChannelService.occupiedBy(activityId) {
EnrollingChannel channel = EnrollingChannelRepository.channelOf(activityId)
channel.occupiedWith(1)
EnrollingChannelRepository.save(channel)
}
}
ActivitySubscribedPublisher.publish(ActivitySubscribed)
}
}
領(lǐng)域建模階段輸出的靜態(tài)領(lǐng)域設(shè)計(jì)模型與動(dòng)態(tài)領(lǐng)域設(shè)計(jì)模型共同組成限界上下文的設(shè)計(jì)文檔。