前幾天和幾個(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)。
![aid-images.jianshu.io/upload_images/17194554-4d462c1fba4a5aab?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
例如組織架構(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)和類(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ū)佣傥苯釉L問(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)了一個(gè)架子屏箍,分包類似于在每一層架子上又放置了多個(gè)收納盒绘梦。所謂的內(nè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è)試。