<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          長(zhǎng)文多圖:結(jié)合DDD講清楚編寫(xiě)技術(shù)方案的七大維度

          共 12165字,需瀏覽 25分鐘

           ·

          2021-10-20 16:11

          點(diǎn)擊上方“服務(wù)端思維”,選擇“設(shè)為星標(biāo)

          回復(fù)”669“獲取獨(dú)家整理的精選資料集

          回復(fù)”加群“加入全國(guó)服務(wù)端高端社群「后端圈」


          作者 | IT徐胖子
          出品?| JAVA前線

          1 為什么要寫(xiě)技術(shù)方案

          回顧軟件開(kāi)發(fā)的歷史進(jìn)程,我們可以將其分為程序設(shè)計(jì)時(shí)代、程序系統(tǒng)時(shí)代和軟件工程時(shí)代三大歷史階段。

          在程序設(shè)計(jì)時(shí)代(1946-1956),軟件開(kāi)發(fā)主要依賴于個(gè)人編程技巧,技術(shù)文檔只要存在個(gè)人開(kāi)發(fā)者的大腦即可,因?yàn)闆](méi)有溝通和協(xié)作需要,編寫(xiě)技術(shù)文檔也不具有緊迫性。

          在程序系統(tǒng)時(shí)代(1956-1968),計(jì)算機(jī)性能顯著提升,應(yīng)用范圍和規(guī)模逐步擴(kuò)大,以至于依靠個(gè)人無(wú)法完成軟件的開(kāi)發(fā),所以出現(xiàn)了團(tuán)隊(duì)合作。在早期團(tuán)隊(duì)合作過(guò)程中,開(kāi)發(fā)者仍然保持了早期各自為戰(zhàn)的開(kāi)發(fā)習(xí)慣,即使出現(xiàn)了一些方法論雛形,也無(wú)法從根本上控制溝通和協(xié)作的巨大成本,軟件危機(jī)就此出現(xiàn)。1968年國(guó)際學(xué)術(shù)會(huì)議提出了軟件危機(jī)和軟件工程的概念。

          軟件危機(jī)的定義是落后的軟件生產(chǎn)方式無(wú)法滿足迅速增長(zhǎng)的計(jì)算機(jī)軟件需求,從而導(dǎo)致開(kāi)發(fā)與維護(hù)過(guò)程中出現(xiàn)一系列嚴(yán)重問(wèn)題的現(xiàn)象。軟件的工程定義是建立并使用完善的工程化原則,以較經(jīng)濟(jì)的手段獲得能在實(shí)際機(jī)器上有效運(yùn)行的可靠軟件的一系列方法

          從此軟件開(kāi)發(fā)進(jìn)入工程化階段,也應(yīng)運(yùn)而生了大量開(kāi)發(fā)方法論和開(kāi)發(fā)模型。其中標(biāo)準(zhǔn)和完善的文檔是軟件工程重要組成部分,可以很大程度上減少溝通和協(xié)作成本,而技術(shù)方案又是技術(shù)文檔重要組成部分。


          2 技術(shù)方案要體現(xiàn)什么

          軟件系統(tǒng)生命周期包括定義、開(kāi)發(fā)、運(yùn)維、消亡這四大階段。定義階段包括定義問(wèn)題、可行性研究和需求分析。開(kāi)發(fā)階段包括概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼和測(cè)試。運(yùn)維階段包括更正性維護(hù)、適應(yīng)性維護(hù)、預(yù)防性維護(hù)和完善性維護(hù)。消亡階段包括系統(tǒng)報(bào)廢和優(yōu)雅下線。



          生命周期每個(gè)階段固然有各自的重要性,但是開(kāi)發(fā)者更應(yīng)該關(guān)注定義階段與開(kāi)發(fā)階段。定義階段需要解決為什么開(kāi)發(fā)(why)、需求是什么(what)兩個(gè)問(wèn)題,開(kāi)發(fā)階段需要解決怎么設(shè)計(jì),怎么編碼,怎么測(cè)試(how)三個(gè)問(wèn)題。

          技術(shù)方案是否需要體現(xiàn)定義和開(kāi)發(fā)的所有子階段?我認(rèn)為也無(wú)必要。問(wèn)題定義和可行性研究主要由產(chǎn)品經(jīng)理負(fù)責(zé),測(cè)試階段主要由測(cè)試人員負(fù)責(zé),開(kāi)發(fā)者可以關(guān)注但不是必須體現(xiàn)在技術(shù)方案。我認(rèn)為技術(shù)方案必須要體現(xiàn)需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、編碼四個(gè)子階段。


          3 七大維度

          我認(rèn)為一份完整技術(shù)方案應(yīng)該至少具有七大維度,每個(gè)維度描述系統(tǒng)的一個(gè)側(cè)面,組合在一起最終描繪出整個(gè)系統(tǒng),這些維度分別是:

          四色分領(lǐng)域

          用例看功能

          流程三劍客

          領(lǐng)域與數(shù)據(jù)

          縱橫做設(shè)計(jì)

          分層看架構(gòu)

          接口看對(duì)接

          本文我們分析一個(gè)足球運(yùn)動(dòng)員信息管理系統(tǒng),這個(gè)系統(tǒng)我們可能也都沒(méi)有做過(guò),正好一起分析這個(gè)系統(tǒng)。需要說(shuō)明本文著重介紹方法論的落地,業(yè)務(wù)細(xì)節(jié)難以面面俱到。


          3.1 四色分領(lǐng)域

          3.1.1 流程梳理

          首先梳理業(yè)務(wù)流程,這里有兩個(gè)問(wèn)題需要考慮,第一個(gè)問(wèn)題是從什么視角去梳理?因?yàn)椴煌娜丝吹降牧鞒淌遣灰粯拥?。答案是取決于系統(tǒng)需要解決什么問(wèn)題,因?yàn)槲覀円芾磉\(yùn)動(dòng)員從轉(zhuǎn)會(huì)到上場(chǎng)比賽整條鏈路信息,所以從運(yùn)動(dòng)員視角出發(fā)是一個(gè)合適的選擇。

          第二個(gè)問(wèn)題是對(duì)業(yè)務(wù)不熟悉怎么辦?因?yàn)槲覀儾皇求w育和運(yùn)動(dòng)專家,并不清楚整條鏈路的業(yè)務(wù)細(xì)節(jié)。答案是梳理流程時(shí)一定要有業(yè)務(wù)專家在場(chǎng),因?yàn)闆](méi)有真實(shí)業(yè)務(wù)細(xì)節(jié),無(wú)法領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。同理在互聯(lián)網(wǎng)梳理復(fù)雜業(yè)務(wù)流程時(shí),一定要有對(duì)相關(guān)業(yè)務(wù)熟悉的產(chǎn)品經(jīng)理或者運(yùn)營(yíng)一起參與。

          假設(shè)足球業(yè)務(wù)專家梳理出了業(yè)務(wù)流程,運(yùn)動(dòng)員提出轉(zhuǎn)會(huì),協(xié)商一致后到新俱樂(lè)部體檢,體檢通過(guò)就進(jìn)行簽約。進(jìn)入新俱樂(lè)部后進(jìn)行訓(xùn)練,訓(xùn)練指標(biāo)達(dá)標(biāo)后上場(chǎng)比賽,賽后參加新聞發(fā)布會(huì)。實(shí)際流程會(huì)復(fù)雜很多,本文還是著重講解方法論。



          3.1.2 四色建模

          (1) 時(shí)標(biāo)對(duì)象

          四色建模第一種顏色是紅色,表示時(shí)標(biāo)對(duì)象。時(shí)標(biāo)對(duì)象是四色建模最重要的對(duì)象,可以理解為核心業(yè)務(wù)單據(jù)。在業(yè)務(wù)過(guò)程中一定要對(duì)關(guān)鍵業(yè)務(wù)留下單據(jù),通過(guò)這些單據(jù)可以追溯整個(gè)業(yè)務(wù)流程。

          時(shí)標(biāo)對(duì)象具有兩個(gè)特點(diǎn):第一是事實(shí)不可變性,記錄了過(guò)去某個(gè)時(shí)間點(diǎn)或時(shí)間段內(nèi)發(fā)生的事實(shí)。第二是責(zé)任可追溯性,記錄了管理者關(guān)注的信息?,F(xiàn)在我們分析本系統(tǒng)時(shí)標(biāo)對(duì)象有哪些,需要留下哪些核心業(yè)務(wù)單據(jù)。

          轉(zhuǎn)會(huì)對(duì)應(yīng)轉(zhuǎn)會(huì)單據(jù),體檢對(duì)應(yīng)體檢單據(jù),簽合同對(duì)應(yīng)合同單據(jù),訓(xùn)練對(duì)應(yīng)訓(xùn)練指標(biāo)單據(jù),比賽對(duì)應(yīng)比賽指標(biāo)單據(jù),新聞發(fā)布會(huì)對(duì)應(yīng)采訪單據(jù)。根據(jù)分析繪制如下時(shí)標(biāo)對(duì)象:



          (2) 參與方、地、物

          這三類對(duì)象在四色建模中用綠色表示,我們以電商場(chǎng)景為例進(jìn)行說(shuō)明。用戶支付購(gòu)買(mǎi)商家的商品時(shí),用戶和商家是參與方。物流系統(tǒng)發(fā)貨時(shí)配送單據(jù)需要有配送地址對(duì)象,地址對(duì)象就是地。訂單需要商品對(duì)象,物流配送需要有貨品,商品和貨品就是物。

          我們分析本例可以知道參與方包含總經(jīng)理、隊(duì)醫(yī)、教練、球迷、記者,地包含訓(xùn)練地址、比賽地址、采訪地址,物包含簽名球衣和簽名足球:



          (3) 角色對(duì)象

          在四色建模中用黃色表示,這類對(duì)象表示參與方、地、物以什么角色參與到業(yè)務(wù)流程:



          (4) 描述對(duì)象

          我們可以為對(duì)象增加相關(guān)描述信息,在四色建模中用藍(lán)色表示:



          3.1.3 劃分領(lǐng)域

          在四色建模過(guò)程中我們體會(huì)到時(shí)標(biāo)對(duì)象是最重要的對(duì)象,因?yàn)槠涑休d了業(yè)務(wù)系統(tǒng)核心單據(jù)。在劃分領(lǐng)域時(shí)我們同樣離不開(kāi)時(shí)標(biāo)對(duì)象,通過(guò)收斂相關(guān)時(shí)標(biāo)對(duì)象劃分領(lǐng)域。



          3.1.4 領(lǐng)域事件

          當(dāng)業(yè)務(wù)系統(tǒng)發(fā)生一件事情時(shí),如果本領(lǐng)域或其它領(lǐng)域有后續(xù)動(dòng)作跟進(jìn),那么我們把這件事情稱為領(lǐng)域事件,這個(gè)事件需要被感知。

          例如球員比賽受傷,這是比賽子域事件,但是醫(yī)療和訓(xùn)練子域是需要感知的,那么比賽子域就發(fā)出一個(gè)事件,醫(yī)療和訓(xùn)練子域會(huì)訂閱。球員比賽取得進(jìn)球,這也是比賽子域事件,但是訓(xùn)練和合同子域也會(huì)關(guān)注這個(gè)事件,所以比賽子域也會(huì)發(fā)出一個(gè)比賽進(jìn)球事件,訓(xùn)練和合同子域會(huì)訂閱。

          通過(guò)事件交互有一個(gè)問(wèn)題需要注意,通過(guò)事件訂閱實(shí)現(xiàn)業(yè)務(wù)只能采用最終一致性,需要放棄強(qiáng)一致性,可能會(huì)引入新的復(fù)雜度需要權(quán)衡。



          3.2 用例看功能

          目前為止領(lǐng)域已經(jīng)確定了,大領(lǐng)域已經(jīng)拆分成了小領(lǐng)域,我們已經(jīng)不再束手無(wú)策,而是可以對(duì)小領(lǐng)域進(jìn)行用例分析了。用例圖由參與者和用例組成,目的是回答這樣一個(gè)問(wèn)題:什么人使用系統(tǒng)干什么事。

          下圖表示在比賽領(lǐng)域,運(yùn)動(dòng)員視角(什么人)使用系統(tǒng)進(jìn)行進(jìn)球統(tǒng)計(jì),助攻統(tǒng)計(jì),犯規(guī)統(tǒng)計(jì),跑動(dòng)距離統(tǒng)計(jì),比賽評(píng)分統(tǒng)計(jì),傳球成功率統(tǒng)計(jì),受傷統(tǒng)計(jì)(干什么事),同理也可以選擇四色建模中其它參與者視角繪制用例圖。



          include關(guān)鍵字表示包含關(guān)系。例如比賽是基用例,包含了進(jìn)球統(tǒng)計(jì),助攻統(tǒng)計(jì),犯規(guī)統(tǒng)計(jì),跑動(dòng)距離統(tǒng)計(jì),比賽評(píng)分統(tǒng)計(jì),傳球成功率統(tǒng)計(jì),受傷統(tǒng)計(jì)七個(gè)子用例。包含關(guān)系表示法有兩個(gè)優(yōu)點(diǎn):第一是可以清晰地組織子用例,第二是有利于子用例復(fù)用,例如主教練視角用例圖也包含比賽評(píng)分,那么就可以直接指向比賽評(píng)分子用例。

          extend關(guān)鍵字表示擴(kuò)展關(guān)系。例如點(diǎn)球統(tǒng)計(jì)是進(jìn)球統(tǒng)計(jì)的擴(kuò)展,因?yàn)椴灰欢梢垣@得點(diǎn)球,所以點(diǎn)球統(tǒng)計(jì)即使不存在,也不會(huì)影響進(jìn)球統(tǒng)計(jì)功能。黃牌統(tǒng)計(jì)、紅牌統(tǒng)計(jì)是犯規(guī)統(tǒng)計(jì)的擴(kuò)展,因?yàn)槠胀ǚ敢?guī)不會(huì)獲得紅黃牌,所以紅黃牌統(tǒng)計(jì)不存在,也不會(huì)影響犯規(guī)統(tǒng)計(jì)功能。

          用例圖不關(guān)心實(shí)現(xiàn)細(xì)節(jié),而是從外部視角描述系統(tǒng)功能,即使不了解實(shí)現(xiàn)細(xì)節(jié)的人,通過(guò)看用例圖也可以快速了解系統(tǒng)功能,這個(gè)特性規(guī)定了用例圖不宜過(guò)于復(fù)雜,能夠說(shuō)明核心功能即可。


          3.3 流程三劍客

          用例圖是從外部視角描述系統(tǒng),但是分析系統(tǒng)總是要深入系統(tǒng)內(nèi)部的,其中流程視圖就是描述系統(tǒng)內(nèi)如何流轉(zhuǎn)的視圖。

          活動(dòng)圖、序列圖、狀態(tài)機(jī)圖是流程視圖中最重要的三種視圖,我們稱為流程三劍客。三者側(cè)重點(diǎn)有所不同:活動(dòng)圖側(cè)重于邏輯分支,順序圖側(cè)重于交互,狀態(tài)機(jī)圖側(cè)重于狀態(tài)流轉(zhuǎn)。


          3.3.1 活動(dòng)圖

          活動(dòng)圖適合描述復(fù)雜邏輯分支,設(shè)想這樣一種業(yè)務(wù)場(chǎng)景,球隊(duì)需要選拔一名球員成為足球先生,選拔標(biāo)準(zhǔn)如下:前場(chǎng)、中場(chǎng)、后場(chǎng)、門(mén)將各選出一名候選球員。前場(chǎng)隊(duì)員依次比較進(jìn)球數(shù)、助攻數(shù),中場(chǎng)隊(duì)員依次比較助攻數(shù)、搶斷數(shù),后場(chǎng)隊(duì)員依次比較解圍數(shù)、搶斷數(shù),門(mén)將依次比較撲救數(shù)、撲點(diǎn)數(shù),如果所有指標(biāo)均相同則抽簽。每個(gè)位置有人選之后,全體教練組投票,如果投票數(shù)相同則抽簽。

          業(yè)界流傳著一句話:一圖勝千言,其中一個(gè)重要原因是文字是線性的,所以表達(dá)邏輯分支能力不如流程視圖,而在流程視圖中表達(dá)邏輯分支能力最強(qiáng)正是活動(dòng)圖。



          3.3.2 順序圖

          順序圖側(cè)重于交互,適合按照時(shí)間順序體現(xiàn)一個(gè)業(yè)務(wù)流程中交互細(xì)節(jié),但是順序圖并不擅長(zhǎng)體現(xiàn)復(fù)雜邏輯分支。

          如果某個(gè)邏輯分支特別重要,可以選擇再畫(huà)一個(gè)順序圖。例如支付流程中有支付成功正常流程,也有支付失敗異常流程,這兩個(gè)流程都非常重要,所以可以用兩張順序圖體現(xiàn)?;氐奖疚膶?shí)例,我們可以通過(guò)順序圖體現(xiàn)球員從提出轉(zhuǎn)會(huì)到比賽全流程。



          3.3.3 狀態(tài)機(jī)圖

          假設(shè)一條數(shù)據(jù)有ABC三種狀態(tài),從正常業(yè)務(wù)角度來(lái)看,狀態(tài)只能從A流轉(zhuǎn)到B,再?gòu)腂流轉(zhuǎn)到C,不能亂序也不可逆。但是可能出現(xiàn)這種異常情況:數(shù)據(jù)當(dāng)前狀態(tài)為A,接收異步消息更改狀態(tài),B消息由于延時(shí)晚于C消息,最終導(dǎo)致?tīng)顟B(tài)先改為C再改為B,那么此時(shí)狀態(tài)就是錯(cuò)誤的。

          狀態(tài)機(jī)圖側(cè)重于狀態(tài)流轉(zhuǎn),說(shuō)明了哪些狀態(tài)之間可以相互流轉(zhuǎn),在實(shí)際開(kāi)發(fā)中再結(jié)合狀態(tài)機(jī)代碼模式,可以解決上述狀態(tài)異常情況?;氐奖疚膶?shí)例,我們可以通過(guò)狀態(tài)機(jī)圖表示球員從提出轉(zhuǎn)會(huì)到簽約整個(gè)狀態(tài)流程。



          3.4 領(lǐng)域與數(shù)據(jù)

          上述章節(jié)從功能層面和流程層面進(jìn)行了系統(tǒng)分析,現(xiàn)在從數(shù)據(jù)層分析系統(tǒng),我們首先對(duì)比兩組概念:值對(duì)象與實(shí)體,領(lǐng)域?qū)ο笈c數(shù)據(jù)對(duì)象。

          實(shí)體是具有唯一標(biāo)識(shí)的對(duì)象,唯一標(biāo)識(shí)會(huì)伴隨實(shí)體對(duì)象整個(gè)生命周期并且不可變更。值對(duì)象本質(zhì)上是屬性的集合,沒(méi)有唯一標(biāo)識(shí)。

          領(lǐng)域?qū)ο笈c數(shù)據(jù)對(duì)象一個(gè)重要的區(qū)別是值對(duì)象存儲(chǔ)方式。領(lǐng)域?qū)ο笤诎祵?duì)象的同時(shí)也保留了值對(duì)象的業(yè)務(wù)含義,而數(shù)據(jù)對(duì)象可以使用更加松散的結(jié)構(gòu)保存值對(duì)象,簡(jiǎn)化數(shù)據(jù)庫(kù)設(shè)計(jì)。

          現(xiàn)在我們需要管理足球運(yùn)動(dòng)員基本信息和比賽數(shù)據(jù),對(duì)應(yīng)領(lǐng)域模型和數(shù)據(jù)模型應(yīng)該如何設(shè)計(jì)?姓名、身高、體重是一名運(yùn)動(dòng)員本質(zhì)屬性,加上唯一編號(hào)可以對(duì)應(yīng)實(shí)體對(duì)象。跑動(dòng)距離,傳球成功率,進(jìn)球數(shù)是運(yùn)動(dòng)員比賽表現(xiàn),這些屬性的集合可以對(duì)應(yīng)值對(duì)象。



          我們根據(jù)圖示編寫(xiě)領(lǐng)域?qū)ο笈c數(shù)據(jù)對(duì)象代碼:

          //?數(shù)據(jù)對(duì)象
          public?class?FootballPlayerDO?{
          ????private?Long?id;
          ????private?String?name;
          ????private?Integer?height;
          ????private?Integer?weight;
          ????private?String?gamePerformance;
          }

          //?領(lǐng)域?qū)ο?/span>
          public?class?FootballPlayerDMO?{
          ????private?Long?id;
          ????private?String?name;
          ????private?Integer?height;
          ????private?Integer?weight;
          ????private?GamePerformanceVO?gamePerformanceVO;
          }

          public?class?GamePerformanceVO?{
          ????private?Double?runDistance;
          ????private?Double?passSuccess;
          ????private?Integer?scoreNum;
          }

          為什么要采用JSON存儲(chǔ)值對(duì)象?因?yàn)槟_本化是一種拓展靈活性的方式,腳本化不僅指使用groovy、QLExpress腳本增強(qiáng)系統(tǒng)靈活性,還包括松散可擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)模型抽象出了姓名、身高、體重這些基本屬性,對(duì)于頻繁變化的比賽表現(xiàn)屬性,這些屬性值可能經(jīng)常變化,甚至屬性本身也是經(jīng)常變化,可能會(huì)加上射門(mén)次數(shù),突破次數(shù)等,所以采用松散結(jié)構(gòu)進(jìn)行存儲(chǔ)。

          如果需要根據(jù)JSON結(jié)構(gòu)中KEY進(jìn)行檢索,例如查詢進(jìn)球數(shù)大于5的球員,這也不是沒(méi)有辦法。我們可以將MySQL表中數(shù)據(jù)平鋪到ES中,一條數(shù)據(jù)根據(jù)JSON KEY平鋪?zhàn)兂蔀槎鄺l數(shù)據(jù),這樣就可以進(jìn)行檢索了。


          3.5 縱橫做設(shè)計(jì)

          復(fù)雜業(yè)務(wù)之所以復(fù)雜,一個(gè)重要原因是涉及角色或者類型較多,很難平鋪直敘地進(jìn)行設(shè)計(jì),所以我們需要增加分析維度。其中最常見(jiàn)的是增加橫向和縱向兩個(gè)維度,本文也著重討論兩個(gè)維度??傮w而言橫向擴(kuò)展的是思考廣度,縱向擴(kuò)展的是思考深度,對(duì)應(yīng)到系統(tǒng)設(shè)計(jì)而言可以總結(jié)為:縱向做隔離,橫向做編排。

          我們首先分析一個(gè)下單場(chǎng)景。當(dāng)前有ABC三種訂單類型:A訂單價(jià)格9折,物流最大重量不能超過(guò)9公斤,不支持退款。B訂單價(jià)格8折,物流最大重量不能超過(guò)8公斤,支持退款。C訂單價(jià)格7折,物流最大重量不能超過(guò)7公斤,支持退款。按照需求字面含義平鋪直敘地寫(xiě)代碼也并不難:

          public?class?OrderServiceImpl?implements?OrderService?{

          ????@Resource
          ????private?OrderMapper?orderMapper;

          ????@Override
          ????public?void?createOrder(OrderBO?orderBO)?{
          ????????if?(null?==?orderBO)?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????if?(OrderTypeEnum.isNotValid(orderBO.getType()))?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????//?A類型訂單
          ????????if?(OrderTypeEnum.A_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.9);
          ????????????if?(orderBO.getWeight()?>?9)?{
          ????????????????throw?new?RuntimeException("超過(guò)物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.FALSE);
          ????????}
          ????????//?B類型訂單
          ????????else?if?(OrderTypeEnum.B_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.8);
          ????????????if?(orderBO.getWeight()?>?8)?{
          ????????????????throw?new?RuntimeException("超過(guò)物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.TRUE);
          ????????}
          ????????//?C類型訂單
          ????????else?if?(OrderTypeEnum.C_TYPE.getCode().equals(orderBO.getType()))?{
          ????????????orderBO.setPrice(orderBO.getPrice()?*?0.7);
          ????????????if?(orderBO.getWeight()?>?7)?{
          ????????????????throw?new?RuntimeException("超過(guò)物流最大重量");
          ????????????}
          ????????????orderBO.setRefundSupport(Boolean.TRUE);
          ????????}
          ????????//?保存數(shù)據(jù)
          ????????OrderDO?orderDO?=?new?OrderDO();
          ????????BeanUtils.copyProperties(orderBO,?orderDO);
          ????????orderMapper.insert(orderDO);
          ????}
          }

          上述代碼從功能上完全可以實(shí)現(xiàn)業(yè)務(wù)需求,但是程序員不僅要滿足功能,還需要思考代碼的可維護(hù)性。如果新增一種訂單類型,或者新增一個(gè)訂單屬性處理邏輯,那么我們就要在上述邏輯中新增代碼,如果處理不慎就會(huì)影響原有邏輯。

          為了避免牽一發(fā)而動(dòng)全身這種情況,設(shè)計(jì)模式中的開(kāi)閉原則要求我們面向新增開(kāi)放,面向修改關(guān)閉,我認(rèn)為這是設(shè)計(jì)模式中最重要的一條原則。

          需求變化通過(guò)擴(kuò)展,而不是通過(guò)修改已有代碼實(shí)現(xiàn),這樣就保證代碼穩(wěn)定性。擴(kuò)展也不是隨意擴(kuò)展,因?yàn)槭孪榷x了算法,擴(kuò)展也是根據(jù)算法擴(kuò)展,用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。標(biāo)準(zhǔn)意義的二十三種設(shè)計(jì)模式說(shuō)到底最終都是在遵循開(kāi)閉原則。

          如何改變平鋪直敘的思考方式?這就要為問(wèn)題分析加上縱向和橫向兩個(gè)維度,我選擇使用分析矩陣方法,其中縱向表示策略,橫向表示場(chǎng)景:



          3.5.1 縱向做隔離

          縱向維度表示策略,不同策略在邏輯上和業(yè)務(wù)上應(yīng)該是隔離的,本實(shí)例包括優(yōu)惠策略、物流策略和退款策略,策略作為抽象,不同訂單類型去擴(kuò)展這個(gè)抽象,策略模式非常適合這種場(chǎng)景。本文詳細(xì)分析優(yōu)惠策略,物流策略和退款策略同理。

          //?優(yōu)惠策略
          public?interface?DiscountStrategy?{
          ????public?void?discount(OrderBO?orderBO);
          }

          //?A類型優(yōu)惠策略
          @Component
          public?class?TypeADiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.9);
          ????}
          }

          //?B類型優(yōu)惠策略
          @Component
          public?class?TypeBDiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.8);
          ????}
          }

          //?C類型優(yōu)惠策略
          @Component
          public?class?TypeCDiscountStrategy?implements?DiscountStrategy?{

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????orderBO.setPrice(orderBO.getPrice()?*?0.7);
          ????}
          }

          //?優(yōu)惠策略工廠
          @Component
          public?class?DiscountStrategyFactory?implements?InitializingBean?{
          ????private?Map?strategyMap?=?new?HashMap<>();

          ????@Resource
          ????private?TypeADiscountStrategy?typeADiscountStrategy;
          ????@Resource
          ????private?TypeBDiscountStrategy?typeBDiscountStrategy;
          ????@Resource
          ????private?TypeCDiscountStrategy?typeCDiscountStrategy;

          ????public?DiscountStrategy?getStrategy(String?type)?{
          ????????return?strategyMap.get(type);
          ????}

          ????@Override
          ????public?void?afterPropertiesSet()?throws?Exception?{
          ????????strategyMap.put(OrderTypeEnum.A_TYPE.getCode(),?typeADiscountStrategy);
          ????????strategyMap.put(OrderTypeEnum.B_TYPE.getCode(),?typeBDiscountStrategy);
          ????????strategyMap.put(OrderTypeEnum.C_TYPE.getCode(),?typeCDiscountStrategy);
          ????}
          }

          //?優(yōu)惠策略執(zhí)行
          @Component
          public?class?DiscountStrategyExecutor?{
          ????private?DiscountStrategyFactory?discountStrategyFactory;

          ????public?void?discount(OrderBO?orderBO)?{
          ????????DiscountStrategy?discountStrategy?=?discountStrategyFactory.getStrategy(orderBO.getType());
          ????????if?(null?==?discountStrategy)?{
          ????????????throw?new?RuntimeException("無(wú)優(yōu)惠策略");
          ????????}
          ????????discountStrategy.discount(orderBO);
          ????}
          }

          3.5.2 橫向做編排

          橫向維度表示場(chǎng)景,一種訂單類型在廣義上可以認(rèn)為是一種業(yè)務(wù)場(chǎng)景,在場(chǎng)景中將獨(dú)立的策略進(jìn)行串聯(lián),模板方法設(shè)計(jì)模式適用于這種場(chǎng)景。

          模板方法模式一般使用抽象類定義算法骨架,同時(shí)定義一些抽象方法,這些抽象方法延遲到子類實(shí)現(xiàn),這樣子類不僅遵守了算法骨架約定,也實(shí)現(xiàn)了自己的算法。既保證了規(guī)約也兼顧靈活性,這就是用抽象構(gòu)建框架,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。

          //?創(chuàng)建訂單服務(wù)
          public?interface?CreateOrderService?{
          ????public?void?createOrder(OrderBO?orderBO);
          }

          //?抽象創(chuàng)建訂單流程
          public?abstract?class?AbstractCreateOrderFlow?{

          ????@Resource
          ????private?OrderMapper?orderMapper;

          ????public?void?createOrder(OrderBO?orderBO)?{
          ????????//?參數(shù)校驗(yàn)
          ????????if?(null?==?orderBO)?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????if?(OrderTypeEnum.isNotValid(orderBO.getType()))?{
          ????????????throw?new?RuntimeException("參數(shù)異常");
          ????????}
          ????????//?計(jì)算優(yōu)惠
          ????????discount(orderBO);
          ????????//?計(jì)算重量
          ????????weighing(orderBO);
          ????????//?退款支持
          ????????supportRefund(orderBO);
          ????????//?保存數(shù)據(jù)
          ????????OrderDO?orderDO?=?new?OrderDO();
          ????????BeanUtils.copyProperties(orderBO,?orderDO);
          ????????orderMapper.insert(orderDO);
          ????}

          ????public?abstract?void?discount(OrderBO?orderBO);

          ????public?abstract?void?weighing(OrderBO?orderBO);

          ????public?abstract?void?supportRefund(OrderBO?orderBO);
          }

          //?實(shí)現(xiàn)創(chuàng)建訂單流程
          @Service
          public?class?CreateOrderFlow?extends?AbstractCreateOrderFlow?{

          ????@Resource
          ????private?DiscountStrategyExecutor?discountStrategyExecutor;
          ????@Resource
          ????private?ExpressStrategyExecutor?expressStrategyExecutor;
          ????@Resource
          ????private?RefundStrategyExecutor?refundStrategyExecutor;

          ????@Override
          ????public?void?discount(OrderBO?orderBO)?{
          ????????discountStrategyExecutor.discount(orderBO);
          ????}

          ????@Override
          ????public?void?weighing(OrderBO?orderBO)?{
          ????????expressStrategyExecutor.weighing(orderBO);
          ????}

          ????@Override
          ????public?void?supportRefund(OrderBO?orderBO)?{
          ????????refundStrategyExecutor.supportRefund(orderBO);
          ????}
          }

          3.5.3 綜合應(yīng)用

          上述實(shí)例業(yè)務(wù)和代碼并不復(fù)雜,其實(shí)復(fù)雜業(yè)務(wù)場(chǎng)景也不過(guò)是簡(jiǎn)單場(chǎng)景的疊加、組合和交織,無(wú)外乎也是通過(guò)縱向做隔離、橫向做編排尋求答案。



          縱向維度抽象出能力池這個(gè)概念,能力池中包含許多能力,不同的能力按照不同業(yè)務(wù)維度聚合,例如優(yōu)惠能力池,物流能力池,退款能力池。我們可以看到兩種程度的隔離性,能力池之間相互隔離,能力之間也相互隔離。

          橫向維度將能力從能力池選出來(lái),按照業(yè)務(wù)需求串聯(lián)在一起,形成不同業(yè)務(wù)流程。因?yàn)槟芰梢匀我饨M合,所以體現(xiàn)了很強(qiáng)的靈活性。除此之外,不同能力既可以串行執(zhí)行,如果不同能力之間沒(méi)有依賴關(guān)系,也可以如同流程Y一樣并行執(zhí)行,提升執(zhí)行效率。

          此時(shí)我們回到本文足球運(yùn)動(dòng)員管理系統(tǒng),如果采用縱向和橫向思維分析3.3.1足球先生選拔業(yè)務(wù)場(chǎng)景可以得到下圖:



          縱向隔離出進(jìn)攻能力池,防守能力池,門(mén)將能力池,橫向編排出前場(chǎng)、中場(chǎng)、后場(chǎng)、門(mén)將四個(gè)流程,在不同流程中可以任意從能力池中選擇能力進(jìn)行組合,而不是編寫(xiě)冗長(zhǎng)的判斷邏輯,顯著提升了代碼可擴(kuò)展性。


          3.6 分層看架構(gòu)

          系統(tǒng)架構(gòu)總體而言分為兩個(gè)層次,第一種層次是指本項(xiàng)目在整個(gè)公司位于哪一層次。持久層、緩存層、中間件、業(yè)務(wù)中臺(tái)、服務(wù)層、網(wǎng)關(guān)層、客戶端和代理層是常見(jiàn)的分層架構(gòu),大多數(shù)情況下業(yè)務(wù)需求最終會(huì)體現(xiàn)在服務(wù)層,不同的業(yè)務(wù)領(lǐng)域?qū)?yīng)不同的微服務(wù)。



          第二種層次是指本項(xiàng)目?jī)?nèi)部代碼的組織方式,一般可以分為接口層,訪問(wèn)層,業(yè)務(wù)層,領(lǐng)域?qū)?,外部訪問(wèn)層和基礎(chǔ)層。

          (1) api

          接口層:提供面向外部接口聲明和DTO

          (2) controller

          訪問(wèn)層:提供HTTP訪問(wèn)入口

          (3) service

          業(yè)務(wù)層:提供BO對(duì)象,領(lǐng)域?qū)雍蜆I(yè)務(wù)層都包含業(yè)務(wù),但是用途不同。業(yè)務(wù)層可以組合不同領(lǐng)域業(yè)務(wù),并且可以增加流控、監(jiān)控、日志、權(quán)限控制切面,相較于領(lǐng)域?qū)痈鼮樨S富

          (4) domain

          領(lǐng)域?qū)樱禾峁〥MO、VO、事件、DO和數(shù)據(jù)訪問(wèn),核心是根據(jù)領(lǐng)域進(jìn)行分包,領(lǐng)域內(nèi)高內(nèi)聚,領(lǐng)域間低耦合

          (5) dependency

          外部訪問(wèn)層:在這個(gè)模塊中調(diào)用外部RPC服務(wù),解析返回碼和返回?cái)?shù)據(jù)

          (6) infrastructure

          基礎(chǔ)層:包含通用基礎(chǔ)功能,例如基礎(chǔ)工具,緩存工具,打印日志,消息發(fā)送



          本文僅展開(kāi)領(lǐng)域?qū)舆M(jìn)行分析。領(lǐng)域?qū)雍诵氖前凑疹I(lǐng)域進(jìn)行分包,并且提供DMO、VO、事件、DO和數(shù)據(jù)訪問(wèn),領(lǐng)域內(nèi)高內(nèi)聚,領(lǐng)域間低耦合。



          3.7 接口看對(duì)接

          一個(gè)接口代碼編寫(xiě)完成后,那么這個(gè)接口如何調(diào)用,輸入和輸出參數(shù)是什么,這些問(wèn)題需要在接口文檔中得到回答。

          接口文檔生成有兩種方式,第一種方式是自動(dòng)生成,例如使用Swagger框架,第二種方式是手工生成。自動(dòng)生成的優(yōu)點(diǎn)是代碼即文檔,還具有調(diào)試功能,在公司內(nèi)部進(jìn)行聯(lián)調(diào)時(shí)非常方便。但是如果接口是提供給外部第三方使用,那么還是需要手工編寫(xiě)接口文檔。一個(gè)接口核心描述無(wú)外乎接口名稱、接口說(shuō)明、輸入?yún)?shù)、輸出參數(shù),其它信息根據(jù)需要再增加。



          4 文章總結(jié)

          本文通過(guò)一個(gè)業(yè)務(wù)實(shí)例介紹了技術(shù)方案的七大維度:四色分領(lǐng)域、用例看功能、流程三劍客、領(lǐng)域與數(shù)據(jù)、縱橫做設(shè)計(jì)、分層看架構(gòu)、接口看對(duì)接。每個(gè)維度描述系統(tǒng)的一個(gè)側(cè)面,組合在一起最終描繪出整個(gè)系統(tǒng)。

          在實(shí)際開(kāi)發(fā)中如果需求不復(fù)雜,那么也不是七個(gè)維度都要體現(xiàn),而是根據(jù)實(shí)際情況取舍,能夠把方案說(shuō)清楚即可,希望本文對(duì)大家有所幫助。


          — 本文結(jié)束 —


          ●?漫談設(shè)計(jì)模式在 Spring 框架中的良好實(shí)踐

          ●?顛覆微服務(wù)認(rèn)知:深入思考微服務(wù)的七個(gè)主流觀點(diǎn)

          ●?人人都是 API 設(shè)計(jì)者

          ●?一文講透微服務(wù)下如何保證事務(wù)的一致性

          ●?要黑盒測(cè)試微服務(wù)內(nèi)部服務(wù)間調(diào)用,我該如何實(shí)現(xiàn)?



          關(guān)注我,回復(fù) 「加群」 加入各種主題討論群。



          對(duì)「服務(wù)端思維」有期待,請(qǐng)?jiān)谖哪c(diǎn)個(gè)在看

          喜歡這篇文章,歡迎轉(zhuǎn)發(fā)、分享朋友圈


          在看點(diǎn)這里
          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  台湾长腿美人ladyyuan | 欧美黄色直播视频 | 亚洲欧美在线视频 | 中文人妻av | 操的爽视频 |