注明:本文已投遞到《極客時(shí)間》電子刊物上:從用例分析到方案評(píng)審,我們是如何進(jìn)行業(yè)務(wù)系統(tǒng)設(shè)計(jì)的?
前言
工作這些年,通過自身由0到1的成長,結(jié)合所見所聞,發(fā)現(xiàn)無論是剛?cè)肼殘龅男卤€是有些工作經(jīng)驗(yàn)的老兵,在業(yè)務(wù)系統(tǒng)的設(shè)計(jì)上非常之薄弱。
一般來講,職場新兵在設(shè)計(jì)業(yè)務(wù)系統(tǒng)的時(shí)候,容易出以下幾個(gè)問題:
1 需求溝通完,提筆就開始設(shè)計(jì)數(shù)據(jù)表結(jié)構(gòu),就想著有哪幾張表,要建哪些索引,有哪些唯一鍵,如何保證事務(wù),如何設(shè)計(jì)觸發(fā)器等等。出現(xiàn)這類問題很正常,畢竟在學(xué)校學(xué)的都是理論,授課的老師也都沒有太多的工程經(jīng)驗(yàn),也指導(dǎo)不了你的設(shè)計(jì)。
2 要他設(shè)計(jì)一個(gè)系統(tǒng),完全摸不著頭腦,一提筆就是寫代碼,想到哪里寫到哪里,沒有一絲設(shè)計(jì)。出現(xiàn)這類問題的同學(xué),一般都是在學(xué)校沒好好學(xué)習(xí)的同學(xué),或者是非計(jì)算機(jī)專業(yè)的同學(xué)。
而一些工作經(jīng)驗(yàn)的人,做出來的設(shè)計(jì)也是一團(tuán)糟,要么就是考慮不周全,要么就是畫的圖別人看不懂。這類人一般是平時(shí)不喜歡看書學(xué)習(xí),或者是之前剛工作的時(shí)候也沒有有經(jīng)驗(yàn)的人指導(dǎo)等等。
另外,撇開以上個(gè)人問題不說,目前市場上關(guān)于“互聯(lián)網(wǎng)業(yè)務(wù)系統(tǒng)設(shè)計(jì)”相關(guān)的文檔和書籍真是少之又少。用搜索引擎搜索“業(yè)務(wù)系統(tǒng)設(shè)計(jì)”關(guān)鍵字,出現(xiàn)的基本上都是系統(tǒng)宏觀架構(gòu)類的相關(guān)文章,或者就是各種傳統(tǒng)軟件管理系統(tǒng)的設(shè)計(jì)文檔等不適用的內(nèi)容,而目前市面上諸如軟件設(shè)計(jì)或者uml設(shè)計(jì)等之類的書有個(gè)共同點(diǎn)就是:“實(shí)在是太厚了,講的實(shí)在是太細(xì)了!”。如果按照書上面的步驟來,寫文檔就得寫上一周,根本適應(yīng)不了互聯(lián)網(wǎng)業(yè)務(wù)快速迭代,小步快跑的特點(diǎn)。
針對(duì)這些共性問題,結(jié)合我這些年的所學(xué),所感,所悟,寫成一篇文章,供大家一起學(xué)習(xí),若有不對(duì)之處,還望指正。
為什么要有設(shè)計(jì)
一個(gè)好的設(shè)計(jì)可以幫我們梳理業(yè)務(wù)邏輯且抓住核心需求,設(shè)計(jì)穩(wěn)定可擴(kuò)展的業(yè)務(wù)系統(tǒng),評(píng)估業(yè)務(wù)開發(fā)周期和開發(fā)成本,有效的規(guī)避風(fēng)險(xiǎn)。就如,蓋房子,咱得有建筑圖紙,有了圖紙,才能核算施工周期,施工成本等建筑指標(biāo)一樣。
如何進(jìn)行優(yōu)雅的設(shè)計(jì)
首先說說“優(yōu)雅”二字。之前在部門內(nèi)部也看過很多同學(xué)的設(shè)計(jì)文檔之類的,主要存在如下問題:
一上來就貼數(shù)據(jù)表的設(shè)計(jì);時(shí)序圖畫的不規(guī)范(沒有面向?qū)ο蟮乃枷耄嵊蔡?,前后毫無邏輯等等,這些都是源于沒有系統(tǒng)學(xué)習(xí)設(shè)計(jì)相關(guān)的知識(shí),其次不會(huì)畫圖,不懂如何用圖形正確的表達(dá)自己的思想和觀點(diǎn),導(dǎo)致輸出的東西讓人看不懂或者產(chǎn)生誤解,所以,“優(yōu)雅”二字很關(guān)鍵!
了解需求
了解需求就是跟業(yè)務(wù)方(一般是產(chǎn)品)進(jìn)行溝通:我們要做什么東西,這個(gè)東西包含哪些內(nèi)容,其中最核心的是哪塊兒,要達(dá)到什么效果,訪問量預(yù)估多少,后期的產(chǎn)品需求迭代方向是怎樣的。
一般在此過程中,產(chǎn)品會(huì)提供經(jīng)過產(chǎn)品內(nèi)部評(píng)審過的產(chǎn)品文檔,同時(shí),產(chǎn)品還會(huì)對(duì)產(chǎn)品文檔中的內(nèi)容進(jìn)行宣講。在產(chǎn)品宣講產(chǎn)品文檔之前,大家一定要仔細(xì)閱讀產(chǎn)品文檔中的內(nèi)容,對(duì)于不懂的地方及時(shí)跟產(chǎn)品進(jìn)行溝通;在產(chǎn)品宣講文檔的過程中,如果有不懂的地方,或者你自己針對(duì)其中某個(gè)問題有更好的解決方案,都要踴躍的提出來。在這種反反復(fù)復(fù)思辯的過程中,有利于培養(yǎng)業(yè)務(wù)工程師的產(chǎn)品意識(shí)。
注意,作為一名優(yōu)秀的工程師,針對(duì)產(chǎn)品需求,一定要學(xué)會(huì)問為什么,千萬不要淪為寫代碼的工具。
接下來,為了便于讀者能夠清晰的理解我說闡述的內(nèi)容,我以目前熱門直播圈的禮物系統(tǒng)的為例進(jìn)行闡述。
用例分析
消化產(chǎn)品文檔,借助“用例分析”這一手段,理解參與者和系統(tǒng)之間的關(guān)系,捕獲系統(tǒng)行為和方法,梳理用例場景,抓住核心需求,避免在設(shè)計(jì)和之后寫代碼的過程中本末倒置。另外,這也是評(píng)估你開發(fā)周期中必不可少的一個(gè)環(huán)節(jié)。
如下圖所示,用例圖中描述了禮物系統(tǒng)中有兩個(gè)參與者:用戶和后臺(tái)管理員;另外還有三個(gè)用例:禮物管理(創(chuàng)建禮物和修改禮物),獲取禮物列表和送禮物。

業(yè)務(wù)實(shí)體模型
前面介紹的用例主要用來描述我們要達(dá)到什么樣的目標(biāo),而業(yè)務(wù)實(shí)體代表參與者執(zhí)行用例時(shí)所使用的“事物”。而這個(gè)“事物”才正是我們所需要分析的核心概念。比如說,“先定個(gè)小目標(biāo),我先掙他一個(gè)億”。一個(gè)億是一個(gè)目標(biāo),但是如何掙到一個(gè)億才是我們真正需要分析的東西。
言歸正傳,我們還是以“禮物系統(tǒng)”為例,從產(chǎn)品層面來看,送禮物是最核心的用例,那么我們就以送禮物這個(gè)場景進(jìn)行分析。我們可以把這里用例映射到我們現(xiàn)實(shí)生活中,當(dāng)你遇到一個(gè)心儀的姑娘,給她送禮物的場景:在商店選禮物,付錢,買了后讓送貨小哥兒去送禮物,收到回執(zhí)(禮物已送達(dá)的通知)。通過以上場景的描述,我們就可以分析出“”送禮物“”這個(gè)過程所需要的業(yè)務(wù)實(shí)體,如下圖所示:

參考以上的分析,我們回到我們“”禮物系統(tǒng)“”中的送禮物場景,物流這類業(yè)務(wù)實(shí)體對(duì)應(yīng)的是將禮物消息傳遞給主播的消息通道;錢對(duì)應(yīng)的是虛擬貨幣;虛擬世界沒有回執(zhí)這類實(shí)體。那么,在禮物系統(tǒng)中,送禮物所對(duì)應(yīng)的實(shí)體模型圖如下:

值得注意的是,在此建模階段,我分析的思路:找一個(gè)生活中類似的場景進(jìn)行分析入手,看看生活中對(duì)應(yīng)的此類場景是怎樣的,達(dá)成這一目標(biāo)需要些什么東西,然后再以生活中的場景作為基礎(chǔ),結(jié)合業(yè)務(wù)特性,做進(jìn)一步分析。
針對(duì)我這類分析思路,有人可能會(huì)有以下問題:
你這樣分析這不是多此一舉嗎,如果要我設(shè)計(jì),我覺得可以直接由用例分析環(huán)節(jié)跳到時(shí)序圖環(huán)節(jié),送禮物嘛,挺簡單的一個(gè)場景?
對(duì),這位同學(xué)說的沒錯(cuò)。我這里之所以這樣描述主要是提供給大家一個(gè)分析問題的思路,如果系統(tǒng)簡單,或者設(shè)計(jì)者經(jīng)驗(yàn)豐富,就可以考慮略過這一步的。當(dāng)然,如果系統(tǒng)復(fù)雜,或者你沒有頭緒,可以考慮我這種分析思路,對(duì)理清系統(tǒng)關(guān)系非常有用。
時(shí)序圖
上一節(jié),我們分析了業(yè)務(wù)實(shí)體之間的關(guān)系,那么這一節(jié),我們將從執(zhí)行順序的角度出發(fā),描述實(shí)體對(duì)象之間的操作關(guān)系。這里我們還是以核心用例——送禮物為例, 從時(shí)序圖中,我們就知道自己需要做那些工作了。另外啰嗦一句,上一節(jié)我們分析的業(yè)務(wù)實(shí)體模型和時(shí)序圖中的業(yè)務(wù)對(duì)象不一定是一一對(duì)應(yīng)的關(guān)系。比如,在下圖中,就多出了倉庫這個(gè)對(duì)象,因?yàn)槎Y物的本身也是一種商品,既然是商品,很自然就有一個(gè)倉庫來存放和管理它。當(dāng)然,在最后寫代碼落地的過程中,你也可以把禮物相關(guān)信息放在Gift Center中來維護(hù),這個(gè)取決于平臺(tái)是否有“”倉庫“”這類系統(tǒng)以及未來對(duì)這類產(chǎn)品的規(guī)劃。咱們這里只是分析。

由上圖可見,整個(gè)送禮物的流程,依賴于多個(gè)外部系統(tǒng),因此這里需要注意:在實(shí)際做項(xiàng)目中,你需要優(yōu)先跟你需要合作的團(tuán)隊(duì)或者人打好招呼,確定好和外部的工作時(shí)間和工作量后,再來做自己份內(nèi)的事情。因?yàn)椋饨绲臇|西都不可控,自己的事情可控,萬一不提前打好招呼,你把自己手里的事兒忙完了,再跟其它團(tuán)隊(duì)打招呼時(shí),如果此時(shí)其它團(tuán)隊(duì)告訴你,這個(gè)事兒做不了,那你豈不是白忙活了!
此外,針對(duì)時(shí)序圖的畫法,我再舉一個(gè)我在工作中經(jīng)常遇到的錯(cuò)誤畫法,如下圖所示,注意紅圈處,圖中存在一個(gè)嚴(yán)重的錯(cuò)誤:出現(xiàn)了面向過程的畫法,沒有把倉庫當(dāng)成一個(gè)對(duì)象描述,而是一種面向過程的思路,缺乏面向?qū)ο蟮脑O(shè)計(jì)理念,另外,此階段還沒有考慮存儲(chǔ)選型呢,不一定是用Mysql來進(jìn)行存儲(chǔ)。

流程圖
當(dāng)梳理完時(shí)序圖后,如果有些地方需要用到算法之類的東西,或者在時(shí)序圖中某個(gè)操作有些特殊的細(xì)節(jié)需要具體描述的情況下,一般用流程圖來描述。比如說,在送禮物這個(gè)操作中,如果用戶連續(xù)送了10個(gè)以上的禮物,就自動(dòng)觸發(fā)該禮物的3D特效。那么對(duì)于這個(gè)關(guān)鍵細(xì)節(jié),咱們可以流程圖來描述,如下圖所示:

關(guān)于流程圖的規(guī)范我想啰嗦一下,大家在描述流程的時(shí)候,盡量用“數(shù)學(xué)語言”來描述,相比用文字描述,這樣比較言簡意賅,而且不會(huì)使人產(chǎn)生理解上的偏差。
實(shí)體圖
前面在業(yè)務(wù)實(shí)體建模和時(shí)序圖階段,我們分析了實(shí)體之間的關(guān)系,那么在此階段主要描述實(shí)體內(nèi)的屬性。在禮物系統(tǒng)中,我們只需要關(guān)注禮物本身的屬性即可。Gift Detail實(shí)體描述禮物明細(xì):某人在什么時(shí)候送了什么物品給誰,送了多少。Goods實(shí)體用于描述商品(禮物)本身的屬性,如創(chuàng)建時(shí)間,價(jià)格等,如果團(tuán)隊(duì)中沒有庫存系統(tǒng)之類的獨(dú)立系統(tǒng)來維護(hù)商品的話,可以暫時(shí)將商品(禮物)放在禮物系統(tǒng)中,等將來有需求,再拆分出去。
另外,有同學(xué)會(huì)問,實(shí)體圖中不是還應(yīng)該有描述操作屬性的方法嗎?對(duì),一般書中都會(huì)這么寫,但是,以我個(gè)人的經(jīng)驗(yàn),只需要屬性描述清楚,基本就可以確定一個(gè)實(shí)體,方法可寫也不可寫。

技術(shù)選型
語言選型
采用什么語言(php/java/lua/go/c++ 等)來編寫你的系統(tǒng)主要取決于兩方面:
1 系統(tǒng)需要承載多少訪問量,每個(gè)語言的特點(diǎn)都不一樣,都適用于不一樣的場景;
2 團(tuán)隊(duì)的風(fēng)格以及團(tuán)隊(duì)的積累。
由于我們團(tuán)隊(duì)負(fù)責(zé)的業(yè)務(wù)訪問量比較大,以及之前團(tuán)隊(duì)的積累,一般采用的都是golang。
存儲(chǔ)選型
存儲(chǔ)選型在整個(gè)業(yè)務(wù)系統(tǒng)的設(shè)計(jì)中是至關(guān)重要的,一般情況下,系統(tǒng)最先出現(xiàn)瓶頸的地方都是存儲(chǔ)這塊。針對(duì)存儲(chǔ)選型,我們主要從以下幾點(diǎn)來考慮:
1 實(shí)體對(duì)象中的數(shù)據(jù)需要用什么樣的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)才能滿足我們的查詢需求(這主要考驗(yàn)?zāi)銛?shù)據(jù)結(jié)構(gòu)的基本功底好不好了);
2 ?了解目前市面上的存儲(chǔ)組件(mysql,redis,ssdb,mongo,memcache等等)的特性,看看有哪些能夠滿足我們所設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu),能夠近似滿足即可,然后再根據(jù)存儲(chǔ)組件的特性對(duì)我們的數(shù)據(jù)結(jié)構(gòu)稍作修改。換句話說,我們?cè)O(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)是一種理論情況,而已有的存儲(chǔ)組件是現(xiàn)實(shí),當(dāng)理論與現(xiàn)實(shí)產(chǎn)生碰撞的時(shí)候,需要折中考慮。
3 實(shí)體對(duì)象中的數(shù)據(jù)是否需要持久化。
我們以Goods實(shí)體對(duì)象為例,從產(chǎn)品層面上分析,用戶會(huì)頻繁請(qǐng)求禮物列表,因此在系統(tǒng)內(nèi),此實(shí)體對(duì)象是讀多寫少的場景,既然是對(duì)查詢要求較高,那我們可以想想哪些數(shù)據(jù)結(jié)構(gòu)具有較高的查詢性能,比如,跳表、散列表、樹結(jié)構(gòu)。一般場景下,散列表的查詢性能較高,為O(1)。
我們確定數(shù)據(jù)結(jié)構(gòu)后,我們還需要考慮,實(shí)體對(duì)象中的數(shù)據(jù)是否需要持久化,由于禮物信息在系統(tǒng)重啟后,不能丟失,故需要持久化。然后,我們?cè)偃タ纯词忻嫔系倪@些流行的存儲(chǔ)組件,哪些滿足以上兩個(gè)特性,我們發(fā)現(xiàn)redis中的string結(jié)構(gòu)能夠跟我們?cè)O(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)相契合,而且是內(nèi)存級(jí)數(shù)據(jù)庫,性能好,并支持持久化。
因此,針對(duì)Goods實(shí)體對(duì)象數(shù)據(jù)的存儲(chǔ),我們選擇用redis。
在這里,容易出現(xiàn)的一個(gè)問題:有些人不去了解這些數(shù)據(jù)庫底層的實(shí)現(xiàn)方案,就輕易做出選擇。就如,在redis中string結(jié)構(gòu),hash結(jié)構(gòu)從功能上都可以滿足我們的需求。那么隨著數(shù)據(jù)量的增長,從性能上能否能繼續(xù)滿足我們的需求呢?這就要求我們工程師,知其一,還得知其二。只有深入的了解它,才能更好的使用它。
最后,確定存儲(chǔ)方案后,需要進(jìn)行方案驗(yàn)證。
降級(jí)策略(可選)
程序所依賴的宿主機(jī)和網(wǎng)絡(luò),不可能百分之百的不出問題,所以對(duì)于一些核心業(yè)務(wù)中的核心接口,需要有降級(jí)預(yù)案。比如數(shù)據(jù)庫掛了,網(wǎng)絡(luò)不通等諸如策略此類的問題,程序本身需要做降級(jí)策略。說白了,不論是做系統(tǒng)設(shè)計(jì)還是寫代碼,都得有防御式編程思想。一般常用策略如下:
1 程序需要多機(jī)器,多機(jī)房部署;
2 程序內(nèi)部,連接數(shù)據(jù)庫需要有超時(shí)時(shí)間,這個(gè)時(shí)間不宜過大;
3 以文件或者內(nèi)存的方式,保存上一次返回的正確結(jié)果。當(dāng)后端資源有問題的時(shí)候,讀取本地文件或者內(nèi)存中的數(shù)據(jù)。
4 需要一個(gè)手動(dòng)/自動(dòng)降級(jí)開關(guān),用于切斷對(duì)后端故障資源的訪問。避免因后端資源故障,導(dǎo)致接口響應(yīng)時(shí)間過長。
方案評(píng)審
項(xiàng)目設(shè)計(jì)完后,就邀請(qǐng)團(tuán)隊(duì)的噴子來噴一噴吧。
其它常見問題
缺乏設(shè)計(jì) vs 過渡設(shè)計(jì) vs 恰如其分的設(shè)計(jì)
有時(shí)候在進(jìn)行設(shè)計(jì)評(píng)審的時(shí)候,被評(píng)審的同學(xué)經(jīng)常會(huì)說這樣一句話:”我不做呢,說我缺乏設(shè)計(jì);我做了呢,又說我過渡設(shè)計(jì)”。被評(píng)審的同學(xué)真是左右為難。其實(shí)解決這個(gè)問題,關(guān)鍵在于一個(gè)度,掌握好這個(gè)“度”,便可以做到恰如其分的設(shè)計(jì)。
如何把握這個(gè)”度“?
我一般是這樣做的,滿足當(dāng)前產(chǎn)品需求,針對(duì)未來可能提出的需求(可能產(chǎn)品還沒想到)保留較好的擴(kuò)展性。一句話概括:你必須考慮到,但是你可以不做,讓系統(tǒng)保持一定的彈性。缺乏設(shè)計(jì)就是,你壓根兒就沒考慮到,導(dǎo)致的結(jié)果就是產(chǎn)品一加需求你就得大改或者重構(gòu)系統(tǒng);過渡設(shè)計(jì)就是,你考慮到了,你也做了,但是隨著產(chǎn)品的迭代,你發(fā)現(xiàn)你做的壓根兒沒用,并且還增加了系統(tǒng)復(fù)雜度和維護(hù)成本。
有人又會(huì)問,如何保證較好的擴(kuò)展性? 這就取決于你業(yè)務(wù)實(shí)體本身的設(shè)計(jì)了,保持一個(gè)設(shè)計(jì)原則:單一職責(zé)。(這一說就說到設(shè)計(jì)模式去了,推薦一本書《敏捷開發(fā)》)
理不清實(shí)體之間的邏輯我們應(yīng)該怎么辦
解釋這個(gè)問題,我想引用一句話:“藝術(shù)來源于生活又高于生活”。程序設(shè)計(jì)也是一門藝術(shù),程序也來源于生活。當(dāng)你理不清業(yè)務(wù)實(shí)體之間的關(guān)系時(shí),不妨將程序中的實(shí)體同生活中的實(shí)體對(duì)應(yīng)起來,看看他們?cè)谏钪惺窃鯓拥年P(guān)系。舉個(gè)簡單的例子,如果要你設(shè)計(jì)一個(gè)站內(nèi)信系統(tǒng),你理不清的時(shí)候,你可以想想生活中,信件的投遞是怎樣的。
建模工具
Enterprise Architect
推薦學(xué)習(xí)書籍
《UML精粹:標(biāo)準(zhǔn)對(duì)象建模語言簡明指南(第3版)》:此書不厚,適合入門,淺顯易懂,里面的圖都是用Enterprise Architect工具畫的,可以作為畫圖的工具書。
《大象:Thinking in UML(第2版)》:此書重點(diǎn)講如何建模,并從多個(gè)角度產(chǎn)生了面向過程和面向?qū)ο笾g的區(qū)別,以及使用場景。講的非常細(xì),非常厚??梢赃x擇性閱讀部分章節(jié)。
《敏捷開發(fā)-敏捷軟件開發(fā):原則、模式與實(shí)踐》:重點(diǎn)理解什么是敏捷設(shè)計(jì)。
《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》:領(lǐng)域抽象能力的內(nèi)功修煉。