張建飛 技術(shù)瑣話
前幾天和幾個(gè)餓了么的同學(xué)聊天,一聽(tīng)說(shuō)他們還在用COLA 1.0,我二話沒(méi)說(shuō),90度鞠躬,賠禮道歉,虛心聆聽(tīng)他們的吐槽。COLA的初衷旨在控制復(fù)雜度,救碼農(nóng)于水火,慚愧的是,早期的思想不成熟,設(shè)計(jì)也多有缺陷,不僅沒(méi)幫到他們,反而坑了他們,實(shí)在抱歉。
實(shí)際上,我在COLA 3.0迭代的時(shí)候,已經(jīng)舉起奧卡姆剃刀,砍掉了很多東西。
然而還不夠,主要體現(xiàn)在對(duì)架構(gòu)的思考還不夠透徹。再三考量,我覺(jué)得有必要對(duì)COLA進(jìn)行一次重新梳理,回歸初心,讓COLA真正成為應(yīng)用架構(gòu)的最佳實(shí)踐,幫助廣大的業(yè)務(wù)技術(shù)同學(xué),脫離醬缸代碼的泥潭!
應(yīng)用架構(gòu)的本質(zhì)
什么是架構(gòu)?十個(gè)人可能有十個(gè)回答,架構(gòu)在技術(shù)的語(yǔ)境下,就和架構(gòu)師一樣魔幻。我曾經(jīng)看過(guò)一本技術(shù)書(shū),用了一章的篇幅討論架構(gòu)的定義,最終也沒(méi)有說(shuō)明白。
實(shí)際上,定義架構(gòu)也沒(méi)那么難,如下圖所示,架構(gòu)的本質(zhì),簡(jiǎn)單來(lái)說(shuō),就是要素結(jié)構(gòu)。所謂的要素(Components)是指架構(gòu)中的主要元素,結(jié)構(gòu)是指要素之間的相互關(guān)系(Relationship)。
例如組織架構(gòu),其要素是什么?組成組織的要素當(dāng)然是人,結(jié)構(gòu)呢?結(jié)構(gòu)是人與人之間的關(guān)系。因此,組織架構(gòu)就是關(guān)于定義人的職責(zé)劃分,以及人與人之間協(xié)作關(guān)系的一種設(shè)計(jì)方法。
同樣,對(duì)于應(yīng)用架構(gòu)而言,代碼是其核心組成要素,結(jié)構(gòu)就是這些代碼該如何被組織,也就是要如何處理模塊(Module)、組件(Component)、包(Package)和類(lèi)(Class)之間的關(guān)系。簡(jiǎn)而言之,應(yīng)用架構(gòu)就是要解決代碼要如何被組織的問(wèn)題。
一個(gè)沒(méi)有架構(gòu)的應(yīng)用系統(tǒng),就像一堆隨意堆放、雜亂無(wú)章的玩具,只有熵值,沒(méi)有熵減。而一個(gè)有良好架構(gòu)的應(yīng)用系統(tǒng),有章法、有結(jié)構(gòu),一切都顯得井井有條。
好的組織架構(gòu)會(huì)遵循一定的架構(gòu)模式,大部分的組織都會(huì)按職能和業(yè)務(wù)來(lái)設(shè)計(jì)自己的架構(gòu)。如果你反其道而行之,硬要把銷(xiāo)售、財(cái)務(wù)和技術(shù)人員放在一個(gè)部門(mén),就會(huì)顯得很奇怪。
同樣,好的應(yīng)用架構(gòu),也遵循一些共同模式,不管是六邊形架構(gòu)、洋蔥圈架構(gòu)、整潔架構(gòu)、還是COLA架構(gòu),都提倡以業(yè)務(wù)為核心,解耦外部依賴,分離業(yè)務(wù)復(fù)雜度和技術(shù)復(fù)雜度。
應(yīng)用架構(gòu)的本質(zhì),就是要從繁雜的業(yè)務(wù)系統(tǒng)中提煉出共性,找到解決業(yè)務(wù)問(wèn)題的最佳共同模式,為開(kāi)發(fā)人員提供統(tǒng)一的認(rèn)知,治理混亂。幫助應(yīng)用系統(tǒng)“從混亂到有序”,COLA架構(gòu)就是為此而生,其核心職責(zé)就是定義良好的應(yīng)用結(jié)構(gòu),提供最佳實(shí)踐。
COLA 架構(gòu)
自從COLA誕生以來(lái),已經(jīng)被使用在很多的業(yè)務(wù)系統(tǒng)里面,有CRM的業(yè)務(wù),有電商的業(yè)務(wù),有物流的業(yè)務(wù),有外賣(mài)業(yè)務(wù),有排課系統(tǒng)…. COLA作為應(yīng)用架構(gòu),有一定的普適性,是因?yàn)闃I(yè)務(wù)問(wèn)題都有一定的共性。例如,典型的業(yè)務(wù)系統(tǒng)都需要:
• 接收request,響應(yīng)response;
• 做業(yè)務(wù)邏輯處理,像校驗(yàn)參數(shù),狀態(tài)流轉(zhuǎn),業(yè)務(wù)計(jì)算等等;
• 和外部系統(tǒng)有聯(lián)動(dòng),像數(shù)據(jù)庫(kù),微服務(wù),搜索引擎等;
正是有這樣的共性存在,才會(huì)有很多普適的架構(gòu)思想出現(xiàn),比如分層架構(gòu)、六邊形架構(gòu)、洋蔥圈架構(gòu)、整潔架構(gòu)(Clean Architecture)、DDD架構(gòu)等等。
這些應(yīng)用架構(gòu)思想雖然很好,但我們很多同學(xué)還是“不講Co德,明白了很多道理,可還是過(guò)不好這一生”。問(wèn)題就在于缺乏實(shí)踐和指導(dǎo)。COLA的意義就在于,他不僅是思想,還提供了可落地的實(shí)踐。應(yīng)該是為數(shù)不多的應(yīng)用架構(gòu)層面的開(kāi)源軟件。
分層結(jié)構(gòu)
假如你是一個(gè)公司的CTO要管100號(hào)人,你怎么管?按照管理學(xué)的定義,一個(gè)人的管理幅度如果超過(guò)10個(gè),管理就會(huì)變得很困難。因此,管100號(hào)人,你可以把他們分成10個(gè)小組,這樣你管理10個(gè)小組長(zhǎng)就好了。
所有的復(fù)雜系統(tǒng)都會(huì)呈現(xiàn)出層級(jí)結(jié)構(gòu),管理如此,軟件設(shè)計(jì)也不例外,你能想象如果網(wǎng)絡(luò)協(xié)議不是四層,而是一層,意味著,你要在應(yīng)用層去處理鏈路層的bit數(shù)據(jù)流會(huì)是怎樣的情景嗎?同樣,應(yīng)用系統(tǒng)處理復(fù)雜業(yè)務(wù)邏輯也應(yīng)該是分層的,下層對(duì)上層屏蔽處理細(xì)節(jié),每一層各司其職,分離關(guān)注點(diǎn),而不是一個(gè)ServiceImpl解決所有問(wèn)題。
對(duì)于一個(gè)典型的業(yè)務(wù)應(yīng)用系統(tǒng)來(lái)說(shuō),COLA會(huì)做如下層次定義,每一層都有明確的職責(zé)定義:
1)適配層(Adapter Layer):負(fù)責(zé)對(duì)前端展示(web,wireless,wap)的路由和適配,對(duì)于傳統(tǒng)B/S系統(tǒng)而言,adapter就相當(dāng)于MVC中的controller;
2)應(yīng)用層(Application Layer):主要負(fù)責(zé)獲取輸入,組裝上下文,參數(shù)校驗(yàn),調(diào)用領(lǐng)域?qū)幼鰳I(yè)務(wù)處理,如果需要的話,發(fā)送消息通知等。層次是開(kāi)放的,應(yīng)用層也可以繞過(guò)領(lǐng)域?qū)?,直接訪問(wèn)基礎(chǔ)實(shí)施層;
3)領(lǐng)域?qū)樱―omain Layer):主要是封裝了核心業(yè)務(wù)邏輯,并通過(guò)領(lǐng)域服務(wù)(Domain Service)和領(lǐng)域?qū)ο螅―omain Entity)的方法對(duì)App層提供業(yè)務(wù)實(shí)體和業(yè)務(wù)邏輯計(jì)算。領(lǐng)域是應(yīng)用的核心,不依賴任何其他層次;
4)基礎(chǔ)實(shí)施層(Infrastructure Layer):主要負(fù)責(zé)技術(shù)細(xì)節(jié)問(wèn)題的處理,比如數(shù)據(jù)庫(kù)的CRUD、搜索引擎、文件系統(tǒng)、分布式服務(wù)的RPC等。此外,領(lǐng)域防腐的重任也落在這里,外部依賴需要通過(guò)gateway的轉(zhuǎn)義處理,才能被上面的App層和Domain層使用。
包結(jié)構(gòu)
分層是屬于大粒度的職責(zé)劃分,太粗,我們有必要往下再down一層,細(xì)化到包結(jié)構(gòu)的粒度,才能更好的指導(dǎo)我們的工作。
還是拿一堆玩具舉例子,分層類(lèi)似于拿來(lái)了一個(gè)架子,分包類(lèi)似于在每一層架子上又放置了多個(gè)收納盒。所謂的內(nèi)聚,就是把功能類(lèi)似的玩具放在一個(gè)盒子里,這樣可以讓?xiě)?yīng)用結(jié)構(gòu)清晰,極大的降低系統(tǒng)的認(rèn)知成本和維護(hù)成本。
那么,對(duì)于一個(gè)后端應(yīng)用來(lái)說(shuō),應(yīng)該需要哪些收納盒呢?這一塊的設(shè)計(jì)真可謂是費(fèi)了老鼻子勁了,基本上每一次COLA的迭代都會(huì)涉及到包結(jié)構(gòu)的調(diào)整,迭代到現(xiàn)在,才算基本穩(wěn)定下來(lái)。
各個(gè)包結(jié)構(gòu)的簡(jiǎn)要功能描述,如下表所示:
層次
包名
功能
必選
Adapter層
web
處理頁(yè)面請(qǐng)求的Controller
否
Adapter層
wireless
處理無(wú)線端的適配
否
Adapter層
wap
處理wap端的適配
否
App層
executor
處理request,包括command和query
是
App層
consumer
處理外部message
否
App層
scheduler
處理定時(shí)任務(wù)
否
Domain層
model
領(lǐng)域模型
否
Domain層
ability
領(lǐng)域能力,包括DomainService
否
Domain層
gateway
領(lǐng)域網(wǎng)關(guān),解耦利器
是
Infra層
gatewayimpl
網(wǎng)關(guān)實(shí)現(xiàn)
是
Infra層
mapper
ibatis數(shù)據(jù)庫(kù)映射
否
Infra層
config
配置信息
否
Client SDK
api
服務(wù)對(duì)外透出的API
是
Client SDK
dto
服務(wù)對(duì)外的DTO
是
你可能會(huì)有疑問(wèn),為什么Domain的model是可選的?因?yàn)镃OLA是應(yīng)用架構(gòu),不是DDD架構(gòu)。在工作中,很多同學(xué)問(wèn)我領(lǐng)域模型要怎么設(shè)計(jì),我的回答通常是:無(wú)有必要勿增實(shí)體。領(lǐng)域模型對(duì)設(shè)計(jì)能力要求很高,沒(méi)把握用好,一個(gè)錯(cuò)誤的抽象還不如不抽象,寧可不要用,也不要濫用,不要為了DDD而DDD。
問(wèn)題的關(guān)鍵是要看,新增的模型沒(méi)有給你帶來(lái)收益。比如有沒(méi)有幫助系統(tǒng)解耦,有沒(méi)有提升業(yè)務(wù)語(yǔ)義表達(dá)能力的提升,有沒(méi)有提升系統(tǒng)的可維護(hù)性和可測(cè)性等等。
模型雖然可選,但DDD的思想是一定要去學(xué)習(xí)和貫徹的,特別是統(tǒng)一語(yǔ)言、邊界上下文、防腐層的思想,值得深入學(xué)習(xí),仔細(xì)體會(huì)。實(shí)際上,COLA里面的很多設(shè)計(jì)思想都來(lái)自于DDD。其中就包括領(lǐng)域包的設(shè)計(jì)。
前面的包定義,都是功能維度的定義。為了兼顧領(lǐng)域維度的內(nèi)聚性,我們有必要對(duì)包結(jié)構(gòu)進(jìn)行一下微調(diào),即頂層包結(jié)構(gòu)應(yīng)該是按照領(lǐng)域劃分,讓領(lǐng)域內(nèi)聚。
也就是說(shuō),我們要綜合考慮功能和領(lǐng)域兩個(gè)維度包結(jié)構(gòu)定義。按照領(lǐng)域和功能兩個(gè)維度分包策略,最后呈現(xiàn)出來(lái)的,是如下圖所示的頂層包節(jié)點(diǎn)是領(lǐng)域名稱,領(lǐng)域之下,再按功能劃分包結(jié)構(gòu)。
例如,在我們剛剛上線的一個(gè)云店鋪(cloudstore)項(xiàng)目中,按照COLA的分包策略,我們?cè)诿恳粋€(gè)module下面首先按照領(lǐng)域做一個(gè)頂層劃分,然后在領(lǐng)域內(nèi),再按照功能進(jìn)行分包。
解耦
“高內(nèi)聚,低耦合”這句話,你工作的越久,就越會(huì)覺(jué)得其有道理。
所謂耦合就是聯(lián)系的緊密程度,只要有依賴就會(huì)有耦合,不管是進(jìn)程內(nèi)的依賴,還是跨進(jìn)程的RPC依賴,都會(huì)產(chǎn)生耦合。依賴不可消除,同樣,耦合也不可避免。我們所能做的不是消除耦合,而是把耦合降低到可以接受的程度。在軟件設(shè)計(jì)中,有大量的設(shè)計(jì)模式,設(shè)計(jì)原則都是為了解耦這一目的。
在DDD中有一個(gè)很棒的解耦設(shè)計(jì)思想——防腐層(Anti-Corruption),簡(jiǎn)單說(shuō),就是應(yīng)用不要直接依賴外域的信息,要把外域的信息轉(zhuǎn)換成自己領(lǐng)域上下文(Context)的實(shí)體再去使用,從而實(shí)現(xiàn)本域和外部依賴的解耦。
在COLA中,我們把AC這個(gè)概念進(jìn)行了泛化,將數(shù)據(jù)庫(kù)、搜索引擎等數(shù)據(jù)存儲(chǔ)都列為外部依賴的范疇。利用依賴倒置,統(tǒng)一使用gateway來(lái)實(shí)現(xiàn)業(yè)務(wù)領(lǐng)域和外部依賴的解耦。
其實(shí)現(xiàn)方式如下圖所示,主要是在Domain層定義Gateway接口,然后在Infrastructure提供Gateway接口的實(shí)現(xiàn)。
舉個(gè)例子,假如有一個(gè)電商系統(tǒng),對(duì)于下單這個(gè)操作,它需要聯(lián)動(dòng)訂單服務(wù)、商品服務(wù)、庫(kù)存服務(wù)、營(yíng)銷(xiāo)服務(wù)等多個(gè)系統(tǒng)才能完成。
那么在訂單域,該如何獲取商品和庫(kù)存信息呢?最直接的方式,無(wú)外乎就是RPC調(diào)用商品和庫(kù)存服務(wù),拿到DTO直接使用就完了。
然而,商品域吐出的是一個(gè)大而全的DTO(可能包含幾十個(gè)字段),而在下單這個(gè)階段,訂單所需要的可能只是其中幾個(gè)字段而已。更合適的做法,應(yīng)該是在訂單域中,使用gateway對(duì)商品域和庫(kù)存域的依賴進(jìn)行解耦。
這樣做有兩個(gè)好處,一個(gè)是降低了對(duì)外域信息依賴的耦合;另一個(gè)是通過(guò)上下文映射(Context mapping),確保本領(lǐng)域邊界上下文(Bounded context)下領(lǐng)域知識(shí)的完整性,實(shí)現(xiàn)了統(tǒng)一語(yǔ)言(Ubiquitous language)。
COLA Archetype
以上就是COLA架構(gòu)的核心內(nèi)容了。然而這么多module,這么多package,如果要手動(dòng)去創(chuàng)建的話,是非常繁瑣和費(fèi)時(shí)的。為了能夠快速創(chuàng)建滿足COLA架構(gòu)的應(yīng)用,我創(chuàng)建了兩個(gè)Maven Archetype。
1. 一個(gè)是用來(lái)創(chuàng)建純后端服務(wù)的archetype:cola-archetype-service。
2. 一個(gè)是用來(lái)創(chuàng)建adapter和后端服務(wù)一體的web應(yīng)用archetype:cola-archetype-web。
另外,你也可以使用阿里云的應(yīng)用生成器去生成一個(gè)COLA應(yīng)用,只是那邊的版本沒(méi)有同步更新,可能會(huì)老舊一點(diǎn)。
COLA組件
使用過(guò)老版本COLA的同學(xué),應(yīng)該知道,COLA除了架構(gòu)之外,還提供了一些框架級(jí)別的功能,比如攔截器功能,擴(kuò)展點(diǎn)功能等。
之前,這種框架功能和架構(gòu)混淆在一起,會(huì)讓人以為使用COLA,就必須要使用這些功能。實(shí)際上二者是可以分開(kāi)使用的,也就是說(shuō),你可以單純的使用COLA架構(gòu),而不使用任何COLA組件提供的功能也是完全沒(méi)問(wèn)題的。
當(dāng)然,我還是強(qiáng)烈推薦你可以有選擇的使用這些COLA組件,畢竟這些組件都是我們?cè)趯?shí)際工作中的總結(jié)沉淀,其復(fù)用性和價(jià)值是被反復(fù)驗(yàn)證過(guò)的。
為了方便管理,以及更清晰的把架構(gòu)和框架區(qū)分開(kāi)來(lái)。在此次COLA 4.0的升級(jí)中,我把這些功能組件全部收攏到了cola-components下面。到目前為止,我們已經(jīng)沉淀了以下組件:
組件名稱
功能
版本
依賴
cola-component-dto
定義了DTO格式,包括分頁(yè)
1.0.0
無(wú)
cola-component-exception
定義了異常格式,主要有BizException和SysException
1.0.0
無(wú)
cola-component-statemachine
狀態(tài)機(jī)組件
1.0.0
無(wú)
cola-component-domain-starter
Spring托管的領(lǐng)域?qū)嶓w組件
1.0.0
無(wú)
cola-component-catchlog-starter
異常處理和日志組件
1.0.0
exception,dto組件
cola-component-extension-starter
擴(kuò)展點(diǎn)組件
1.0.0
無(wú)
cola-component-test-container
測(cè)試容器組件
1.0.0
無(wú)
這些組件是一個(gè)良好的開(kāi)端,我相信,在未來(lái)會(huì)有更多有用的組件加入。當(dāng)然,作為一個(gè)開(kāi)源項(xiàng)目,如果你有好的組件idea,歡迎你隨時(shí)為這個(gè)組件庫(kù)添磚加瓦。
COLA 4.0
總結(jié)一下,在本次COLA升級(jí)中,我們進(jìn)一步明確了架構(gòu)和框架功能的定義。升級(jí)之后,如下圖所示,COLA會(huì)被分成COLA架構(gòu)和COLA組件兩個(gè)部分:
1. COLA架構(gòu):關(guān)注應(yīng)用架構(gòu)的定義和構(gòu)建,提升應(yīng)用質(zhì)量。
2. COLA組件:提供應(yīng)用開(kāi)發(fā)所需要的可復(fù)用組件,提升研發(fā)效率。
image.png
COLA 開(kāi)源地址: https://github.com/alibaba/COLA
你可以按照以下步驟去使用COLA:
** 第一步:安裝 cola archetype ** 下載cola-archetypes下的源碼到本地,然后本地運(yùn)行mvn install安裝。
** 第二步:安裝 cola components ** 下載cola-components下的源碼到本地,然后本地運(yùn)行mvn install安裝。
** 第三步:創(chuàng)建應(yīng)用 ** 執(zhí)行以下命令:
mvn archetype:generate -DgroupId=com.alibaba.demo -DartifactId=demoWeb -Dversion=1.0.0-SNAPSHOT -Dpackage=com.alibaba.demo -DarchetypeArtifactId=cola-framework-archetype-web -DarchetypeGroupId=com.alibaba.cola -DarchetypeVersion=4.0.0
命令執(zhí)行成功的話,會(huì)看到如下的應(yīng)用代碼結(jié)構(gòu):
** 第四步:運(yùn)行應(yīng)用 ** 首先在demoWeb目錄下運(yùn)行mvn install(如果不想運(yùn)行測(cè)試,可以加上-DskipTests參數(shù))。然后進(jìn)入start目錄,執(zhí)行mvn spring-boot:run。運(yùn)行成功的話,可以看到SpringBoot啟動(dòng)成功的界面。
生成的應(yīng)用中,已經(jīng)實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的Rest請(qǐng)求,可以在瀏覽器中輸入http://localhost:8080/helloworld 進(jìn)行測(cè)試。
作者簡(jiǎn)介:
阿里巴巴高級(jí)技術(shù)專(zhuān)家,張建飛。著有《代碼精進(jìn)之路:從碼農(nóng)到工匠》,憑借此書(shū),獲得2020年人民郵電出版社IT類(lèi)最佳作者。是開(kāi)源應(yīng)用架構(gòu)COLA的作者,擅長(zhǎng)應(yīng)用架構(gòu)和領(lǐng)域建模。更多技術(shù)精華內(nèi)容,請(qǐng)關(guān)注作者的微信公眾號(hào):從碼農(nóng)到工匠。






