0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

循序漸進(jìn)搭建復(fù)雜B端系統(tǒng)整潔架構(gòu)

京東云 ? 來(lái)源:jf_75140285 ? 作者:jf_75140285 ? 2024-11-18 17:11 ? 次閱讀

前言:信息時(shí)代技術(shù)更迭和傳播速度不斷加快,技術(shù)變得泛娛樂(lè)化,大數(shù)據(jù)、云計(jì)算區(qū)塊鏈、元宇宙、大模型,一代代技術(shù)熱點(diǎn)在社會(huì)輿論的裹挾之下不斷地吸引著資本的眼球,技術(shù)人員為了不被時(shí)代所淘汰也不得不時(shí)刻追趕潮流。在這樣一個(gè)時(shí)代背景下,軟件工程作為一門不起眼到有些枯燥的古老學(xué)科,似乎早已被開發(fā)者們遺忘在角落。作為一名技術(shù)人員我們自然應(yīng)該時(shí)刻保持對(duì)前沿技術(shù)的追蹤,然而,當(dāng)發(fā)生線上問(wèn)題我們卻面對(duì)著成片的屎山代碼毫無(wú)頭緒時(shí);當(dāng)業(yè)務(wù)方提出個(gè)性化需求我們卻因?yàn)椴桓覍?duì)系統(tǒng)做出修改而強(qiáng)迫對(duì)方做出妥協(xié)時(shí);當(dāng)一次請(qǐng)求處理流程中出現(xiàn)多達(dá)數(shù)萬(wàn)次重復(fù)地?cái)?shù)據(jù)庫(kù)操作而影響到整個(gè)系統(tǒng)的穩(wěn)定性時(shí),大家都應(yīng)該沉下心來(lái)思考一下,我們是不是忘記了作為一名程序員的初心和對(duì)代碼的極致追求。 還記的當(dāng)年我抱著朝圣的心態(tài)從傳統(tǒng)行業(yè)踏入京東職場(chǎng)時(shí)的興奮與期待,然而這份期待很快就被四處可見的屎山代碼給澆滅了,后來(lái)從朋友口中了解到其他頭部互聯(lián)網(wǎng)廠商的業(yè)務(wù)系統(tǒng)其實(shí)也是半斤八兩。這似乎是軟件行業(yè)中的一個(gè)電車難題,一邊是無(wú)盡的業(yè)務(wù)需求和倒排的工期,一邊是補(bǔ)丁摞補(bǔ)丁的糟糕代碼,是繼續(xù)泡在醬缸中縫縫補(bǔ)補(bǔ)還是向屎山代碼說(shuō)不,開發(fā)人員被困在中間不知該如何抉擇。然而事實(shí)上,追求整潔架構(gòu)與提升研發(fā)效率之間從來(lái)就不是一個(gè)悖論。正如Robert C.Martin在其著作《Clean Architecture》中所說(shuō):“不管你多敬業(yè)、加多少班,(在面對(duì)爛系統(tǒng)時(shí))你仍然會(huì)寸步難行,因?yàn)槟愦蟛糠值木κ窃趹?yīng)對(duì)混亂(而不是在開發(fā)需求)。”造成我們整日加班趕需求和疲于應(yīng)對(duì)線上問(wèn)題的根本原因,恰是那些不被我們重視的糟糕代碼。業(yè)務(wù)天然就是復(fù)雜的,這決定了軟件系統(tǒng)的本質(zhì)復(fù)雜度(Essential Complexity),這種復(fù)雜度是無(wú)法通過(guò)軟件架構(gòu)去消除的。那么解決上述問(wèn)題的關(guān)鍵就是找到某種架構(gòu)去引導(dǎo)開發(fā)者對(duì)復(fù)雜業(yè)務(wù)進(jìn)行問(wèn)題拆解,分而治之,在這個(gè)基礎(chǔ)上再通過(guò)標(biāo)準(zhǔn)規(guī)約和工具約束及輔助開發(fā)者寫出可理解、易拓展、好維護(hù)的代碼,以此來(lái)對(duì)抗軟件系統(tǒng)本身的偶然復(fù)雜度(Accidental Complexity,F(xiàn)rederick P.Brooks,Jr, 《The Mythical Man-Month》)。 為了找到這樣的一種架構(gòu),我們從19年就開始對(duì)各類架構(gòu)思想和實(shí)踐案例進(jìn)行了深入地學(xué)習(xí)和探索,并在接下來(lái)的3年時(shí)間里通過(guò)局部架構(gòu)演進(jìn)的方式進(jìn)行了大量的實(shí)踐驗(yàn)證,在這個(gè)過(guò)程中我們對(duì)這些架構(gòu)思想的理解也從早期的懵懂教條式執(zhí)行逐漸做到了如今的融匯貫通,并最終在22年底形成了一套成體系的框架及方法論,并在京東廣告投放平臺(tái)重構(gòu)工作中進(jìn)行了實(shí)戰(zhàn)應(yīng)用。本文也將以廣告投放平臺(tái)架構(gòu)升級(jí)作為背景案例,從設(shè)計(jì)思想到落地框架,循序漸進(jìn)地為您介紹這套新架構(gòu)的誕生始末,而這套架構(gòu)思想的演進(jìn)歷程則在《改進(jìn)我們的架構(gòu)》一文中有詳細(xì)的闡述。

一、架構(gòu)升級(jí)背景

與高并發(fā)請(qǐng)求給C端系統(tǒng)帶來(lái)的系統(tǒng)高性能、高可用能力挑戰(zhàn)相比,B端系統(tǒng)所面臨的挑戰(zhàn)則是如何在海量多維度、多模塊、多場(chǎng)景融合的復(fù)雜業(yè)務(wù)需求中保持系統(tǒng)健康、穩(wěn)定、快速地迭代。京東廣告投放平臺(tái)就是一個(gè)典型的復(fù)雜B端業(yè)務(wù)系統(tǒng),它承擔(dān)著集成廣告業(yè)務(wù)體系中各個(gè)垂直業(yè)務(wù)模塊,構(gòu)建、維護(hù)和分發(fā)廣告物料的重要職責(zé)。經(jīng)過(guò)多年迭代,京東廣告投放系統(tǒng)目前已集成40余個(gè)垂直業(yè)務(wù)系統(tǒng),支撐7條核心產(chǎn)品線,先后賦能10余個(gè)獨(dú)立投放平臺(tái),維護(hù)著一個(gè)擁有200余個(gè)業(yè)務(wù)實(shí)體的龐大數(shù)據(jù)模型,每天都需要處理海量長(zhǎng)事務(wù)、多系統(tǒng)交互的復(fù)雜業(yè)務(wù)請(qǐng)求。同時(shí)作為整個(gè)廣告業(yè)務(wù)鏈路上的首發(fā)環(huán)節(jié)和功能門面,廣告投放系統(tǒng)每年都要承接400余個(gè)來(lái)自不同業(yè)務(wù)方的差異化需求、執(zhí)行1000余次代碼合并及600余次功能發(fā)布。在極高的需求密度之下,作為撬動(dòng)廣告主預(yù)算的重要戰(zhàn)場(chǎng),投放系統(tǒng)在為廣告主提供優(yōu)秀投放體驗(yàn)的同時(shí),還需要每天向廣告業(yè)務(wù)鏈路穩(wěn)定輸送PB級(jí)的物料數(shù)據(jù),這對(duì)系統(tǒng)的性能、穩(wěn)定性以及團(tuán)隊(duì)的研發(fā)效能提出了極高的要求。

wKgaomc7BH6AVYDVAANn0HkT3GI202.jpg

廣告投放平臺(tái)是一個(gè)典型的多平臺(tái)、多模塊集成的復(fù)雜B端系統(tǒng)

二、傳統(tǒng)架構(gòu)的研發(fā)痛點(diǎn)

近年來(lái)隨著技術(shù)和業(yè)務(wù)的飛速發(fā)展,新的廣告業(yè)務(wù)形態(tài)和投放組件層出不窮,廣告物料結(jié)構(gòu)愈發(fā)復(fù)雜。與此同時(shí),為了提高廣告主留存和撬動(dòng)預(yù)算,業(yè)界各大平臺(tái)都在向著極簡(jiǎn)版、智能化和集成化的方向發(fā)展。這些新的業(yè)態(tài)發(fā)展方向一方面給廣告主帶來(lái)了更加便捷和流暢的投放體驗(yàn),另一方面也讓投放系統(tǒng)內(nèi)部業(yè)務(wù)流程愈發(fā)復(fù)雜,如何用有限的研發(fā)人力快速支撐越來(lái)越多的多場(chǎng)景復(fù)雜業(yè)務(wù)需求成為各大廣告投放平臺(tái)必須要解決的關(guān)鍵問(wèn)題。然而傳統(tǒng)的“三層架構(gòu)+面向數(shù)據(jù)庫(kù)編程”的研發(fā)模式由于過(guò)于簡(jiǎn)單的封裝及粗暴的設(shè)計(jì)思想在面對(duì)這些高復(fù)雜度業(yè)務(wù)需求時(shí)變得愈發(fā)吃力,逐漸成為阻塞研發(fā)效能提升的罪魁禍?zhǔn)住?/p>

客觀:傳統(tǒng)架構(gòu)面對(duì)高復(fù)雜度的業(yè)務(wù)時(shí)毫無(wú)應(yīng)對(duì)之法

作為一個(gè)典型的Web應(yīng)用,廣告投放系統(tǒng)長(zhǎng)期以來(lái)采用的都是傳統(tǒng)的三層架構(gòu),這種沒(méi)有架構(gòu)的架構(gòu)極其簡(jiǎn)單、易上手,因此一直以來(lái)都是業(yè)界的主流。但是由于它缺少統(tǒng)一明確的邏輯拆分與封裝工具,業(yè)務(wù)的復(fù)雜度會(huì)等比滲透到代碼實(shí)現(xiàn)中,進(jìn)而導(dǎo)致系統(tǒng)的代碼復(fù)雜度飆升,模塊之間隨意耦合,邏輯糾結(jié)纏繞,經(jīng)過(guò)幾輪迭代之后就成了看不懂、動(dòng)不了、不敢動(dòng)的醬缸代碼。這些看似基礎(chǔ)的編碼問(wèn)題實(shí)際上卻是阻礙我們研發(fā)效能提升的罪魁禍?zhǔn)祝?/p>

1.需求交付提速困難:不同平臺(tái)、產(chǎn)品線及業(yè)務(wù)場(chǎng)景邏輯交織,晦澀難懂,導(dǎo)致系統(tǒng)功能迭代時(shí)梳理及設(shè)計(jì)耗時(shí)漫長(zhǎng),同時(shí)在測(cè)試階段需投入大量精力進(jìn)行聯(lián)動(dòng)功能回歸;

2.對(duì)新業(yè)態(tài)的接受度低、響應(yīng)能力差:系統(tǒng)拓展性差,且模塊間深度耦合,在面對(duì)新業(yè)態(tài)時(shí)我們卻為了控制影響范圍而不得讓業(yè)務(wù)選擇讓步,結(jié)果錯(cuò)失商機(jī)。

3.問(wèn)題評(píng)估和排查效率低下:缺少明確統(tǒng)一的邏輯歸屬與封裝準(zhǔn)則,邏輯四處復(fù)寫與逃逸,導(dǎo)致問(wèn)題定位時(shí)間長(zhǎng),難以快速評(píng)估影響范圍和修復(fù)方案。

4.接口性能與穩(wěn)定性下降:混亂的封裝與復(fù)用導(dǎo)致一次接口請(qǐng)求就會(huì)產(chǎn)生導(dǎo)致大量重復(fù)的IO操作,嚴(yán)重影響接口性能,每臨大促都需要花費(fèi)大量人力進(jìn)行性能優(yōu)化。

主觀:“面向數(shù)據(jù)庫(kù)編程”的設(shè)計(jì)思維讓系統(tǒng)加速腐化

我們的業(yè)務(wù)本質(zhì)就是獲取、處理、存儲(chǔ)及傳輸數(shù)據(jù),在傳統(tǒng)架構(gòu)中業(yè)務(wù)邏輯通常以事務(wù)腳本(Transaction Script)的形式實(shí)現(xiàn):業(yè)務(wù)規(guī)則直接在開發(fā)者的大腦中轉(zhuǎn)化為數(shù)據(jù)庫(kù)的增刪改查操作(這也是很多程序員調(diào)侃自己是CRUD工程師的原因),然后被寫到代碼里。這種模式在場(chǎng)景單一、需求簡(jiǎn)單的業(yè)務(wù)發(fā)展早期階段可以快速實(shí)現(xiàn)功能,但是隨著業(yè)務(wù)復(fù)雜度的提升,這種過(guò)于粗糙的設(shè)計(jì)思維所帶來(lái)的問(wèn)題就會(huì)逐漸顯現(xiàn)出來(lái):

1.難以建立對(duì)整個(gè)數(shù)據(jù)模型的全景認(rèn)知:完整的數(shù)據(jù)模型信息被拆分到不同的業(yè)務(wù)接口實(shí)現(xiàn)中,往往需要對(duì)整個(gè)工程代碼進(jìn)行逐行review才能梳理出完整的數(shù)據(jù)模型,當(dāng)工程代碼量和數(shù)據(jù)模型膨脹到一定程度后,模型梳理成本急劇飆升。

2.模型野蠻膨脹、存在大量相似或重復(fù)的實(shí)體,增加系統(tǒng)運(yùn)維成本:數(shù)據(jù)模型全景認(rèn)知的缺失導(dǎo)致開發(fā)者難以進(jìn)行統(tǒng)一的頂層設(shè)計(jì),數(shù)據(jù)模型泛化表達(dá)能力弱,在多需求并行開發(fā)過(guò)程中極易形成信息孤島,無(wú)法實(shí)現(xiàn)模型合并與共享,系統(tǒng)中存在大量相似的業(yè)務(wù)實(shí)體與庫(kù)表結(jié)構(gòu)。

3.代碼對(duì)業(yè)務(wù)語(yǔ)義表達(dá)能力弱、業(yè)務(wù)知識(shí)傳承效率低下:代碼經(jīng)過(guò)開發(fā)者的轉(zhuǎn)譯失去了對(duì)業(yè)務(wù)語(yǔ)義的直接表達(dá),導(dǎo)致系統(tǒng)中存在大量只有開發(fā)者本人才能理解的魔法邏輯,系統(tǒng)維護(hù)與人員更迭成本過(guò)高。

要想解決上述問(wèn)題,就亟需一種面向未來(lái)的架構(gòu)思想來(lái)指導(dǎo)我們對(duì)系統(tǒng)進(jìn)行全面地升級(jí)。在此背景下,業(yè)界眾多平臺(tái)紛紛進(jìn)行了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的探索和嘗試,經(jīng)典的案例有阿里的星環(huán)與COLA、快手的Baldr等,京東也推出了藏經(jīng)閣平臺(tái)與Matrix框架。這些實(shí)踐案例和架構(gòu)迭代路線給了我們很多啟發(fā),本著腳踏實(shí)地、事實(shí)就是的基本原則,在經(jīng)過(guò)充分調(diào)研和長(zhǎng)期驗(yàn)證之后,我們立足于京東廣告業(yè)務(wù)的本質(zhì)特征推出了一套可復(fù)用的復(fù)雜B端業(yè)務(wù)支撐框架,其核心內(nèi)容可以分為PICASO能力編排框架與聚合及資源庫(kù)機(jī)制兩部分。

網(wǎng)絡(luò)上能夠找到很多介紹領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的文章,但是大多都聚焦在對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中眾多術(shù)語(yǔ)和概念的介紹上,對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的落地實(shí)踐卻淺嘗輒止。再加上中英文語(yǔ)境的差異和國(guó)內(nèi)外軟件開發(fā)生態(tài)的不同,都在很大程度上將領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想“妖魔化”了,讓很多同學(xué)望而卻步或者不得其要義。然而我們?cè)诿鲗?shí)踐的過(guò)程中逐漸意識(shí)到,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)作為一種軟件架構(gòu)設(shè)計(jì)的指導(dǎo)思想其實(shí)并沒(méi)有創(chuàng)造什么新的東西,而是對(duì)基本的軟件設(shè)計(jì)思想進(jìn)行的系統(tǒng)化總結(jié)和升華。但正是這種系統(tǒng)性的歸納將各類技巧、準(zhǔn)則和思想凝練成體系化的方法論,并且在行業(yè)內(nèi)形成了被所有開發(fā)者所公認(rèn)的行為準(zhǔn)則,這才是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想強(qiáng)大生產(chǎn)力的源泉和魅力所在。 與傳統(tǒng)的三層架構(gòu)相比領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想其實(shí)并沒(méi)有復(fù)雜多少,其要義就在于保持業(yè)務(wù)、模型與代碼三者的統(tǒng)一,只要掌握了這一點(diǎn),領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想中的各種理念都將是水到渠成的事情。初讀《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》時(shí)書中眾多晦澀的術(shù)語(yǔ)也曾讓我十分困惑,但其中的很多內(nèi)容其實(shí)已經(jīng)是很多優(yōu)秀架構(gòu)師的工作日常了。隨著對(duì)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想理解的逐漸深入,我不時(shí)會(huì)產(chǎn)生“咳,這說(shuō)的不就是xxx么”的感慨。這也是沒(méi)有辦法事,誰(shuí)讓國(guó)外那些提前入局的大佬們牢牢掌握著專業(yè)領(lǐng)域的命名權(quán)呢。也正因?yàn)槿绱?,在本文中我們不?huì)去介紹、甚至?xí)M量避免引用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)理論中的術(shù)語(yǔ),避免大家一開始就陷入到那些晦澀難懂的概念里而無(wú)法自拔。希望大家能將更多的精力放在框架內(nèi)各個(gè)模塊的設(shè)計(jì)動(dòng)機(jī)與運(yùn)行機(jī)制上,這才是我們最應(yīng)該思考和關(guān)注的內(nèi)容。至于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想在新架構(gòu)演進(jìn)過(guò)程中的指導(dǎo)作用我們將會(huì)在《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)與PICASO框架》一文中進(jìn)行詳細(xì)地介紹。

三、升級(jí)措施

(一)PICASO框架:從混亂到有序,構(gòu)建圖書館式的代碼架構(gòu)

圖靈獎(jiǎng)得主Frederick在其著作《The Mythical Man-Month》中將軟件系統(tǒng)的復(fù)雜度劃分為本質(zhì)復(fù)雜度(Essential Complexity)和偶然復(fù)雜度(Accidental Complexity),其中本質(zhì)復(fù)雜度是問(wèn)題本身所具有的復(fù)雜度,與求解方法無(wú)關(guān),而偶然復(fù)雜度是求解方法引入的復(fù)雜度。本質(zhì)復(fù)雜度無(wú)法避免,但是我們可以通過(guò)優(yōu)化求解方法來(lái)盡可能降低系統(tǒng)的偶然復(fù)雜度。這給了我們很大的啟發(fā),業(yè)務(wù)天然就是復(fù)雜的,這是一個(gè)客觀事實(shí),架構(gòu)設(shè)計(jì)的目標(biāo)不是消除業(yè)務(wù)上的本質(zhì)復(fù)雜度,而是應(yīng)該引導(dǎo)和輔助開發(fā)者更好的拆解和分析業(yè)務(wù)帶來(lái)的復(fù)雜度(是handle而不是eliminate)。同時(shí),軟件架構(gòu)應(yīng)該提供足夠靈活的標(biāo)準(zhǔn)規(guī)約與框架工具,讓所有開發(fā)者都能夠按照統(tǒng)一的思想寫出可理解、易拓展和好維護(hù)的代碼,減少甚至是消除由于沒(méi)有封裝或封裝不統(tǒng)一帶來(lái)的偶然復(fù)雜度。在這一思想的指導(dǎo)之下,經(jīng)過(guò)兩年多的打磨,我們推出了PICASO框架。

PICASO概述

PICASO是一套以領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(Domain-Driven Design, DDD)作為思想內(nèi)核,專門為集成式復(fù)雜業(yè)務(wù)系統(tǒng)設(shè)計(jì)的通用基礎(chǔ)框架。它的命名來(lái)自“PICASOIs aContextualAbilitySeparate andOrchestrate Framework(PICASO是一種基于上下文的能力分解與編排框架)”的首字母縮寫。有趣的是這個(gè)縮略詞的發(fā)音恰好與西班牙現(xiàn)代派繪畫大師畢加索(Picasso)的姓名讀音相同,畢加索在畫作中經(jīng)常對(duì)人體部位進(jìn)行解構(gòu)和重組,在接下來(lái)的介紹中我們將發(fā)現(xiàn)這一點(diǎn)與PICASO框架所強(qiáng)調(diào)的能力拆分與編排思想有異曲同工之妙,而這也是我們最終采納這個(gè)命名的原因。

PICASO的命名啟發(fā)自筆者比較喜愛的一個(gè)開源項(xiàng)目——WINE,其功能是通過(guò)內(nèi)核適配器在Linux環(huán)境中運(yùn)行Windows應(yīng)用程序,其命名也是這種藏頭詩(shī)的風(fēng)格:WINEIsNotEumlator(WINE不是模擬器)。

PICASO框架的職責(zé)是引導(dǎo)開發(fā)者將復(fù)雜業(yè)務(wù)流程正交分解為多個(gè)簡(jiǎn)單子問(wèn)題,然后將這些簡(jiǎn)單子問(wèn)題的處理邏輯封裝為邊界明確的標(biāo)準(zhǔn)可執(zhí)行實(shí)體,在PICASO框架中這些可執(zhí)行實(shí)體被稱為領(lǐng)域能力。完成能力拆解之后,開發(fā)者可以通過(guò)PICASO提供的能力編排框架將不同的領(lǐng)域能力的組合成一個(gè)完整的請(qǐng)求處理流程,這個(gè)處理流程所在的可執(zhí)行實(shí)體就是一個(gè)領(lǐng)域服務(wù)。領(lǐng)域服務(wù)會(huì)為每次請(qǐng)求生成一個(gè)上下文對(duì)象,通過(guò)這個(gè)上下文對(duì)象可以在不同領(lǐng)域能力以及領(lǐng)域能力與領(lǐng)域服務(wù)之間進(jìn)行數(shù)據(jù)傳遞與共享,進(jìn)而避免重復(fù)及碎片化的IO操作。PICASO框架還提供了開箱即用的通用可執(zhí)行實(shí)體發(fā)現(xiàn)與路由組件,開發(fā)者可以通過(guò)該組件按功能域?qū)︻I(lǐng)域能力及領(lǐng)域服務(wù)進(jìn)行分組和聚合,每個(gè)分組對(duì)外暴露統(tǒng)一的請(qǐng)求路由門面,從而向上層調(diào)用實(shí)體屏蔽分組內(nèi)部的場(chǎng)景復(fù)雜度,進(jìn)而實(shí)現(xiàn)復(fù)雜度降維。如果在領(lǐng)域能力或領(lǐng)域服務(wù)的路由維度之外還存在其他維度的細(xì)微邏輯差異,開發(fā)者可以通過(guò)PICASO提供的拓展點(diǎn)機(jī)制進(jìn)一步實(shí)現(xiàn)差異點(diǎn)分離。同樣的,拓展點(diǎn)依然可以接入通用可執(zhí)行實(shí)體發(fā)現(xiàn)及路由組件,向上層實(shí)體屏蔽拓展點(diǎn)所在功能域內(nèi)的場(chǎng)景復(fù)雜度。

上文對(duì)PICASO框架的整體架構(gòu)進(jìn)行了整體地介紹,接下來(lái)我們將從軟件系統(tǒng)復(fù)雜度根源分析開始,循序漸進(jìn)地詳細(xì)闡述PICASO各個(gè)模塊的設(shè)計(jì)動(dòng)機(jī)及運(yùn)行機(jī)制。

wKgZomc7BH-Afl6xAAR-OzAak40998.png

PICASO框架整體架構(gòu)

復(fù)雜度的根源

軟件設(shè)計(jì)的本質(zhì)就是持續(xù)對(duì)抗軟件本身產(chǎn)生的復(fù)雜度,早在最開始進(jìn)行新架構(gòu)探索的時(shí)候我們就意識(shí)到,構(gòu)建整潔架構(gòu)的前提是厘清系統(tǒng)復(fù)雜度的根源。

本質(zhì)復(fù)雜度

通過(guò)對(duì)復(fù)雜業(yè)務(wù)系統(tǒng)發(fā)展歷程的分析,我們發(fā)現(xiàn)業(yè)務(wù)復(fù)雜度一般來(lái)自水平方向上的多維度拓展和垂直方向上的多模塊集成。

業(yè)務(wù)發(fā)展的早期往往都是單一場(chǎng)景,隨著業(yè)務(wù)的發(fā)展,產(chǎn)品形態(tài)開始變得豐富多樣,服務(wù)的用戶及業(yè)務(wù)方也越來(lái)越多,業(yè)務(wù)架構(gòu)從原來(lái)的單點(diǎn)結(jié)構(gòu)逐漸演變?yōu)閺?fù)雜的樹狀結(jié)構(gòu),樹的每一層都代表一個(gè)業(yè)務(wù)維度,業(yè)務(wù)的發(fā)展讓系統(tǒng)在水平方向上呈現(xiàn)出多維度增長(zhǎng)的特征。以廣告投放系統(tǒng)為例,最初的投放系統(tǒng)只有合約展示包段一種業(yè)務(wù)形態(tài),隨著程序化廣告和智能廣告的興起,廣告投放及播放形式層出不窮,業(yè)務(wù)樹中開始出現(xiàn)“產(chǎn)品線”的維度;而為了服務(wù)不同業(yè)務(wù)方,我們?cè)谙到y(tǒng)中增加了“投放平臺(tái)”的維度;對(duì)不同投放標(biāo)的物的支持又在系統(tǒng)中引入了“計(jì)劃類型”的維度......就這樣廣告投放系統(tǒng)的業(yè)務(wù)架構(gòu)也逐漸演變成了如下圖所示的復(fù)雜樹狀結(jié)構(gòu)。

wKgaomc7BIKAbpi7AAVvle-4rwE522.png

多維度、多模塊、多場(chǎng)景的廣告投放業(yè)務(wù)

而在垂直方向上,早期的業(yè)務(wù)流程一般比較簡(jiǎn)短,只有少數(shù)幾個(gè)業(yè)務(wù)環(huán)節(jié)。隨著業(yè)務(wù)的發(fā)展,系統(tǒng)功能越來(lái)越豐富,業(yè)務(wù)流程也變得愈發(fā)冗長(zhǎng),開始呈現(xiàn)出鮮明的模塊化特征。同樣以廣告投放系統(tǒng)為例,早期的廣告物料只有時(shí)段、預(yù)算、出價(jià)、創(chuàng)意幾個(gè)基礎(chǔ)模塊,隨著業(yè)務(wù)的發(fā)展陸續(xù)新增了智能出價(jià)、人群定向、地域定向、商品定向、智能創(chuàng)意、智能選品等業(yè)務(wù)模塊,物料創(chuàng)編流程也越來(lái)越冗長(zhǎng)。除此之外,單個(gè)模塊內(nèi)部也開始出現(xiàn)多場(chǎng)景分化,如廣告投放系統(tǒng)中的智能出價(jià)模塊內(nèi)部就存在tCPA、tROI、eCPC、MC等不同的智能出價(jià)模型,其數(shù)據(jù)模型及業(yè)務(wù)規(guī)則也不盡相同,這進(jìn)一步增加了業(yè)務(wù)的復(fù)雜度。

本小節(jié)從業(yè)務(wù)架構(gòu)演進(jìn)歷程的視角分析了業(yè)務(wù)復(fù)雜度的來(lái)源,這構(gòu)成了系統(tǒng)的本質(zhì)復(fù)雜度。而對(duì)這些復(fù)雜業(yè)務(wù)規(guī)則的實(shí)現(xiàn)方案(好的、或者是壞的)就成了系統(tǒng)偶然復(fù)雜度的來(lái)源。

偶然復(fù)雜度

業(yè)務(wù)在多個(gè)維度上向著熵增的方向不斷發(fā)展,但是我們的代碼始終只有一套,不同維度的業(yè)務(wù)場(chǎng)景可能對(duì)同一個(gè)業(yè)務(wù)環(huán)節(jié)提出不同的個(gè)性化需求,造成不同維度的業(yè)務(wù)邏輯互相耦合,代碼中開始出現(xiàn)大量層層嵌套的if-else分支,圈復(fù)雜度不斷飆升,系統(tǒng)開始出現(xiàn)腐化跡象。此時(shí)一些工程師可能會(huì)意識(shí)到這個(gè)問(wèn)題并開始著手優(yōu)化,但是由于缺少統(tǒng)一的邏輯封裝與拓展工具,再加上開發(fā)者的水平與技法也不盡相同,導(dǎo)致優(yōu)化方案五花八門,這種方案上的不一致反而進(jìn)一步增加了代碼的復(fù)雜度。除此之外,隨著系統(tǒng)集成的業(yè)務(wù)模塊越來(lái)越多,業(yè)務(wù)流程愈發(fā)冗長(zhǎng),與外部子系統(tǒng)的交互邏輯越來(lái)越復(fù)雜,開發(fā)者不得不去處理超時(shí)、重試、冪等、長(zhǎng)事務(wù)、分布式事務(wù)及跨系統(tǒng)的數(shù)據(jù)一致性等問(wèn)題,這些技術(shù)方案的引入對(duì)系統(tǒng)來(lái)說(shuō)也是復(fù)雜度的來(lái)源。

對(duì)架構(gòu)設(shè)計(jì)的啟發(fā)

從上面的論述中可以看出,系統(tǒng)偶然復(fù)雜度的高低在很大程度上取決于開發(fā)者能否分析處理好業(yè)務(wù)的本質(zhì)復(fù)雜度,另外在多人協(xié)作開發(fā)場(chǎng)景中,軟件架構(gòu)的標(biāo)準(zhǔn)性和解決方案的一致性也是決定系統(tǒng)偶然復(fù)雜度的重要因素,這就是我們推出PICASO框架的根本原因。我們希望PICASO能夠引導(dǎo)開發(fā)者對(duì)復(fù)雜業(yè)務(wù)流程進(jìn)行模塊化拆解,采用分治思想逐一擊破,并通過(guò)標(biāo)準(zhǔn)的邏輯封裝規(guī)約與框架來(lái)實(shí)現(xiàn)多維度邏輯拓展,讓團(tuán)隊(duì)中每一位開發(fā)者都能夠以統(tǒng)一的思想寫出清晰、簡(jiǎn)潔、有序、可檢索的代碼。

到這里相信有些讀者可能會(huì)產(chǎn)生一些疑問(wèn),既然軟件系統(tǒng)的偶然復(fù)雜度是技術(shù)方案本身的復(fù)雜度,那么引入PICASO框架是否也在增加系統(tǒng)的偶然復(fù)雜度呢?答案是肯定的,新框架的引入的確會(huì)增加系統(tǒng)的偶然復(fù)雜度。PICASO框架由于采用了全新的設(shè)計(jì)思想,在推行早期曾經(jīng)歷過(guò)痛苦的磨合期,也出現(xiàn)過(guò)不少由于開發(fā)者不理解新架構(gòu)的運(yùn)行機(jī)制而導(dǎo)致的設(shè)計(jì)缺陷或線上問(wèn)題。但是任何架構(gòu)迭代之路都是螺旋上升的,新技術(shù)帶來(lái)的系統(tǒng)復(fù)雜度畢竟是靜態(tài)的,隨著開發(fā)人員對(duì)新架構(gòu)運(yùn)行機(jī)制及使用技巧的逐漸掌握,系統(tǒng)便開始趨于穩(wěn)定,新技術(shù)帶來(lái)的優(yōu)化收益也會(huì)逐漸顯現(xiàn)出來(lái)。但是如果我們不對(duì)現(xiàn)有的架構(gòu)做出升級(jí),那么系統(tǒng)將隨著源源不斷的業(yè)務(wù)需求向著不可控熵增的方向不斷發(fā)展,由此帶來(lái)的系統(tǒng)復(fù)雜度將是動(dòng)態(tài)且持續(xù)增加的。

PICASO的復(fù)雜度應(yīng)對(duì)之道

在分析完系統(tǒng)的復(fù)雜度來(lái)源之后,接下來(lái)我們將詳細(xì)介紹PICASO如何協(xié)助開發(fā)者對(duì)抗軟件系統(tǒng)的復(fù)雜度。IEEE對(duì)軟件架構(gòu)的定義為:架構(gòu)是由系統(tǒng)之間的組織、組件及組件之間的關(guān)系、以及對(duì)設(shè)計(jì)與演進(jìn)的指導(dǎo)原則組成的,其中前兩者是具體的實(shí)體框架,后者是指導(dǎo)思想。而軟件架構(gòu)的指導(dǎo)思想往往決定著前兩者的實(shí)現(xiàn),對(duì)指導(dǎo)思想的理解與掌握程度也直接決定了開發(fā)者能否在實(shí)際業(yè)務(wù)中用好架構(gòu)。以Spring框架為例,Spring的指導(dǎo)思想為:控制反轉(zhuǎn)(IoC)、依賴注入(DI)及面向切面編程(AOP),這三大核心思想一方面直接決定了Spring框架核心模塊的實(shí)現(xiàn),另一方面也是開發(fā)者要想用好Spring則必須掌握的內(nèi)容。而對(duì)PICASO來(lái)說(shuō),其指導(dǎo)思想可以概括為:能力拆分、拓展點(diǎn)抽象及能力編排。

wKgZomc7BIKAKmCGAAFlQ3Aps5Y369.png

軟件架構(gòu)的構(gòu)成

領(lǐng)域能力拆分與路由助力多模塊集成

神經(jīng)認(rèn)知學(xué)家喬治·米勒在他的論文《神奇的數(shù)字7》中指出人腦能夠同時(shí)處理的信息容量是有限的,人腦的短時(shí)記憶容量為7(7個(gè)數(shù)字、6個(gè)字母或5個(gè)單詞),后來(lái)的研究更是將這個(gè)數(shù)字降到了4個(gè)左右。所以當(dāng)冗長(zhǎng)的業(yè)務(wù)流程疊加上多維度的個(gè)性化訴求,系統(tǒng)的業(yè)務(wù)復(fù)雜度將飆升為

,這顯然超出了我們大腦的瞬時(shí)處理容量,此時(shí)就需要利用

關(guān)注點(diǎn)分離

、

分類

分層

思想對(duì)復(fù)雜問(wèn)題進(jìn)行求解。

分離

關(guān)注點(diǎn)分離(Separation of concerns,SOC)就是把復(fù)雜問(wèn)題正交分解為多個(gè)互不相關(guān)的最小子問(wèn)題,聚焦整體問(wèn)題的局部復(fù)雜性,逐步進(jìn)行求解。我們?cè)凇稄?fù)雜度的根源》章節(jié)中指出,復(fù)雜的業(yè)務(wù)系統(tǒng)往往會(huì)呈現(xiàn)出鮮明的模塊化特征,因此我們可以自然而然地根據(jù)業(yè)務(wù)模塊的功能邊界對(duì)冗長(zhǎng)的業(yè)務(wù)流程進(jìn)行拆分,然后聚焦單個(gè)模塊進(jìn)行設(shè)計(jì)與抽象,避免陷入多模塊、多場(chǎng)景互相耦合的思維泥沼。PICASO框架為此引入了領(lǐng)域能力及領(lǐng)域服務(wù)的概念,其中領(lǐng)域能力用來(lái)承接單個(gè)業(yè)務(wù)模塊內(nèi)部的邏輯細(xì)節(jié),而領(lǐng)域服務(wù)則負(fù)責(zé)通過(guò)組合不同的領(lǐng)域能力實(shí)現(xiàn)一個(gè)完整的業(yè)務(wù)流程。如下圖所示,以單元新建流程為例,我們可以把單元新建流程劃分為:?jiǎn)卧A(chǔ)信息構(gòu)造、優(yōu)化目標(biāo)設(shè)置、出價(jià)設(shè)置、人群設(shè)置、地域定向設(shè)置和商品定向設(shè)置多個(gè)子模塊,我們可以將這些模塊內(nèi)部邏輯封裝成領(lǐng)域能力,然后通過(guò)這些能力的組合構(gòu)建一個(gè)完整的單元信息領(lǐng)域服務(wù)。

chaijie_default.png

一個(gè)完整的業(yè)務(wù)流程可以拆分為多個(gè)原子業(yè)務(wù)模塊,每個(gè)原子業(yè)務(wù)模塊還可以按照其內(nèi)部的業(yè)務(wù)模式進(jìn)行進(jìn)一步細(xì)分

PICASO框架中的領(lǐng)域服務(wù)與DDD思想中的領(lǐng)域服務(wù)是同一個(gè)概念,其職責(zé)和定位都是承接無(wú)法在單個(gè)實(shí)體與值對(duì)象內(nèi)部直接實(shí)現(xiàn)的業(yè)務(wù)邏輯(事實(shí)上,B端系統(tǒng)對(duì)外提供的大部分服務(wù)都無(wú)法在單個(gè)聚合內(nèi)直接實(shí)現(xiàn))。而領(lǐng)域能力的概念則經(jīng)常出現(xiàn)在一些企業(yè)級(jí)中臺(tái)化框架中,如阿里的星環(huán)、京東的Matrix等。盡管當(dāng)年如火如荼的中臺(tái)化戰(zhàn)略如今已經(jīng)偃旗息鼓,但是我們還是將這個(gè)命名引入到了PICASO中,因?yàn)槲覀兇_實(shí)沒(méi)有找到一個(gè)比它更合適的命名,可以如此形象地描述一個(gè)足夠內(nèi)聚、自治且能夠被復(fù)用和拓展的原子實(shí)體。當(dāng)然PICASO中的領(lǐng)域能力與那些企業(yè)級(jí)中臺(tái)化框架中的領(lǐng)域能力相比要輕量和易用的多,不需要繁瑣的身份申請(qǐng),也不存在跨工程熱加載的問(wèn)題,畢竟中臺(tái)化的重心在管理域平臺(tái)及前中臺(tái)團(tuán)隊(duì)的協(xié)作上,而PICASO則始終聚焦在代碼本身的復(fù)雜度控制上。其實(shí)中臺(tái)化也好,組件化也罷,系統(tǒng)的復(fù)雜度就擺在那里,不管用什么由頭,要想提升團(tuán)隊(duì)整體的研發(fā)效能,它都是我們必須要去解決的一個(gè)問(wèn)題。在本小節(jié)的論述中,領(lǐng)域能力似乎就是根據(jù)業(yè)務(wù)模塊的邊界簡(jiǎn)單劃分出來(lái)的。但是在實(shí)際開發(fā)中的能力劃分要復(fù)雜的多,需要綜合考慮能力的應(yīng)用場(chǎng)景、會(huì)被哪些領(lǐng)域服務(wù)使用、以及能力之間的依賴關(guān)系等諸多因素進(jìn)行反復(fù)地推導(dǎo)和調(diào)整。本文對(duì)能力劃分方法論只是簡(jiǎn)單地做了問(wèn)題引入,更加具體的內(nèi)容我們將在《PICASO框架最佳實(shí)踐——能力識(shí)別與劃分》一文中進(jìn)行詳細(xì)介紹。

領(lǐng)域能力的拆解除了能夠降低業(yè)務(wù)流程分析的復(fù)雜度之外,也提高了代碼復(fù)用和拓展的靈活性。領(lǐng)域能力就像積木一樣,可以被組裝到不同的領(lǐng)域服務(wù)中,如人群設(shè)置能力可以同時(shí)被單元新建服務(wù)、單元編輯服務(wù)、人群快捷修改等領(lǐng)域服務(wù)復(fù)用。而能力拆解帶來(lái)的拓展靈活性性是相對(duì)于樸素模版設(shè)計(jì)模式而言的。在傳統(tǒng)架構(gòu)中模板類可能是我們使用最多的設(shè)計(jì)模式,它的確能夠簡(jiǎn)單有效地實(shí)現(xiàn)復(fù)用共性流程、分離差異的目標(biāo)。但是由于復(fù)雜業(yè)務(wù)流程中不同業(yè)務(wù)節(jié)點(diǎn)的差異化維度往往是不同的,直接將業(yè)務(wù)主流程抽象成一個(gè)模板類,將各個(gè)節(jié)點(diǎn)作為模板中的抽象方法,那么該模板類子類的繼承關(guān)系復(fù)雜度將是各個(gè)業(yè)務(wù)節(jié)點(diǎn)內(nèi)部場(chǎng)景復(fù)雜度的叉乘。再加上傳統(tǒng)架構(gòu)并沒(méi)有積極引導(dǎo)開發(fā)者落實(shí)面向?qū)ο缶幊痰乃枷?,?dǎo)致我們基本上還在以面向過(guò)程的方式開發(fā)我們的系統(tǒng),通常會(huì)將同一個(gè)產(chǎn)品線中不同的業(yè)務(wù)方法實(shí)現(xiàn)到同一個(gè)Service或者M(jìn)anager類中,這將進(jìn)一步加重模板抽象及子類繼承關(guān)系的復(fù)雜度。而PICASO框架通過(guò)領(lǐng)域能力拆解將不同的業(yè)務(wù)環(huán)節(jié)拆分到了單獨(dú)的原子業(yè)務(wù)實(shí)體中,將模板中的抽象方法算子化。由于不同的原子業(yè)務(wù)模塊之間互相正交、互不干擾,因此能夠讓這些業(yè)務(wù)算子獨(dú)立迭代,在各自的業(yè)務(wù)維度上靈活地進(jìn)行繼承和拓展。

分類

只是把業(yè)務(wù)流程按照功能邊界拆分成不同的模塊通常是不夠的,因?yàn)閱蝹€(gè)模塊內(nèi)部往往還存在細(xì)分的業(yè)務(wù)模式,如上圖中的出價(jià)設(shè)置模塊,其內(nèi)部還存在手動(dòng)、MC、tCPA、eCPC等不同的出價(jià)模型,這個(gè)時(shí)候就需要根據(jù)分類思想進(jìn)行進(jìn)一步拆解。分類思想是關(guān)注點(diǎn)分離思想進(jìn)一步的延伸,它在分離的同時(shí)還注重元素之間的共性特征。當(dāng)模塊內(nèi)部出現(xiàn)場(chǎng)景分化時(shí),PICASO框架建議開發(fā)者對(duì)模塊進(jìn)行進(jìn)一步細(xì)分,將模塊內(nèi)不同場(chǎng)景的業(yè)務(wù)規(guī)則封裝為不同的能力實(shí)例。這些能力實(shí)例之間盡管存在邏輯差異,但是畢竟屬于同一個(gè)原子業(yè)務(wù)模塊,在數(shù)據(jù)模型、接口協(xié)議乃至業(yè)務(wù)流程上都存在很大的相似度。因此PICASO會(huì)將同模塊下不同業(yè)務(wù)場(chǎng)景對(duì)應(yīng)的領(lǐng)域能力實(shí)例聚合到一起,這樣的一組能力被稱為一個(gè)能力節(jié)點(diǎn)。同一個(gè)能力節(jié)點(diǎn)下的各個(gè)能力實(shí)例使用相同的接口參數(shù)及上下文定義,每個(gè)能力節(jié)點(diǎn)下會(huì)額外定義一個(gè)能力門面,能力門面通常不承載具體的業(yè)務(wù)規(guī)則,它僅負(fù)責(zé)定義當(dāng)前能力節(jié)點(diǎn)對(duì)外的接口協(xié)議以及從請(qǐng)求參數(shù)中提取業(yè)務(wù)場(chǎng)景標(biāo)識(shí)的邏輯,它是能力節(jié)點(diǎn)下所有能力實(shí)例對(duì)外提供服務(wù)的統(tǒng)一入口。上層的領(lǐng)域服務(wù)組合領(lǐng)域能力時(shí),引用的不是具體的領(lǐng)域能力實(shí)例,而是各個(gè)能力節(jié)點(diǎn)下的能力門面。PICASO框架內(nèi)置的可執(zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制會(huì)在應(yīng)用啟動(dòng)時(shí)掃描出系統(tǒng)中所有的能力門面,并建立好能力門面與各個(gè)能力實(shí)例的路由表。當(dāng)請(qǐng)求到來(lái)時(shí),領(lǐng)域服務(wù)不必關(guān)注本次請(qǐng)求應(yīng)該使用哪個(gè)具體的能力實(shí)例,而是直接調(diào)用能力門面的統(tǒng)一入口,PICASO框架會(huì)通過(guò)內(nèi)置的可執(zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制提取請(qǐng)求中的場(chǎng)景標(biāo)識(shí),然后將請(qǐng)求路由到對(duì)應(yīng)的領(lǐng)域能力實(shí)例上,從而實(shí)現(xiàn)模塊內(nèi)部的場(chǎng)景復(fù)雜度與領(lǐng)域服務(wù)模塊集成復(fù)雜度之間的解耦。以出價(jià)模塊為例,出價(jià)模塊內(nèi)部會(huì)根據(jù)不同的出價(jià)類型細(xì)分為tCPA、MC、eCPC等智能出價(jià)能力實(shí)例,但是單元新建領(lǐng)域服務(wù)并不會(huì)直接操作這些具體的能力實(shí)例,它引用是出價(jià)設(shè)置能力門面。當(dāng)請(qǐng)求到來(lái)時(shí),PICASO框架會(huì)根據(jù)請(qǐng)求中的出價(jià)類型自動(dòng)將請(qǐng)求路由到相應(yīng)的能力實(shí)例上??蓤?zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制是PICASO框架內(nèi)置的一個(gè)底層通用組件,是能力編排、拓展點(diǎn)機(jī)制等頂層功能的基礎(chǔ)。其本質(zhì)上就是一個(gè)增強(qiáng)型的門面+策略模式,我們通過(guò)一些實(shí)現(xiàn)技巧將其做成了一個(gè)可以適配任意可執(zhí)行實(shí)體的通用組件。如下圖所示,單元新建業(yè)務(wù)流程涉及標(biāo)的物設(shè)置、出價(jià)設(shè)置及人群設(shè)置等業(yè)務(wù)環(huán)節(jié),這些業(yè)務(wù)環(huán)節(jié)內(nèi)部都有各自的細(xì)分場(chǎng)景。在代碼實(shí)現(xiàn)中,這些業(yè)務(wù)環(huán)節(jié)被抽象為3個(gè)能力節(jié)點(diǎn),節(jié)點(diǎn)內(nèi)部的細(xì)分場(chǎng)景被隔離到不同的能力實(shí)例中,在構(gòu)建領(lǐng)域服務(wù)時(shí)就不需要考慮當(dāng)前各個(gè)能力節(jié)點(diǎn)下的細(xì)分邏輯,只需要專注于業(yè)務(wù)流程本身,實(shí)現(xiàn)各個(gè)能力門面的組裝邏輯即可。

wKgaomc7BIWAOOvzAAEihCV9iHs039.png

能力門面與能力實(shí)例的抽象實(shí)現(xiàn)了能力編排復(fù)雜度的降維

分層

分層則是分類思想在領(lǐng)域服務(wù)、拓展點(diǎn)等其他實(shí)體粒度上的延伸。如快車、互動(dòng)、推薦三條產(chǎn)品線的單元新建服務(wù)會(huì)被劃分到同一個(gè)服務(wù)分組下,對(duì)外暴露一個(gè)單元新建服務(wù)門面。這樣做目的是下層實(shí)體對(duì)上層實(shí)體暴露統(tǒng)一的門面接口,自下而上地逐層屏蔽下層實(shí)體的內(nèi)部復(fù)雜度,實(shí)現(xiàn)維度間復(fù)雜度解耦,進(jìn)而將代碼的整體復(fù)雜度由

降維到

。下圖展示了PICASO框架中各個(gè)業(yè)務(wù)實(shí)體的層級(jí)結(jié)構(gòu),最下層是各個(gè)領(lǐng)域能力實(shí)例執(zhí)行器,它們承載了具體的業(yè)務(wù)規(guī)則;相同功能子域的領(lǐng)域能力實(shí)例會(huì)對(duì)上層的領(lǐng)域服務(wù)實(shí)例暴露一個(gè)統(tǒng)一的領(lǐng)域能力門面執(zhí)行器,領(lǐng)域服務(wù)實(shí)例執(zhí)行器會(huì)通過(guò)組合領(lǐng)域能力門面定義具體的業(yè)務(wù)流程;而相同的功能子域的領(lǐng)域服務(wù)實(shí)例又會(huì)對(duì)領(lǐng)域服務(wù)統(tǒng)一入口(Domain Service Faced)暴露一個(gè)領(lǐng)域服務(wù)門面執(zhí)行器,屏蔽模塊內(nèi)領(lǐng)域服務(wù)實(shí)例之間的細(xì)分規(guī)則;領(lǐng)域服務(wù)統(tǒng)一入口將不同的領(lǐng)域服務(wù)門面集成到一起,對(duì)上層不同的流量來(lái)源暴露統(tǒng)一的請(qǐng)求入口。

這種分層結(jié)構(gòu)讓開發(fā)者逐層解構(gòu)業(yè)務(wù)復(fù)雜度的同時(shí),實(shí)際上也構(gòu)造了一個(gè)索引結(jié)構(gòu),為實(shí)現(xiàn)可檢索的代碼架構(gòu)打下基礎(chǔ)。

wKgZomc7BIaAfGvkAALPFsd84Dc491.png

自下而上逐層屏蔽層級(jí)內(nèi)部的業(yè)務(wù)場(chǎng)景復(fù)雜度

在分層架構(gòu)中,除了可以通過(guò)通用可執(zhí)行實(shí)體路由機(jī)制自下而上地屏蔽下層實(shí)體的內(nèi)部場(chǎng)景復(fù)雜度之外,有時(shí)我們還要反過(guò)來(lái)自上而下地進(jìn)行復(fù)雜度合并。我們用一個(gè)例子來(lái)說(shuō)明這種設(shè)計(jì)技巧:在廣告投放業(yè)務(wù)中有一個(gè)經(jīng)典的出價(jià)計(jì)算器模塊,它會(huì)根據(jù)廣告物料上的基礎(chǔ)出價(jià)、人群溢價(jià)、關(guān)鍵詞出價(jià)、流量包溢價(jià)、時(shí)段溢價(jià)等信息預(yù)估廣告物料最終的出價(jià)值范圍。計(jì)算邏輯只有一個(gè),但是由于計(jì)算邏輯關(guān)聯(lián)了眾多底層模塊,物料新建、修改以及關(guān)聯(lián)模塊的快捷修改、還有對(duì)物料進(jìn)行修改過(guò)程中實(shí)時(shí)出價(jià)預(yù)估回顯(此時(shí)最新的修改并未落庫(kù))等接口都會(huì)調(diào)用出價(jià)計(jì)算器模塊,但是這些使用場(chǎng)景對(duì)出價(jià)預(yù)估參數(shù)的填充程度是不同的,需要模塊針對(duì)不同的使用場(chǎng)景執(zhí)行不同的參數(shù)補(bǔ)充查詢邏輯。這是一個(gè)典型的上層模塊的調(diào)用場(chǎng)景復(fù)雜度滲透到底層模塊實(shí)現(xiàn)復(fù)雜度中的例子。在分層架構(gòu)中,越是底層的模塊在設(shè)計(jì)上需要考慮的場(chǎng)景應(yīng)該越少,而且要避免與上層模塊的使用場(chǎng)景耦合。因?yàn)樯蠈幽K的使用場(chǎng)景是動(dòng)態(tài)增加的,不知道什么時(shí)候就會(huì)有新的使用場(chǎng)景出現(xiàn),而底層模塊的真正需要處理的內(nèi)部場(chǎng)景應(yīng)該比頂層使用該模塊的場(chǎng)景要少且穩(wěn)定的多。所以解決這個(gè)問(wèn)題的措施就是底層模塊面向自己內(nèi)部的業(yè)務(wù)模式在參數(shù)中定義一個(gè)隱式的標(biāo)識(shí)屬性,讓調(diào)用方根據(jù)自己的使用場(chǎng)景和業(yè)務(wù)訴求隱式地設(shè)置該參數(shù),底層模塊則直接根據(jù)參數(shù)中的這個(gè)標(biāo)識(shí)屬性執(zhí)行相應(yīng)的分支邏輯。回到出價(jià)計(jì)算器的案例中,該模塊的使用場(chǎng)景有:?jiǎn)卧陆ê笫录|發(fā)、單元整體修改后事件觸發(fā)、單元新增關(guān)鍵詞后事件觸發(fā)、單元新建中臨時(shí)觸發(fā)、單元修改中臨時(shí)觸發(fā)、單元添加關(guān)鍵詞中臨時(shí)觸發(fā)等多種使用場(chǎng)景,未來(lái)也不確定會(huì)出現(xiàn)什么新的使用場(chǎng)景。但是對(duì)出價(jià)計(jì)算器模塊的內(nèi)部計(jì)算邏輯來(lái)說(shuō),其實(shí)只有需要補(bǔ)充查詢單元下關(guān)鍵詞信息和不補(bǔ)充查詢這兩種場(chǎng)景。為此我們?cè)诔鰞r(jià)計(jì)算器能力參數(shù)中增加一個(gè)布爾類型的參數(shù),能力內(nèi)部直接根據(jù)該參數(shù)判斷是否需要執(zhí)行關(guān)鍵詞的查詢操作,出價(jià)計(jì)算器模塊的調(diào)用方則分別根據(jù)自己的使用場(chǎng)景判斷該如何設(shè)置這個(gè)參數(shù),從而起到自上而下的合并上層調(diào)用場(chǎng)景復(fù)雜度、保持底層模塊穩(wěn)定的作用。

有些讀者或許會(huì)覺得這種機(jī)制與上文介紹的能力路由機(jī)制是互相矛盾的,然而他們實(shí)際上并不沖突。因?yàn)閷?duì)那些能夠自下而上屏蔽內(nèi)部場(chǎng)景復(fù)雜度的模塊而言,它們通常顯式地定義了內(nèi)部不同業(yè)務(wù)模式的標(biāo)識(shí)屬性,如出價(jià)模塊的出價(jià)類型、人群定向模塊的人群類型等,用戶在請(qǐng)求參數(shù)中也會(huì)顯式地設(shè)置請(qǐng)本次請(qǐng)求對(duì)應(yīng)的業(yè)務(wù)標(biāo)識(shí),因此框架能夠直接對(duì)這些模塊應(yīng)用通用可執(zhí)行實(shí)體路由機(jī)制。但是實(shí)際業(yè)務(wù)中也存在一些模塊,它們內(nèi)部沒(méi)有定義明確的業(yè)務(wù)模式標(biāo)識(shí),而是根據(jù)請(qǐng)求來(lái)源、調(diào)用場(chǎng)景等動(dòng)態(tài)條件執(zhí)行不同的業(yè)務(wù)邏輯。此時(shí)我們可以先暫時(shí)忘掉這些模塊的調(diào)用場(chǎng)景,而是聚焦模塊內(nèi)部的業(yè)務(wù)分支提煉出隱藏其中的業(yè)務(wù)模式,然后讓上層模塊將動(dòng)態(tài)調(diào)用場(chǎng)景轉(zhuǎn)化為底層模塊定義的隱式業(yè)務(wù)模式標(biāo)識(shí)參數(shù),接下來(lái)就能繼續(xù)應(yīng)用通用可執(zhí)行實(shí)體路由機(jī)制了。因此,分層思想中自下而上屏蔽的是模塊內(nèi)部的固有場(chǎng)景復(fù)雜度,而自上而下合并的則是模塊外部的使用場(chǎng)景復(fù)雜度,二者其實(shí)是互補(bǔ)的關(guān)系。

拓展點(diǎn)機(jī)制協(xié)助走出多維度泥潭

領(lǐng)域服務(wù)與領(lǐng)域能力的路由機(jī)制能夠較好的應(yīng)對(duì)系統(tǒng)多模塊集成帶來(lái)的復(fù)雜度,但是領(lǐng)域能力及領(lǐng)域服務(wù)必須嚴(yán)格遵守框架規(guī)約,繼承標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版(后續(xù)章節(jié)會(huì)有詳細(xì)講解),定義出明確的數(shù)據(jù)交換協(xié)議及上下文對(duì)象,這些都是相對(duì)較重的操作。因此能力或服務(wù)路由的維度必須抓住最核心的業(yè)務(wù)差異,而不是把所有存在業(yè)務(wù)差異的維度都納入到路由規(guī)則中,否則就會(huì)造成沙?;鸱?,反而增加系統(tǒng)的維護(hù)成本。因此我們還需要一種機(jī)制能夠以更加輕量的方式承載除了領(lǐng)域服務(wù)或能力路由維度之外其他業(yè)務(wù)維度上的細(xì)微差異,這就是拓展點(diǎn)機(jī)制要解決的問(wèn)題。

拓展點(diǎn)機(jī)制是通用可執(zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制在更細(xì)粒度上的延伸應(yīng)用,本質(zhì)上就是將存在差異化邏輯的環(huán)節(jié)抽象為一個(gè)接口從主流程中分離出去,然后將不同場(chǎng)景的差異化邏輯隔離在不同的拓展點(diǎn)接口實(shí)現(xiàn)中,這其實(shí)就是依賴倒轉(zhuǎn)原則(Dependence Inversion Principle, DIP)的應(yīng)用。與領(lǐng)域服務(wù)和領(lǐng)域能力相比,拓展點(diǎn)的定義和實(shí)現(xiàn)成本都要低很多,框架對(duì)拓展點(diǎn)接口內(nèi)的方法及方法參數(shù)都不會(huì)做過(guò)多的約束,定義一個(gè)拓展點(diǎn)僅需要繼承框架提供的標(biāo)準(zhǔn)接口并指定路由標(biāo)識(shí)的提取邏輯,而實(shí)現(xiàn)一個(gè)拓展點(diǎn)接口時(shí)也僅需要在實(shí)現(xiàn)拓展邏輯之外額外指定當(dāng)前拓展點(diǎn)實(shí)例能適配哪些路由標(biāo)識(shí)。拓展點(diǎn)可以嵌入到領(lǐng)域服務(wù)、領(lǐng)域能力以及資源庫(kù)(Repository,下文中會(huì)詳細(xì)闡述)中任何一處存在差異化邏輯的流程中。

拓展點(diǎn)機(jī)制作為能力與服務(wù)拆分路由機(jī)制的補(bǔ)充,支持任意維度上的差異化邏輯隔離。以下圖為例,在廣告投放系統(tǒng)中,底層的人群設(shè)置能力節(jié)點(diǎn)已經(jīng)按照其核心屬性人群類型進(jìn)行了能力實(shí)例的劃分。由于系統(tǒng)還賦能了多個(gè)投放平臺(tái),不同的投放平臺(tái)對(duì)可綁定的人群上限有著不同的限制,此時(shí)就可以將各個(gè)能力實(shí)例中人群綁定數(shù)量校驗(yàn)環(huán)節(jié)抽象為一個(gè)拓展點(diǎn)接口,以投放平臺(tái)類型作為路由KEY為各個(gè)投放平臺(tái)提供不同的接口實(shí)現(xiàn),從而自上而下地解決多維度拓展的難題。

這里說(shuō)的“自上而下”是一種形象的描述,可以理解為父層級(jí)業(yè)務(wù)維度內(nèi)不同的業(yè)務(wù)場(chǎng)景在子層級(jí)模塊上產(chǎn)生的差異化邏輯。但實(shí)際上拓展點(diǎn)機(jī)制并不限制邏輯的維度拓展方向,如下圖的例子中,右側(cè)觸點(diǎn)新建服務(wù)領(lǐng)域服務(wù)實(shí)例所屬的服務(wù)門面定義的服務(wù)路由維度是產(chǎn)品線,但是不同的計(jì)劃類型的單元新建流程之間依然存在細(xì)微的邏輯差異,此時(shí)盡管計(jì)劃類型是產(chǎn)品線的子維度,但是依然可以通過(guò)拓展點(diǎn)來(lái)承載這些細(xì)微的邏輯差異。

wKgaomc7BIeASER0AAXRp-5dtmU414.png

拓展點(diǎn)機(jī)制的核心作用是作為能力及服務(wù)路由維度的補(bǔ)充,進(jìn)一步實(shí)現(xiàn)差異點(diǎn)的分離

能力編排框架確保架構(gòu)思想切實(shí)落地

前面幾個(gè)小節(jié)一直在論述如何對(duì)復(fù)雜邏輯進(jìn)行拆解和分離,但是系統(tǒng)要想對(duì)外提供可用的功能,就必須再次把這些分離出來(lái)的能力及拓展點(diǎn)組合起來(lái),構(gòu)成一個(gè)完整的領(lǐng)域服務(wù)。最簡(jiǎn)單的組合方式就是直接硬編碼依次調(diào)用各個(gè)能力門面的功能入口,手動(dòng)實(shí)現(xiàn)前置方法調(diào)用結(jié)果與后置方法入?yún)⒌膶傩杂成浜娃D(zhuǎn)換,但是這種組合方式會(huì)在業(yè)務(wù)主流程中插入大量的膠水代碼,稀釋代碼的信息密度,將流程關(guān)鍵節(jié)點(diǎn)掩蓋在大量繁瑣無(wú)趣的`setter`、`gettter`方法調(diào)用中。為了解決這個(gè)問(wèn)題,同時(shí)確保新架構(gòu)設(shè)計(jì)思想能夠精準(zhǔn)落地,讓規(guī)范和標(biāo)準(zhǔn)框架化,PICASO自建了能力編排框架,它為前文所述的各類思想落地提供了框架基礎(chǔ),將前文提到的各種實(shí)體、組件與設(shè)計(jì)思想有機(jī)結(jié)合到一起,自動(dòng)實(shí)現(xiàn)模塊串聯(lián),讓開發(fā)者專注于業(yè)務(wù)邏輯本身,實(shí)現(xiàn)填空式開發(fā),最終構(gòu)建出一個(gè)完整的工程應(yīng)用。

目前業(yè)界有很多流程編排引擎,有老牌廠商的Netflix Conductor、AWS Step Function等,也有開源的Apache Activiti、Zeebe等。我們?cè)谠缙诩軜?gòu)探索階段對(duì)這些解決方案也進(jìn)行了調(diào)研和試用,但是發(fā)現(xiàn)它們都無(wú)法滿足我們的訴求:以輕量級(jí)的方式實(shí)現(xiàn)模塊組合,提高模塊與組件的復(fù)用性,同時(shí)凸出呈現(xiàn)核心業(yè)務(wù)流程,輔助開發(fā)者快速抓住業(yè)務(wù)主線并建立對(duì)業(yè)務(wù)的全景認(rèn)知。這很大程度上是由于上述開源組件的定位大都是接口級(jí)別的服務(wù)編排或者是審批流之類的流程引擎,因此其實(shí)現(xiàn)方案或執(zhí)行成本往往較重,很多流程編排框架過(guò)分強(qiáng)調(diào)通過(guò)UI框架拖拽式實(shí)構(gòu)建業(yè)務(wù)流程,導(dǎo)致開發(fā)者需要先在代碼工程中實(shí)現(xiàn)業(yè)務(wù)組件,再到UI界面中構(gòu)建串聯(lián)流程,適用的場(chǎng)景有限且造成強(qiáng)烈的割裂感不說(shuō),開發(fā)者依然需要手動(dòng)配置組件之間的參數(shù)映射與數(shù)據(jù)傳遞邏輯,而脫離了開發(fā)工具的代碼提示與補(bǔ)全功能,這些邏輯的實(shí)現(xiàn)成本反而增大了。與這些問(wèn)題相比,拖拽式的UI界面雖然炫酷,但并不是我們的核心訴求。還有一些編排框架采用了中心化的部署方式,流程串聯(lián)與組件服務(wù)分離部部署,通過(guò)RPC實(shí)現(xiàn)組件調(diào)用,這種方式會(huì)付出巨大的網(wǎng)絡(luò)開銷及中間結(jié)果存儲(chǔ)成本。這種設(shè)計(jì)讓它們?cè)谂幚砣蝿?wù)場(chǎng)景中有較好的應(yīng)用,但是在交互式服務(wù)應(yīng)用場(chǎng)景中則會(huì)造成嚴(yán)重的性能問(wèn)題并付出巨大的運(yùn)行成本。因此,在經(jīng)過(guò)一次次嘗試之后我們最終決定舉起自研大旗,開發(fā)一套與PICASO架構(gòu)基本思想相適配的能力編排框架。

wKgaoWc7BImAMJ3pAA1anm7UCsw016.png

要想滿足我們?cè)谏衔闹刑岢龅哪芰幣畔嚓P(guān)的訴求,能力編排框架需要提供兩個(gè)基本功能:分別是在編碼階段通過(guò)簡(jiǎn)潔、直觀、易用的API輔助開發(fā)者定義業(yè)務(wù)流程,以及在請(qǐng)求處理階段根據(jù)開發(fā)者制定的執(zhí)行圖串聯(lián)各個(gè)業(yè)務(wù)組件完成請(qǐng)求處理流程。為了實(shí)現(xiàn)這兩個(gè)基本功能,PICASO框架采取了制定標(biāo)準(zhǔn)化業(yè)務(wù)執(zhí)行模版、內(nèi)嵌標(biāo)準(zhǔn)上下文機(jī)制以及自建能力編排框架三項(xiàng)舉措。

標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版

標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版立足于軟件系統(tǒng)的內(nèi)在本質(zhì)定義了適用任何業(yè)務(wù)場(chǎng)景的基本處理流程,就像Object對(duì)象在JDK中的作用一樣,標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版并不復(fù)雜,但它卻是PICASO框架中所有組件功能得以實(shí)現(xiàn)的基礎(chǔ)。從本質(zhì)上看,所有的軟件系統(tǒng)都在做三件事:數(shù)據(jù)的獲取、處理與存儲(chǔ)(或傳輸);從業(yè)務(wù)視角看,數(shù)據(jù)的處理又可細(xì)分為輸入數(shù)據(jù)的合法性校驗(yàn)以及數(shù)據(jù)的計(jì)算與轉(zhuǎn)換,而數(shù)據(jù)的合法性校驗(yàn)又可細(xì)分為對(duì)輸入數(shù)據(jù)直接進(jìn)行的校驗(yàn)以及需要結(jié)合系統(tǒng)內(nèi)外部詳情數(shù)據(jù)進(jìn)行的校驗(yàn)。基于上述論述,PICASO框架定義的業(yè)務(wù)處理的基本流程為:

1.參數(shù)預(yù)校驗(yàn):直接對(duì)請(qǐng)求入?yún)⑦M(jìn)行的校驗(yàn),這些校驗(yàn)邏輯通常都是簡(jiǎn)單的內(nèi)存計(jì)算,不依賴任何外部數(shù)據(jù),如參數(shù)完整性校驗(yàn)、參數(shù)值范圍校驗(yàn)、數(shù)據(jù)長(zhǎng)度校驗(yàn)等。

2.上下文初始化:基于校驗(yàn)后的入?yún)⒉樵償?shù)據(jù)詳情并填充到上下文中,如根據(jù)入?yún)⒅械膯卧狪D查詢單元詳情、根據(jù)userId獲取賬戶詳情等,這些數(shù)據(jù)將會(huì)在后續(xù)流程中使用。

3.基于上下文的業(yè)務(wù)校驗(yàn):執(zhí)行需要結(jié)合上下文中詳情數(shù)據(jù)才能進(jìn)行的業(yè)務(wù)校驗(yàn),如根據(jù)單元狀態(tài)判斷是否可以執(zhí)行物料的修改操作、判斷標(biāo)的物類型與物料計(jì)劃類型是否匹配等。

4.業(yè)務(wù)邏輯處理:基于參數(shù)及上下文中的詳情數(shù)據(jù)執(zhí)行領(lǐng)域模型(下一章節(jié)介紹)的構(gòu)造和修改,注意對(duì)于一些查詢類的服務(wù),這個(gè)步驟可能不是必須的。

5.數(shù)據(jù)持久化:將新建或修改后的領(lǐng)域模型保存到數(shù)據(jù)庫(kù)中或者調(diào)用外部服務(wù)API完成數(shù)據(jù)傳遞,這同樣是一個(gè)可選的標(biāo)準(zhǔn)步驟,另外有些服務(wù)在業(yè)務(wù)邏輯處理環(huán)節(jié)就已經(jīng)完成了數(shù)據(jù)傳遞。

6.發(fā)布領(lǐng)域事件:一些修改類的領(lǐng)域服務(wù)在完成請(qǐng)求處理之后可能需要通知其他領(lǐng)域內(nèi)的業(yè)務(wù)實(shí)體做一些相關(guān)的后置操作,PICASO框架是通過(guò)領(lǐng)域事件機(jī)制來(lái)實(shí)現(xiàn)這個(gè)功能的(后續(xù)章節(jié)中進(jìn)行詳細(xì)介紹)。

7.構(gòu)造處理結(jié)果:業(yè)務(wù)流程執(zhí)行完成后構(gòu)建返回給調(diào)用方的響應(yīng)數(shù)據(jù)。

標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版本質(zhì)上就是一個(gè)Executor模板類,上述基本業(yè)務(wù)流程也就是該模板類中主要的模板方法。在PICASO框架中,領(lǐng)域服務(wù)和領(lǐng)域能力都要繼承標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模板類,這樣做的目的是引導(dǎo)和約束開發(fā)者對(duì)領(lǐng)域服務(wù)和領(lǐng)域能力的具體實(shí)現(xiàn)邏輯按照標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行流程進(jìn)行二次拆分,從而可以讓框架對(duì)代碼進(jìn)行精細(xì)化地調(diào)用控制。標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版是對(duì)所有業(yè)務(wù)處理流程進(jìn)行的最頂層抽象,模版類中各個(gè)標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行步驟API的制定讓把不同業(yè)務(wù)模塊的串聯(lián)執(zhí)行職責(zé)從開發(fā)者手中轉(zhuǎn)義到框架手中成為可能,開發(fā)者不必手動(dòng)實(shí)現(xiàn)不同模塊和方法的串聯(lián)調(diào)用,而是專注于業(yè)務(wù)邏輯,實(shí)現(xiàn)填空式開發(fā),從而減少系統(tǒng)中的膠水代碼,提高信息密度,這本質(zhì)上就是依賴倒轉(zhuǎn)原則(Dependency Inversion Principle, DI)的應(yīng)用。下面的代碼片段給出了標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版的定義,出于突出呈現(xiàn)PICASO框架設(shè)計(jì)思想的目的,示例代碼去除了框架功能的具體實(shí)現(xiàn)邏輯,僅保留了核心要素及模版方法的定義。

/** * 標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模板基類,定義了基本的業(yè)務(wù)處理流程,所有領(lǐng)域服務(wù)和領(lǐng)域能力執(zhí)行器都必須繼承該類。 * * @param 業(yè)務(wù)執(zhí)行器對(duì)應(yīng)的參數(shù)類型,所有的執(zhí)行器參數(shù)都應(yīng)該繼承自標(biāo)準(zhǔn)參數(shù)基類Command對(duì)象 * @param 業(yè)務(wù)執(zhí)行器最終返回的執(zhí)行結(jié)果類型 * @param 業(yè)務(wù)執(zhí)行器使用的上下文對(duì)象類型,所有執(zhí)行器的上下文對(duì)象都應(yīng)該繼承標(biāo)準(zhǔn)上下文基類, * 請(qǐng)求的入?yún)⒑彤a(chǎn)生的中間結(jié)果都會(huì)保存在上下文對(duì)象中 */ public abstract class CommandExecutor > { /** * 參數(shù)預(yù)校驗(yàn),該步驟應(yīng)該只進(jìn)行純內(nèi)存計(jì)算操作 * @param context 上下文,此時(shí)的上下文中只有參數(shù)對(duì)象 */ protected Response doPreValidate(CTX context) { return Response.success(); } /** * 執(zhí)行上下文初始化,根據(jù)參數(shù)執(zhí)底層情數(shù)據(jù)的拓展查詢,并將查詢結(jié)果填充到context對(duì)象中 * @param context 上下文,調(diào)用該方法時(shí)的上下文中只有參數(shù)對(duì)象,調(diào)用完成后上下文將被填充 */ protected Response doInitContext(CTX context) { return Response.success(); } /** * 結(jié)合上下文中的底層數(shù)據(jù)執(zhí)行業(yè)務(wù)校驗(yàn) * @param context 上下文,此時(shí)的上下文中已經(jīng)完成了依賴的業(yè)務(wù)詳情數(shù)據(jù)的填充 */ protected Response doContextualValidate(CTX context) { return Response.success(); } /** * 結(jié)合上下文中的底層數(shù)據(jù)執(zhí)行業(yè)務(wù)邏輯的處理,對(duì)已有實(shí)體的變更及生成的新業(yè)務(wù)實(shí)體都會(huì)填充回上下文對(duì)象中 * @param context 上下文,業(yè)務(wù)邏輯執(zhí)行過(guò)程中的中間結(jié)果也可以暫存到到該上下文中 */ protected Response doProcessBizLogic(CTX context) { return Response.success(); } /** * 保存業(yè)務(wù)流程執(zhí)行過(guò)程中新建或者被修改過(guò)的業(yè)務(wù)實(shí)體,調(diào)用該方法時(shí),這些數(shù)據(jù)已經(jīng)被寫入到了上下文對(duì)象中 * @param context 上下文 */ public Response doPersistAggregates(CTX context) { return Response.success(); } /** * 構(gòu)造本次業(yè)務(wù)請(qǐng)求流程中需要對(duì)外發(fā)布的領(lǐng)域事件 * @param context 上下文 */ protected Response> doPublishAppEvent(CTX context) { return Response.success(); } /** * 構(gòu)造請(qǐng)求的返回值 * @param context 上下文 */ protected Response doAssembleResponse(CTX context) { return Response.success(); } }

到這里有些讀者可能還沒(méi)有意識(shí)到“把不同業(yè)務(wù)模塊的串聯(lián)調(diào)用職責(zé)從開發(fā)者手中轉(zhuǎn)移到框架手中”的價(jià)值,這項(xiàng)措施其實(shí)并沒(méi)有直接解決我們?cè)诒疚牡诙绿岢鋈魏我粋€(gè)痛點(diǎn)問(wèn)題,要想理解這一措施我們必須用辯證法重新審視前文介紹的各項(xiàng)復(fù)雜度應(yīng)對(duì)措施。根據(jù)前面幾個(gè)小節(jié)的論述,我們?yōu)榱藨?yīng)對(duì)業(yè)務(wù)復(fù)雜度而對(duì)請(qǐng)求處理流程進(jìn)行了各種粒度和場(chǎng)景的拆分,拆分出來(lái)的各類實(shí)體再疊加上實(shí)體內(nèi)部標(biāo)準(zhǔn)執(zhí)行步驟的二次拆解,必然會(huì)增加后續(xù)邏輯串聯(lián)和組裝的復(fù)雜度。如果此時(shí)還要求開發(fā)者手動(dòng)硬編碼實(shí)現(xiàn)邏輯組裝,那么勢(shì)必會(huì)帶來(lái)極高的開發(fā)負(fù)擔(dān)和出錯(cuò)概率,而且硬編碼組裝帶來(lái)的大量膠水代碼還會(huì)稀釋和掩蓋代碼中的關(guān)鍵信息,后續(xù)再進(jìn)行迭代時(shí)就容易產(chǎn)生改動(dòng)點(diǎn)遺漏和影響評(píng)估不全等問(wèn)題。PICASO框架解決這些問(wèn)題的措施就是由框架代替開發(fā)者實(shí)現(xiàn)合個(gè)模塊的串聯(lián)組裝,這也我們要定義標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版的根本原因:統(tǒng)一標(biāo)準(zhǔn)的調(diào)用入口是讓框架實(shí)現(xiàn)流程串聯(lián)的前提,進(jìn)而才可能實(shí)現(xiàn)將膠水代碼隱藏在框架內(nèi)部、提高業(yè)務(wù)層代碼信息密度、降低開發(fā)者編碼負(fù)擔(dān)的設(shè)計(jì)目標(biāo)。PICASO框架內(nèi)模塊串聯(lián)的詳細(xì)邏輯我們將在本章后三個(gè)小節(jié)中進(jìn)行闡述,在那之前我們先繼續(xù)介紹標(biāo)準(zhǔn)業(yè)務(wù)模版中的其他核心要素。

上下文機(jī)制

從標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版的示例代碼中我們可以看到,除了各個(gè)標(biāo)準(zhǔn)步驟的方法聲明之外,標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版還通過(guò)泛型變量定義了執(zhí)行器接收的請(qǐng)求參數(shù)類型、返回值類型以及上下文對(duì)象類型,其中“上下文”是標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版中的核心要素,業(yè)務(wù)數(shù)據(jù)就是通過(guò)它在各個(gè)標(biāo)準(zhǔn)步驟之間流轉(zhuǎn)的。所謂的上下文本質(zhì)上就是一個(gè)POJO(Plain Old Java Object),其內(nèi)部定義了業(yè)務(wù)流程執(zhí)行所需要的各種詳情數(shù)據(jù)。在《改進(jìn)我們的架構(gòu)》一文中我們已經(jīng)對(duì)上下文機(jī)制進(jìn)行了詳細(xì)的闡述,在這里我們簡(jiǎn)單回顧一下它的作用。如下圖所示,在傳統(tǒng)架構(gòu)中開發(fā)者往往直接面向數(shù)據(jù)庫(kù)編程,業(yè)務(wù)邏輯與數(shù)據(jù)庫(kù)操作互相交織,容易造成重復(fù)或碎片化的數(shù)據(jù)庫(kù)讀寫操作。而采用上下文機(jī)制之后,業(yè)務(wù)流程中的各個(gè)子模塊都不再封裝數(shù)據(jù)的讀寫操作,而是在請(qǐng)求一開始先將后續(xù)流程所需要的數(shù)據(jù)集中初始化到上下文對(duì)象中,后續(xù)各個(gè)業(yè)務(wù)模塊統(tǒng)一從上下文中獲取所需的詳情數(shù)據(jù),并把產(chǎn)生的中間結(jié)果寫入到上下文對(duì)象中,最后在所有子模塊業(yè)務(wù)邏輯執(zhí)行完成之后,集中將上下文中新增或發(fā)生變化的業(yè)務(wù)實(shí)體持久化到存儲(chǔ)介質(zhì)中。這種設(shè)計(jì)一方面能夠避免子模塊劃分導(dǎo)致的重復(fù)及碎片化的數(shù)據(jù)讀寫操作,另一方面,集中的數(shù)據(jù)操作可以啟發(fā)開發(fā)者采用批量、異步和并行等措施進(jìn)行極致地性能優(yōu)化。

wKgZoWc7BIyATkcrAAQsIVuLcfI607.png

上下文機(jī)制與傳統(tǒng)架構(gòu)業(yè)務(wù)處理流程對(duì)比

PICASO框架對(duì)上下文機(jī)制做了進(jìn)一步升級(jí)和深度集成,上下文作為核心要素被直接定義到標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版中,領(lǐng)域服務(wù)和領(lǐng)域能力執(zhí)行器都要通過(guò)泛型參數(shù)來(lái)聲明自己所需的上下文對(duì)象類型。需要說(shuō)明的是,盡管領(lǐng)域能力執(zhí)行器中也定義了“上下文初始化”標(biāo)準(zhǔn)步驟,但是PICASO框架依然建議開發(fā)者盡量在領(lǐng)域服務(wù)執(zhí)行器的上下文初始化步驟中就將各個(gè)領(lǐng)域能力所依賴的業(yè)務(wù)實(shí)體或外部數(shù)據(jù)集中批量查詢好,然后填充到領(lǐng)域服務(wù)上下文中。后續(xù)各個(gè)領(lǐng)域能力會(huì)優(yōu)先從領(lǐng)域服務(wù)上下文中獲取所需的詳情數(shù)據(jù),領(lǐng)域能力的上下文初始化步驟僅做依賴數(shù)據(jù)的非空校驗(yàn)或者做為從領(lǐng)域服務(wù)上下文中獲取不到所需數(shù)據(jù)時(shí)的托底補(bǔ)充查詢措施,這是由于領(lǐng)域服務(wù)作為整個(gè)業(yè)務(wù)流程的全局把控者,擁有最全的數(shù)據(jù)視角,可以對(duì)代價(jià)昂貴的IO操作進(jìn)行極致地調(diào)優(yōu),而領(lǐng)域能力則聚焦于業(yè)務(wù)流程的局部細(xì)節(jié),在能力內(nèi)部封裝的數(shù)據(jù)讀寫操作很容易隨著能力的組合或循環(huán)復(fù)用而被碎片化或重復(fù)地執(zhí)行。

需要注意的是上文中“領(lǐng)域能力優(yōu)先使用領(lǐng)域服務(wù)上下文中的數(shù)據(jù)”并不意味著領(lǐng)域能力會(huì)直接訪問(wèn)外層領(lǐng)域服務(wù)的上下文對(duì)象,這是由于同一個(gè)領(lǐng)域能力可能會(huì)被不同的領(lǐng)域服務(wù)所復(fù)用,因此領(lǐng)域能力不可能與其中任何一個(gè)領(lǐng)域服務(wù)的上下文耦合到一起。為了解決這個(gè)問(wèn)題,PICASO框架要求每個(gè)領(lǐng)域能力都要定義自己專有的上下文對(duì)象。在調(diào)用領(lǐng)域能力之前先將領(lǐng)域服務(wù)上下文中的數(shù)據(jù)傳遞到領(lǐng)域能力的上下文中,領(lǐng)域能力中的業(yè)務(wù)邏輯直接訪問(wèn)的依然是領(lǐng)域能力自己的上下文對(duì)象,在能力執(zhí)行過(guò)程中構(gòu)建的新實(shí)體或者對(duì)已有實(shí)體的修改也會(huì)直接保存到領(lǐng)域能力上下文中。而在完成能力調(diào)用之后,PICASO會(huì)將領(lǐng)域能力上下文中新生成或者發(fā)生變更的屬性傳遞回領(lǐng)域服務(wù)上下文中,從而在保持領(lǐng)域能力與領(lǐng)域服務(wù)解耦的前提下實(shí)現(xiàn)領(lǐng)域服務(wù)與領(lǐng)域能力上下文數(shù)據(jù)的共享。因此在PICASO框架中上下文機(jī)制除了起到避免碎片化及重復(fù)讀寫數(shù)據(jù)的作用之外,還負(fù)責(zé)在不同領(lǐng)域能力以及領(lǐng)域服務(wù)與領(lǐng)域能力之間進(jìn)行數(shù)據(jù)的傳遞和共享。

我們可以用一個(gè)例子來(lái)詳細(xì)描述上述機(jī)制,如下圖所示,領(lǐng)域服務(wù)內(nèi)編排了三個(gè)領(lǐng)域能力:A、B、C,其中能力A和C分別依賴業(yè)務(wù)實(shí)體1和實(shí)體4,能力B依賴能力A生成的數(shù)據(jù)實(shí)體2,完成業(yè)務(wù)邏輯處理后框架需要把能力B和C構(gòu)建的業(yè)務(wù)實(shí)體3和5以及能力C對(duì)實(shí)體4的修改保存到數(shù)據(jù)庫(kù)中。當(dāng)請(qǐng)求到來(lái)時(shí)PICASO框架會(huì)首先調(diào)用領(lǐng)域服務(wù)的上下文初始化標(biāo)準(zhǔn)步驟(initContext)完成實(shí)體1與實(shí)體4的查詢,在調(diào)用能力A之前會(huì)將實(shí)體1從領(lǐng)域服務(wù)上下文拷貝到能力A上下文中,完成能力A的調(diào)用后會(huì)將其構(gòu)建的實(shí)體2從能力A的上下文中拷貝回領(lǐng)域服務(wù)上下文,然后將領(lǐng)域服務(wù)的上下文作為兩個(gè)能力之間數(shù)據(jù)共享和交換的通道,在調(diào)用能力B之前將實(shí)體2拷貝到能力B的上下文中......以此類推用相同的方式完成能力B和能力C的調(diào)用,最后PICASO框架會(huì)調(diào)用領(lǐng)域服務(wù)的聚合根持久化標(biāo)準(zhǔn)步驟(persistAggregate),集中將新生成的實(shí)體3和5以及由能力C修改后的實(shí)體4持久化到數(shù)據(jù)庫(kù)中。

wKgaoWc7BIyAWh1qAAGOJeSo7fQ622.png

領(lǐng)域服務(wù)與領(lǐng)域能力上下文之間的數(shù)據(jù)傳遞關(guān)系

上下文機(jī)制作為PICASO框架的基礎(chǔ)組件,其內(nèi)部除了用戶自定義的業(yè)務(wù)屬性之外還承載著大量框架內(nèi)部運(yùn)行所需的狀態(tài)數(shù)據(jù)以及大量為開發(fā)者提供的工具API。在本文中我們僅對(duì)其基本運(yùn)行機(jī)制進(jìn)行了介紹,關(guān)于上下文的使用技巧及其基類中各種工具API的使用方法,我們將在《PICASO框架最佳實(shí)踐——上下文機(jī)制》一文中進(jìn)行詳細(xì)的闡述。

標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎

在上一小節(jié)的最后我們通過(guò)一個(gè)架空的例子論述了PICASO框架內(nèi)部的數(shù)據(jù)傳遞流程,這些數(shù)據(jù)傳遞規(guī)則并不需要開發(fā)者手動(dòng)實(shí)現(xiàn),而是通過(guò)PICASO框架內(nèi)置的標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎自動(dòng)觸發(fā)的。接下來(lái)我們就將詳細(xì)闡述標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎是如何將領(lǐng)域服務(wù)及領(lǐng)域能力各個(gè)標(biāo)準(zhǔn)步驟串聯(lián)到一起的。但是在此之前,我們有必要必再次明確領(lǐng)域服務(wù)和領(lǐng)域能力執(zhí)行器的職責(zé),這對(duì)理解標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎的設(shè)計(jì)動(dòng)機(jī)十分重要。

在PICASO框架中,系統(tǒng)對(duì)外提供的服務(wù)都是由領(lǐng)域服務(wù)執(zhí)行器承載的,作為整個(gè)業(yè)務(wù)流程的全局把控者,領(lǐng)域服務(wù)執(zhí)行器的基本職責(zé)就是定義業(yè)務(wù)流程(編排組裝領(lǐng)域能力)以及管理業(yè)務(wù)數(shù)據(jù)(上下文的初始化及持久化),而領(lǐng)域能力執(zhí)行器則聚焦在完整業(yè)務(wù)流程中的某個(gè)特定模塊,負(fù)責(zé)實(shí)現(xiàn)該模塊內(nèi)部具體的業(yè)務(wù)規(guī)則。如前文所述,領(lǐng)域服務(wù)是通過(guò)領(lǐng)域能力組合編排而成的,并且它們都繼承了標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版,因此不難推導(dǎo)出領(lǐng)域服步驟其實(shí)就是通過(guò)各個(gè)領(lǐng)域能力的相應(yīng)標(biāo)準(zhǔn)步驟組合而成的。但是這并不意味著業(yè)務(wù)流程中所有的業(yè)務(wù)邏輯都會(huì)下沉到領(lǐng)域能力中,比如領(lǐng)域服務(wù)上下文初始化操作就必須在領(lǐng)域服務(wù)執(zhí)行器中直接定義。此外,考慮到領(lǐng)域能力聚焦于局部業(yè)務(wù)細(xì)節(jié),無(wú)法獨(dú)立對(duì)外提供服務(wù),為了明確組件職責(zé),避免給開發(fā)者帶來(lái)困惑,領(lǐng)域能力執(zhí)行器對(duì)標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器模版進(jìn)行了二次拓展,隱藏了完整請(qǐng)求流程處理維度才需要的聚合根持久化(persisteAggregates)、構(gòu)建并發(fā)布領(lǐng)域事件(publishAppEvent)以及組裝請(qǐng)求響應(yīng)數(shù)據(jù)(assembleResponse)標(biāo)準(zhǔn)步驟,因此這三個(gè)模版方法對(duì)應(yīng)的業(yè)務(wù)邏輯也需要直接在領(lǐng)域服務(wù)執(zhí)行器中定義。

領(lǐng)域服務(wù)及領(lǐng)域能力執(zhí)行器的職責(zé)劃分決定了二者之間的數(shù)據(jù)傳遞時(shí)機(jī)及其標(biāo)準(zhǔn)步驟之間的組合關(guān)系,標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎的模塊串聯(lián)規(guī)則就是基于此制定的。其核心設(shè)計(jì)就是對(duì)業(yè)務(wù)流程中各個(gè)領(lǐng)域能力標(biāo)準(zhǔn)步驟的重組執(zhí)行。在PICASO框架的早期摸索階段,我們?cè)鴥A向于將同一個(gè)業(yè)務(wù)模塊的參數(shù)校驗(yàn)與業(yè)務(wù)處理邏輯劃分到兩個(gè)不同的領(lǐng)域能力中。這種能力劃分方式固然也能實(shí)現(xiàn)業(yè)務(wù)功能,甚至也能起到復(fù)雜度分離的作用,但是這種劃分方式會(huì)造成業(yè)務(wù)邏輯的沙粒化分解,產(chǎn)生大量瑣碎的小能力,這反而會(huì)增加系統(tǒng)的開發(fā)及維護(hù)成本。另外,由于同一個(gè)模塊的參數(shù)校驗(yàn)及業(yè)務(wù)處理邏輯往往會(huì)依賴相同的底層數(shù)據(jù),沙礫化的能力劃分會(huì)急劇增加能力間數(shù)據(jù)傳遞和共享的負(fù)擔(dān),稍有不慎就會(huì)造成數(shù)據(jù)的碎片化讀寫,進(jìn)而對(duì)系統(tǒng)性能產(chǎn)生影響。因此我們最終選擇回歸業(yè)務(wù)本質(zhì),以最小原子業(yè)務(wù)邊界作為能力劃分的準(zhǔn)則,將同一模塊內(nèi)關(guān)聯(lián)緊密的參數(shù)校驗(yàn)、上下文初始化、上下文校驗(yàn)及業(yè)務(wù)處理邏輯封裝到同一個(gè)領(lǐng)域能力執(zhí)行器中。但是這種封裝規(guī)則卻帶來(lái)了新的問(wèn)題:領(lǐng)域服務(wù)由領(lǐng)域能力組合而成,如果我們直接依次串行調(diào)用每個(gè)領(lǐng)域能力內(nèi)的各個(gè)標(biāo)準(zhǔn)步驟,將無(wú)法實(shí)現(xiàn)領(lǐng)域能力與領(lǐng)域服務(wù)標(biāo)準(zhǔn)步驟之間的協(xié)調(diào)執(zhí)行,另外由于調(diào)用后置能力時(shí)前置能力所有標(biāo)準(zhǔn)步驟都已執(zhí)行完畢,如果后置能力的參數(shù)校驗(yàn)失敗而前置能力在業(yè)務(wù)邏輯處理步驟已經(jīng)與外部系統(tǒng)產(chǎn)生了數(shù)據(jù)交互,此時(shí)就會(huì)產(chǎn)生臟數(shù)據(jù)等問(wèn)題。然而標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版的抽象則為我們帶來(lái)了該問(wèn)題的解決方案:將領(lǐng)域能力執(zhí)行器中的各個(gè)標(biāo)準(zhǔn)步驟拆散到領(lǐng)域服務(wù)執(zhí)行器相應(yīng)的標(biāo)準(zhǔn)步驟中重組執(zhí)行,而不是依次觸發(fā)每個(gè)領(lǐng)域能力的全部標(biāo)準(zhǔn)步驟,如下圖所示,在領(lǐng)域服務(wù)的參數(shù)預(yù)校驗(yàn)標(biāo)準(zhǔn)步驟中會(huì)按照能力執(zhí)行圖依次觸發(fā)各個(gè)領(lǐng)域能力的參數(shù)預(yù)校驗(yàn)步驟,而在領(lǐng)域服務(wù)的上下文初始化步驟中則會(huì)依次觸發(fā)各個(gè)領(lǐng)域能力執(zhí)行器中的上下文初始化步驟。這種重組執(zhí)行機(jī)制確保了服務(wù)請(qǐng)求流程能夠整體按照參數(shù)預(yù)校驗(yàn)、上下文初始化、上下文校驗(yàn)、業(yè)務(wù)邏輯處理、聚合根持久化、發(fā)布領(lǐng)域事件、構(gòu)造返回值的標(biāo)準(zhǔn)流程執(zhí)行下去,實(shí)現(xiàn)fail-fast特性,避免由于后置操作校驗(yàn)失敗而前置操作已執(zhí)行導(dǎo)致的IO資源浪費(fèi)及臟數(shù)據(jù)問(wèn)題,另外這種運(yùn)行機(jī)制帶來(lái)的額外收益是讓我們能夠利用現(xiàn)有服務(wù)快速實(shí)現(xiàn)請(qǐng)求預(yù)校驗(yàn)接口,這一點(diǎn)我們?cè)诒疚牡谒恼碌腜ICASO框架開發(fā)流程示例中將有專門的呈現(xiàn)。

上述過(guò)程也是辯證法的生動(dòng)詮釋,事物之間存在普遍聯(lián)系,在對(duì)立統(tǒng)一中不斷發(fā)展。PICASO框架中也是在一次次提出方案、引發(fā)新問(wèn)題、解決新問(wèn)題的過(guò)程逐漸成型的,框架中各個(gè)組件互相支撐,互為因果,共同實(shí)現(xiàn)整潔架構(gòu)的最終目標(biāo)。我們也希望各位讀者在閱讀本文時(shí)能夠始終將本文介紹的各項(xiàng)組件聯(lián)系到一起來(lái)理解框架的指導(dǎo)思想和設(shè)計(jì)動(dòng)機(jī),這對(duì)未來(lái)我們能否在實(shí)際業(yè)務(wù)用好PICASO框架來(lái)說(shuō)十分重要。

wKgZoWc7BI6AT9T9AAffngV0LKc232.png

標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎的重組執(zhí)行流程

下面我們將結(jié)合著上圖所示的能力標(biāo)準(zhǔn)步驟重組執(zhí)行流程圖逐步解析標(biāo)準(zhǔn)業(yè)務(wù)流程模版執(zhí)行引擎的運(yùn)行機(jī)制:

1. 參數(shù)預(yù)校驗(yàn)

當(dāng)請(qǐng)求到來(lái)時(shí),PICASO框架會(huì)首先通過(guò)領(lǐng)域服務(wù)門面定位到具體的領(lǐng)域服務(wù)執(zhí)行器實(shí)例,然后調(diào)用其參數(shù)預(yù)校驗(yàn)標(biāo)準(zhǔn)步驟(preValidate),在該方法中會(huì)首先執(zhí)行領(lǐng)域服務(wù)執(zhí)行器直接定義的參數(shù)預(yù)校驗(yàn)邏輯(當(dāng)然也可以根據(jù)開發(fā)者的設(shè)計(jì)意圖調(diào)整為先觸發(fā)各個(gè)領(lǐng)域能力的參數(shù)預(yù)校驗(yàn)邏輯),然后再觸發(fā)領(lǐng)域能力執(zhí)行圖中各個(gè)領(lǐng)域能力的參數(shù)預(yù)校驗(yàn)邏輯,需要注意的是由于領(lǐng)域能力執(zhí)行器有自己專屬的參數(shù)及上下文對(duì)象,因此在調(diào)用各個(gè)能力參數(shù)預(yù)校驗(yàn)方法之前,PICASO會(huì)自動(dòng)將領(lǐng)域服務(wù)入?yún)?duì)象中的屬性拷貝到領(lǐng)域能力入?yún)?duì)象中同名同類型的屬性上(與Spring框架中BeanUtils.copyProperties的邏輯相同)。

2. 上下文初始化

完成參數(shù)預(yù)校驗(yàn)邏輯之后,PICASO會(huì)開始執(zhí)行領(lǐng)域服務(wù)的上下文初始化邏輯。我們鼓勵(lì)開發(fā)者將各個(gè)領(lǐng)域能力所依賴的底層數(shù)據(jù)集中到領(lǐng)域服務(wù)的上下文初始化邏輯中批量查詢好,因?yàn)轭I(lǐng)域服務(wù)作為整個(gè)業(yè)務(wù)流程的全局把控者,擁有最全面的數(shù)據(jù)視角,可以進(jìn)行最徹底的性能優(yōu)化。完成領(lǐng)域服務(wù)直接定義的上下文初始化邏輯之后,PICASO將調(diào)用能力執(zhí)行圖中各個(gè)領(lǐng)域能力的上下文初始化步驟,但是在此之前,與領(lǐng)域服務(wù)與領(lǐng)域能力之間的參數(shù)傳遞邏輯類似,PICASO框架會(huì)先將領(lǐng)域服務(wù)上下文對(duì)象中的屬性拷貝到領(lǐng)域能力上下文對(duì)象中同名同類型的屬性上。

有些讀者可能會(huì)對(duì)本小節(jié)的論述有些疑惑,封裝領(lǐng)域能力的目的之一是為了邏輯復(fù)用,然而我們卻要將其依賴數(shù)據(jù)的初始化邏輯代理到領(lǐng)域服務(wù)中,那么當(dāng)一個(gè)領(lǐng)域能力被不同的領(lǐng)域服務(wù)引用時(shí),是否會(huì)造成重復(fù)編碼呢?這個(gè)問(wèn)題的答案是肯定的,但是絕大多數(shù)場(chǎng)景下領(lǐng)域能力依賴的底層實(shí)體通常不多,一方面我們可以通過(guò)接下來(lái)將要介紹的“聚合與資源庫(kù)”機(jī)制簡(jiǎn)化這些底層實(shí)體的查詢邏輯,與由此收獲的性能提升收益相比,重復(fù)編碼所付出的輕微代價(jià)是完全值得的。另一方面我們其實(shí)并不建議在領(lǐng)域能力內(nèi)部實(shí)現(xiàn)依賴數(shù)據(jù)的初始化查詢操作,因?yàn)槟軌虮痪幣诺酵粋€(gè)領(lǐng)域服務(wù)中的領(lǐng)域能力通常都會(huì)依賴相同的業(yè)務(wù)實(shí)體,如果要在每一個(gè)領(lǐng)域能力內(nèi)都實(shí)現(xiàn)一遍實(shí)體查詢邏輯同樣會(huì)造成重復(fù)編碼。因此我們建議還是統(tǒng)一由領(lǐng)域服務(wù)完成上下文的初始化,然后通過(guò)上下文傳遞機(jī)制拷貝到領(lǐng)域能力上下文中,領(lǐng)域能力僅在上下文校驗(yàn)標(biāo)準(zhǔn)步驟中做好上下文參數(shù)的非空校驗(yàn),確保領(lǐng)域服務(wù)傳遞過(guò)來(lái)了正確的數(shù)據(jù)。更多關(guān)于領(lǐng)域服務(wù)及能力上下文數(shù)據(jù)傳遞方案設(shè)計(jì)的技巧請(qǐng)參考《PICASO框架最佳實(shí)踐——上下文機(jī)制》。

3. 上下文校驗(yàn)

完成領(lǐng)域服務(wù)及各個(gè)領(lǐng)域能力的上下文初始化邏輯之后,PICASO會(huì)繼續(xù)執(zhí)行領(lǐng)域服務(wù)及各個(gè)領(lǐng)域能力的上下文校驗(yàn)邏輯。該標(biāo)準(zhǔn)步驟內(nèi)執(zhí)行的是需要結(jié)合上下文中的底層數(shù)據(jù)才能進(jìn)行的校驗(yàn)邏輯,如調(diào)整預(yù)算時(shí)要求新預(yù)算與歷史預(yù)算差值必須大于5%且必須大于當(dāng)前消耗,該邏輯依賴歷史預(yù)算及物料當(dāng)前消耗詳情,就可以在上下文初始化步驟完成這兩部分底層數(shù)據(jù)的查詢,然后在上下文校驗(yàn)步驟中直接從上下文中取出詳情數(shù)據(jù)執(zhí)行相關(guān)的校驗(yàn)規(guī)則。默認(rèn)情況下領(lǐng)域服務(wù)與領(lǐng)域能力在上下文校驗(yàn)步驟不需要執(zhí)行任何的參數(shù)或上下文傳遞操作。

4. 業(yè)務(wù)邏輯處理

基于上下文的業(yè)務(wù)校驗(yàn)通過(guò)之后,PICASO框架會(huì)繼續(xù)觸發(fā)領(lǐng)域服務(wù)及能力執(zhí)行圖中各個(gè)領(lǐng)域能力的業(yè)務(wù)處理邏輯。需要注意的是由于領(lǐng)域能力的業(yè)務(wù)邏輯處理過(guò)程中可能會(huì)對(duì)上下文中已有的實(shí)體進(jìn)行了修改,也可能會(huì)構(gòu)建出新的業(yè)務(wù)實(shí)體對(duì)象,這些變更最終都需要被持久化到存儲(chǔ)介質(zhì)或者外部系統(tǒng)中。因此在默認(rèn)情況下,PICASO會(huì)在能每一個(gè)領(lǐng)域能力的業(yè)務(wù)邏輯處理標(biāo)準(zhǔn)步驟執(zhí)行完成之后執(zhí)行一次領(lǐng)域能力上下文到領(lǐng)域服務(wù)上下文的數(shù)據(jù)回傳操作,將領(lǐng)域能力上下文中的屬性拷貝到領(lǐng)域服務(wù)上下文中同名同類型的屬性上。這些數(shù)據(jù)將在領(lǐng)域服務(wù)的后續(xù)步驟中被持久化到存儲(chǔ)介質(zhì)中,或者被用于構(gòu)造領(lǐng)域事件及請(qǐng)求響應(yīng)結(jié)果。

6. 聚合根持久化、發(fā)布領(lǐng)域事件、構(gòu)造響應(yīng)結(jié)果

由于標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行器中的剩余的幾個(gè)標(biāo)準(zhǔn)步驟承載的都是請(qǐng)求維度的邏輯,領(lǐng)域能力執(zhí)行器標(biāo)準(zhǔn)模版中對(duì)這幾個(gè)方法也做了屏蔽,因此在這幾個(gè)標(biāo)準(zhǔn)步驟的執(zhí)行流程中就不需要再調(diào)用領(lǐng)域能力執(zhí)行圖了。需要特別說(shuō)明的是,當(dāng)執(zhí)行到聚合根持久化標(biāo)準(zhǔn)步驟時(shí),定義在領(lǐng)域服務(wù)及領(lǐng)域能力中的業(yè)務(wù)規(guī)則對(duì)實(shí)體的變更以及構(gòu)建出的新業(yè)務(wù)實(shí)體都已經(jīng)寫入到了上下文中,開發(fā)者可以充分利用領(lǐng)域服務(wù)對(duì)數(shù)據(jù)操作全局把控的職責(zé)定位,積極采用批量、異步、并行等手段進(jìn)行極致地性能優(yōu)化。

7. 定制化執(zhí)行流程

前五步內(nèi)容介紹了領(lǐng)域服務(wù)及領(lǐng)域能力標(biāo)準(zhǔn)執(zhí)行模版默認(rèn)的串聯(lián)執(zhí)行邏輯,PICASO框架也遵循約定大于配置(convention over configuration)的基本原則,如果默認(rèn)的執(zhí)行邏輯能夠滿足開發(fā)者的訴求,開發(fā)者不需要實(shí)現(xiàn)過(guò)多的流程控制,但是要更靈活地適配各類業(yè)務(wù)場(chǎng)景,PICASO框架也支持開發(fā)者對(duì)上述標(biāo)準(zhǔn)串聯(lián)執(zhí)行邏輯進(jìn)行定制化的修改:

?首先,PICASO允許開發(fā)者指定僅執(zhí)行領(lǐng)域服務(wù)的部分標(biāo)準(zhǔn)步驟,如前臺(tái)業(yè)務(wù)方期望能夠在實(shí)際調(diào)用系統(tǒng)的單元?jiǎng)?chuàng)建接口之前先對(duì)其構(gòu)造出來(lái)的請(qǐng)求參數(shù)進(jìn)行校驗(yàn)提前發(fā)現(xiàn)問(wèn)題,因此希望系統(tǒng)為其提供一個(gè)預(yù)校驗(yàn)接口(注意這里的“預(yù)校驗(yàn)”不是標(biāo)準(zhǔn)執(zhí)行模版中的參數(shù)預(yù)校驗(yàn)步驟),該場(chǎng)景就可以直接復(fù)用物單元新建領(lǐng)域服務(wù)執(zhí)行器,并且在觸發(fā)領(lǐng)域服務(wù)執(zhí)行器時(shí)指定間僅執(zhí)行該領(lǐng)域服務(wù)的參數(shù)預(yù)校驗(yàn)、上下文初始化及上下文校驗(yàn)邏輯,領(lǐng)域服務(wù)完成前三個(gè)標(biāo)準(zhǔn)步驟的執(zhí)行之后就會(huì)立即返回前三步的執(zhí)行結(jié)果,從而快速實(shí)現(xiàn)業(yè)務(wù)方訴求,這其實(shí)也是標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版抽象帶來(lái)的額外收益。

?其次,PICASO框架支持開發(fā)者對(duì)領(lǐng)域能力執(zhí)行圖內(nèi)的參數(shù)及上下文傳遞的時(shí)機(jī)與具體映射邏輯、能力調(diào)用的失敗與異常處理以及各個(gè)領(lǐng)域能力的觸發(fā)時(shí)機(jī)等行為進(jìn)行定制化的修改,如出價(jià)設(shè)置能力與預(yù)算設(shè)置能力都依賴物料當(dāng)前的消耗數(shù)據(jù),除了在領(lǐng)域服務(wù)的上下文初始化步驟完成查詢的常規(guī)設(shè)計(jì)之外,也可以讓出價(jià)設(shè)置能力完成消耗數(shù)據(jù)查詢,然后以領(lǐng)域服務(wù)上下文作為媒介,將物料消耗數(shù)據(jù)從出價(jià)設(shè)置能力傳遞到預(yù)算設(shè)置能力中。這個(gè)時(shí)候就可以指定PICASO框架在完成出價(jià)設(shè)置能力的上下文初始化步驟調(diào)用之后立即執(zhí)行一次從能力到領(lǐng)域服務(wù)上下文的數(shù)據(jù)回傳操作,而不必等到默認(rèn)的業(yè)務(wù)邏輯處理步驟完成之后。而這些定制化的串聯(lián)執(zhí)行配置都可以通過(guò)接下來(lái)將要介紹的能力編排領(lǐng)域特定語(yǔ)言來(lái)實(shí)現(xiàn)。

能力編排執(zhí)行圖

在前一小節(jié)中我們介紹了PICASO框架內(nèi)部各個(gè)原子模塊與標(biāo)準(zhǔn)步驟的串聯(lián)執(zhí)行流程,PICASO框架通過(guò)內(nèi)置的標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎將各個(gè)模塊的串聯(lián)執(zhí)行職責(zé)從開發(fā)者手中轉(zhuǎn)移到了框架內(nèi)部,從而讓開發(fā)者專注于業(yè)務(wù)規(guī)則設(shè)計(jì),實(shí)現(xiàn)填空式開發(fā)。這里說(shuō)的“業(yè)務(wù)規(guī)則”一方面是指領(lǐng)域服務(wù)與領(lǐng)域能力各個(gè)標(biāo)準(zhǔn)步驟內(nèi)具體的業(yè)務(wù)邏輯,另一方面是要明確當(dāng)前業(yè)務(wù)流程需要按照什么樣的順序執(zhí)行哪些領(lǐng)域能力、能力執(zhí)行的前置條件、對(duì)默認(rèn)串聯(lián)規(guī)則的定制化配置(包括參數(shù)傳遞規(guī)則、上下文傳遞規(guī)則、錯(cuò)誤及異常處理邏輯等),這些信息將以領(lǐng)域能力執(zhí)行圖的形式提供給PICASO框架,之后框架就可以按照開發(fā)者的意圖完成對(duì)各個(gè)領(lǐng)域能力的串聯(lián)調(diào)用,而能力編排指的就是構(gòu)建領(lǐng)域能力執(zhí)行圖的過(guò)程。

PICASO能力編排框架的核心職責(zé)有兩個(gè),首先是在編碼階段讓開發(fā)者能夠以易用、簡(jiǎn)潔、直白的方式快速定義出業(yè)務(wù)流程對(duì)應(yīng)的領(lǐng)域能力執(zhí)行圖,其次是在請(qǐng)求處理階段將能力執(zhí)行圖解析為可以被標(biāo)準(zhǔn)模版執(zhí)行引擎理解的執(zhí)行計(jì)劃,讓其能夠根據(jù)開發(fā)者意圖完成業(yè)務(wù)邏輯的處理。我們可以通過(guò)下圖所示的框架內(nèi)部實(shí)體關(guān)系圖對(duì)上述兩項(xiàng)職責(zé)進(jìn)行詳細(xì)闡述,圖中藍(lán)色線條標(biāo)記的是領(lǐng)域能力執(zhí)行圖的構(gòu)建過(guò)程,紅色線條標(biāo)記的是請(qǐng)求到來(lái)時(shí)領(lǐng)域能力執(zhí)行圖的執(zhí)行流程。

wKgaoWc7BI-AYG6OAAH447HlPwM876.png

PICASO能力編排框架內(nèi)部實(shí)體關(guān)系圖

上圖其實(shí)不算是標(biāo)準(zhǔn)的實(shí)體關(guān)系圖,它更像是實(shí)體關(guān)系圖與流程圖的結(jié)合,其中不同顏色的線表示不同執(zhí)行流程。事實(shí)上我們認(rèn)為這種呈現(xiàn)方式更加符合現(xiàn)實(shí),實(shí)體之間的關(guān)系本就是復(fù)雜的,在不同的場(chǎng)景和流程下實(shí)體之間的關(guān)系和相互作用往往也是不同的。

從圖中我們可以看到,每一個(gè)領(lǐng)域服務(wù)執(zhí)行器內(nèi)部都集成了一個(gè)領(lǐng)域能力編排器,在編碼階段,開發(fā)者可以通過(guò)它提供的領(lǐng)域特定語(yǔ)言(Domain Specific Language, DSL)以直白的方式構(gòu)建領(lǐng)域能力執(zhí)行圖。能力執(zhí)行圖由多個(gè)領(lǐng)域能力編排節(jié)點(diǎn)構(gòu)成,每一個(gè)領(lǐng)域能力編排節(jié)點(diǎn)內(nèi)部都封裝著一個(gè)領(lǐng)域能力門面執(zhí)行器。當(dāng)請(qǐng)求到來(lái)時(shí),業(yè)務(wù)模版執(zhí)行引擎會(huì)首先對(duì)能力編排執(zhí)行圖進(jìn)行解析,根據(jù)本次請(qǐng)求的參數(shù)及上下文信息將執(zhí)行圖中各個(gè)能力編排節(jié)點(diǎn)解析為零到多個(gè)領(lǐng)域能力執(zhí)行要素,所謂的領(lǐng)域能力執(zhí)行要素就是一個(gè)[領(lǐng)域能力執(zhí)行器、參數(shù)對(duì)象、上下文對(duì)象]三元組,它是一個(gè)有狀態(tài)的、會(huì)話級(jí)生命周期的實(shí)體,除了核心的能力執(zhí)行三要素之外,其內(nèi)部還維護(hù)著在請(qǐng)求處理過(guò)程中執(zhí)行引擎產(chǎn)生的一些控制中間狀態(tài),如當(dāng)前已執(zhí)行到哪個(gè)標(biāo)準(zhǔn)步驟、調(diào)用過(guò)程中是否發(fā)生了異常、本次調(diào)用是否已經(jīng)提前終止等。執(zhí)行圖中解析出來(lái)的所有領(lǐng)域能力執(zhí)行要素將被構(gòu)造成一個(gè)領(lǐng)域能力執(zhí)行要素鏈調(diào)用器,它用來(lái)控制各個(gè)領(lǐng)域能力執(zhí)行要素的觸發(fā)行為,包括能力執(zhí)行器各個(gè)標(biāo)準(zhǔn)步驟的逐步調(diào)用、能力編排節(jié)點(diǎn)的延遲解析、不同執(zhí)行要素之間的并行調(diào)用、能力執(zhí)行要器的提前終止等。在領(lǐng)域能力執(zhí)行要素鏈調(diào)用器的控制之下,能力執(zhí)行圖中的各個(gè)能力門面執(zhí)行器被依次觸發(fā),通過(guò)標(biāo)準(zhǔn)可執(zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制定位到當(dāng)前請(qǐng)求應(yīng)該使用的能力實(shí)例,調(diào)用其各個(gè)標(biāo)準(zhǔn)步驟完成業(yè)務(wù)邏輯處理。由于本文旨在介紹PICASO框架中各項(xiàng)組件的基本原理和運(yùn)行機(jī)制,并沒(méi)有對(duì)能力編排框架的實(shí)現(xiàn)細(xì)節(jié)做過(guò)多探討,有關(guān)能力編排框架各項(xiàng)特性的詳細(xì)介紹請(qǐng)參考《PICASO框架最佳實(shí)踐——能力編排》。

能力編排領(lǐng)域特定語(yǔ)言

在PICASO框架設(shè)計(jì)之初,我們也曾想直接引入一些開源的流程編排框架來(lái)實(shí)現(xiàn)領(lǐng)域能力之間的串聯(lián)調(diào)用。但是正如本章節(jié)最開始論述的那樣,現(xiàn)有的開源解決方案并沒(méi)有滿足我們的核心關(guān)切:以直白、簡(jiǎn)潔、輕量、易用的方式實(shí)現(xiàn)能力組裝,解決為了應(yīng)對(duì)業(yè)務(wù)本質(zhì)復(fù)雜度而采取的各項(xiàng)實(shí)體拆分與路由機(jī)制帶來(lái)的編碼繁瑣、模塊組裝邏輯復(fù)雜等副作用,減少膠水代碼和開發(fā)者的編碼負(fù)擔(dān),提高關(guān)鍵業(yè)務(wù)信息密度。圖形化、配置化的流程編排框架雖然能夠直觀的呈現(xiàn)業(yè)務(wù)處理流程,但是也造成靈活度差、普適性低、開發(fā)流程割裂、業(yè)務(wù)知識(shí)分散、模塊串聯(lián)配置繁瑣等問(wèn)題,無(wú)法達(dá)成整體熵減的設(shè)計(jì)目標(biāo),而PICASO框架解決這些問(wèn)題的方案則是自定義能力編排領(lǐng)域特定語(yǔ)言。

領(lǐng)域特定語(yǔ)言(Domain Specific Language, DSL)是專門針對(duì)特定應(yīng)用領(lǐng)域的計(jì)算機(jī)語(yǔ)言,與C++、Java等通用計(jì)算機(jī)語(yǔ)言(General Purpose Language,GPL)相比,領(lǐng)域特定語(yǔ)言的功能及普適性十分有限,但是在特定領(lǐng)域之內(nèi)它卻具有強(qiáng)大的表達(dá)能力。領(lǐng)域特定語(yǔ)言的核心吸引力在于它提供了一種更清晰地傳達(dá)系統(tǒng)各部分意圖的方法,提高代碼的可讀性(雖然我們總是有意或者無(wú)意地低估了代碼可讀性對(duì)生產(chǎn)力的影響),降低開發(fā)者與領(lǐng)域?qū)<遥óa(chǎn)品、測(cè)試甚至是用戶)的溝通難度。它能夠讓使用者輕松實(shí)現(xiàn)聲明式編程(Declarative Program),對(duì)業(yè)務(wù)層開發(fā)者來(lái)說(shuō),這意味著他們可以直接告訴框架他們想做什么,而不必編寫要想達(dá)成目的而需要執(zhí)行的具體操作步驟(Martin Fowler, Domain Specific Language, 2013),也正是這一點(diǎn)讓PICASO框架能夠?qū)⑶拔乃龅母黜?xiàng)業(yè)務(wù)復(fù)雜度應(yīng)對(duì)措施帶來(lái)的系統(tǒng)偶然復(fù)雜度屏蔽在框架內(nèi)部,將PICASO框架內(nèi)部的各個(gè)功能模塊有機(jī)結(jié)合到一起,共同實(shí)現(xiàn)整體熵減的設(shè)計(jì)目標(biāo)。

軟件工程領(lǐng)域的大師Martin Fowler將領(lǐng)域特定語(yǔ)言分為外部DSL(External DSL)和內(nèi)部DSL(Internal DSL)兩大類。外部DSL往往擁有自定義語(yǔ)法、需要宿主應(yīng)用的代碼執(zhí)行文本解析,基于該類DSL編寫的業(yè)務(wù)規(guī)則通常以腳本或配置的形式存在于系統(tǒng)代碼之外,典型的案例是正則表達(dá)式。而內(nèi)部DSL是通用編程語(yǔ)言的子集,它對(duì)外提供一組特定的API,利用內(nèi)部DSL編寫的業(yè)務(wù)規(guī)則往往是一段合法的代碼,典型的例子就是JDK8之后提供的Java Stream API。與外部DSL相比,內(nèi)部DSL不需要專門的語(yǔ)法解析器和開發(fā)平臺(tái),可以直接與宿主應(yīng)用代碼無(wú)縫銜接,也能直接復(fù)用普通IDE的代碼提示與自動(dòng)補(bǔ)全功能,也正因?yàn)榇?,為了向業(yè)務(wù)開發(fā)者提供集中、連貫的開發(fā)體驗(yàn),我們最終選擇為PICASO能力編排框架開發(fā)一套內(nèi)部領(lǐng)域特定語(yǔ)言。

為了盡可能靈活地適配所有的業(yè)務(wù)流程構(gòu)建場(chǎng)景,我們?cè)赑ICASO框架的能力編排DSL中定義了順序、條件和循環(huán)三套能力編排邏輯,分別對(duì)應(yīng)順序執(zhí)行、if...else判斷、循環(huán)三種流程控制方式。其中每一類能力編排節(jié)點(diǎn)的配置都遵循約定大于配置的原則,按照標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎的默認(rèn)執(zhí)行邏輯提供了全部缺省配置,同時(shí)開發(fā)者也可以通過(guò)能力編排API定制自定義的執(zhí)行邏輯,如循環(huán)規(guī)則、分支判斷條件、觸發(fā)能力時(shí)的參數(shù)及上下文傳遞邏輯、失敗及異常處理邏輯、能力節(jié)點(diǎn)解析步驟等。由于本文旨在介紹PICASO框架的設(shè)計(jì)思想和各模塊的底層運(yùn)行機(jī)制,因此我們不會(huì)對(duì)能力編排框架所有DSL API進(jìn)行詳細(xì)論述,這部分內(nèi)容將在《PICASO框架最佳實(shí)踐——能力編排》一文進(jìn)行詳細(xì)論述。本文僅通過(guò)一個(gè)實(shí)際的能力執(zhí)行圖構(gòu)建案例讓大家對(duì)能力編排DSL有一個(gè)具象的感知。

下圖給出的是一個(gè)站外字節(jié)廣告計(jì)劃創(chuàng)建請(qǐng)求處理流程對(duì)應(yīng)的領(lǐng)域能力執(zhí)行圖構(gòu)建邏輯,可以看出能力編排DSL僅用數(shù)十行代碼以一種近乎白話文形式描述出了完整的計(jì)劃構(gòu)建過(guò)程:首先對(duì)用戶已創(chuàng)建計(jì)劃數(shù)量進(jìn)行上限檢查(CampaignUpperLimitCheckAbility);上限校驗(yàn)通過(guò)后會(huì)構(gòu)造出一個(gè)空的計(jì)劃對(duì)象并為其填充用戶ID、計(jì)劃類型等基礎(chǔ)信息(CampaignBaseInfoAssembleAbility);然后判斷本次計(jì)劃創(chuàng)建請(qǐng)求是否在參數(shù)中設(shè)置了聯(lián)合活動(dòng)ID,如果聯(lián)合活動(dòng)ID不為空則需要執(zhí)行聯(lián)合活動(dòng)信息設(shè)置相關(guān)的業(yè)務(wù)邏輯(CampaignJointActivityAbility);完成聯(lián)合活動(dòng)信息設(shè)置之后就要依次設(shè)置計(jì)劃的投放周期(CampaignScheduleConfigAbility)、計(jì)劃名稱(CampaignNameConfigAbility)、營(yíng)銷目標(biāo)(CampaignMarketTypeConfigAbility)、應(yīng)用集(TrafficStrategyConfigAbiliy)等模塊的相關(guān)屬性,需要注意的是在上述幾個(gè)能力的編排邏輯中,由于領(lǐng)域能力的參數(shù)或上下文對(duì)象中的屬性名稱與領(lǐng)域服務(wù)的參數(shù)及上下文中相應(yīng)屬性并不匹配,默認(rèn)的參數(shù)及上下文數(shù)據(jù)傳遞機(jī)制將無(wú)法為這幾個(gè)屬性設(shè)值,因此開發(fā)者對(duì)營(yíng)銷目標(biāo)設(shè)置能力的參數(shù)傳遞(cmdTransfer)、上下文傳遞(ctxTransfer)、應(yīng)用集設(shè)置能力的上下文傳遞(ctxTransfer)邏輯進(jìn)行了自定義拓展,手動(dòng)實(shí)現(xiàn)了參數(shù)及上下文中特殊屬性的數(shù)據(jù)映射邏輯;在完成這些業(yè)務(wù)模塊的屬性設(shè)置之后,如果請(qǐng)求參數(shù)中設(shè)置的標(biāo)的物類型為“商品庫(kù)”,那么接下來(lái)就要執(zhí)行與站外DPA廣告業(yè)務(wù)相關(guān)的特殊業(yè)務(wù)環(huán)節(jié):推廣SKU的校驗(yàn)(SkuValidateAbility)以及SKU跟單信息的配置(TraceOrderSkuConfigAbility)邏輯......

wKgaoWc7BJKAYZniAAeB1kuLdEQ286.png

順序及條件能力編排DSL示例

上圖計(jì)劃新建流程的能力編排邏輯中只用到了順序和條件編排這兩種最常用的編排模式,但是在一些批量操作請(qǐng)求處理流程中通常還會(huì)用到循環(huán)編排模式,它允許標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎重復(fù)調(diào)用同一個(gè)能力門面實(shí)現(xiàn)批量請(qǐng)求的處理。下圖通過(guò)批量創(chuàng)意綁定接口給出了循環(huán)能力編排模式的使用示例,在該流程的最后一個(gè)環(huán)節(jié)通過(guò)registerFlatMapped能力編排API注冊(cè)了一個(gè)創(chuàng)意綁定能力(CreativeBindAbility)。該能力被設(shè)計(jì)為處理單個(gè)創(chuàng)意的綁定操作,而批量創(chuàng)意綁定領(lǐng)域服務(wù)卻需要在一次請(qǐng)求中完成多個(gè)創(chuàng)意的綁定操作,因此我們通過(guò)cmdFlatMap能力編排API定義了將領(lǐng)域服務(wù)參數(shù)中的待綁定創(chuàng)意列表展開成多個(gè)單創(chuàng)意綁定能力參數(shù)的規(guī)則,標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎將據(jù)此遍歷每一個(gè)單創(chuàng)意綁定參數(shù)并調(diào)用單創(chuàng)意綁定能力進(jìn)而實(shí)現(xiàn)批量創(chuàng)意綁定。從示例中我們還可以看到,開發(fā)者在編排創(chuàng)意綁定能力時(shí)還通過(guò)transferCtxBeforeStep能力編排API指定在調(diào)用領(lǐng)域能力的上下文初始化和業(yè)務(wù)邏輯處理兩個(gè)標(biāo)準(zhǔn)步驟前都執(zhí)行一次領(lǐng)域服務(wù)到領(lǐng)域能力的上下文傳遞操作,而標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎的默認(rèn)行為是僅在領(lǐng)域能力上下文初始化標(biāo)準(zhǔn)步驟調(diào)用前執(zhí)行該操作。除此之外,開發(fā)者還通過(guò)onFailure/onException能力編排API定制了失敗及異常處理措施,確保在循環(huán)過(guò)程中單個(gè)創(chuàng)意綁定失敗或異常不會(huì)中斷整個(gè)業(yè)務(wù)處理流程,而是在處理完所有待綁定創(chuàng)意之后,將綁定失敗的創(chuàng)意信息及失敗原因返回給調(diào)用方。

wKgZoWc7BJWAGJz0ABWPCC6Apns874.png

循環(huán)能力編排DSL示例

有些讀者可能會(huì)疑惑為什么不把創(chuàng)意綁定能力設(shè)計(jì)為直接在能力粒度上就支持批量綁定邏輯,這是因?yàn)楫?dāng)前的設(shè)計(jì)是綜合考慮能力劃分原則、創(chuàng)意綁定業(yè)務(wù)實(shí)際與拆分規(guī)則收益等因素之后決定的。我們?cè)谇拔闹刑岬剑I(lǐng)域能力封裝的是最小原子業(yè)務(wù)模塊,而批量處理實(shí)際上屬于流程控制邏輯,因此從職責(zé)劃分的角度考慮,領(lǐng)域能力沉淀單個(gè)創(chuàng)意綁定的具體業(yè)務(wù)規(guī)則、領(lǐng)域服務(wù)負(fù)責(zé)循環(huán)流程控制的設(shè)計(jì)更符合PICASO框架的底層設(shè)計(jì)邏輯;另外從業(yè)務(wù)實(shí)際來(lái)分析,由于不同創(chuàng)意類型對(duì)應(yīng)的創(chuàng)意綁定邏輯也有差異,因此創(chuàng)意綁定能力會(huì)按照創(chuàng)意類型拆分為不同的能力實(shí)例,而且廣告主有可能通過(guò)批量綁定接口一次性綁定類型不同的多個(gè)創(chuàng)意。在這樣的業(yè)務(wù)背景下,單創(chuàng)意綁定的能力拆分邏輯可以讓領(lǐng)域服務(wù)直接遍歷每一個(gè)待綁定創(chuàng)意,然后通過(guò)能力門面將單個(gè)創(chuàng)意綁定請(qǐng)求路由到與待綁定創(chuàng)意類型相適配的能力實(shí)例上進(jìn)行處理,而不需要執(zhí)行按創(chuàng)意類型分組等預(yù)處理邏輯;最后從拆分收益上看,單個(gè)創(chuàng)意綁定的拆分邏輯能夠給我們提供更多的編排靈活性,通過(guò)定制化的失敗及異常處理邏輯,不同的領(lǐng)域服務(wù)可以靈活地支持快速失敗或允許部分失敗等異常處理規(guī)則。綜上考慮我們最終采用了單創(chuàng)意綁定的能力拆分規(guī)則,而這個(gè)決策過(guò)程其實(shí)也是我們進(jìn)行能力拆分的一般流程。從這個(gè)例子中我們可以發(fā)現(xiàn),能力拆分其實(shí)是一個(gè)主觀性很強(qiáng)的行為,盡管我們?yōu)槠渲贫艘幌盗械闹笇?dǎo)準(zhǔn)則,但是在實(shí)際需求中開發(fā)者依然需要充分發(fā)揮主觀能動(dòng)性,在充分理解新架構(gòu)設(shè)計(jì)思想的前提下,緊貼業(yè)務(wù)實(shí)際,在系統(tǒng)性能、架構(gòu)整潔程度以及實(shí)現(xiàn)成本之間找到最佳的平衡點(diǎn)。

截止到目前,PICASO領(lǐng)域能力編排框架已經(jīng)經(jīng)歷了兩輪功能迭代,前文所述的順序、條件及循環(huán)編排是第一代能力編排框架提供的特性,這一代能力編排框架僅支持領(lǐng)域能力之間編排串聯(lián),而領(lǐng)域服務(wù)各個(gè)標(biāo)準(zhǔn)步驟內(nèi)的業(yè)務(wù)邏輯和能力執(zhí)行圖之間的串聯(lián)依然需要手動(dòng)硬編碼實(shí)現(xiàn)。下圖給出了一個(gè)使用第一代能力編排框架的領(lǐng)域服務(wù)案例,這個(gè)案例也清晰地呈現(xiàn)了領(lǐng)域能力執(zhí)行圖與領(lǐng)域服務(wù)各個(gè)標(biāo)準(zhǔn)步驟之間的關(guān)系??梢钥吹筋I(lǐng)域服務(wù)執(zhí)行器提供了一個(gè)標(biāo)準(zhǔn)的領(lǐng)域能力編排入口registerDomainAbilities,該模板方法通過(guò)參數(shù)提供了一個(gè)領(lǐng)域能力編排器(DomainAbilityOrchestrator),開發(fā)者可以通過(guò)該模板方法完成領(lǐng)域能力執(zhí)行圖的構(gòu)建。然而當(dāng)請(qǐng)求到來(lái)時(shí),標(biāo)準(zhǔn)業(yè)務(wù)模版執(zhí)行引擎并不會(huì)直接觸發(fā)領(lǐng)域能力執(zhí)行圖的調(diào)用,它僅會(huì)按照標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行流程依次調(diào)用領(lǐng)域服務(wù)執(zhí)行器的各個(gè)標(biāo)準(zhǔn)步驟,而領(lǐng)域能力執(zhí)行圖的調(diào)用則必須由開發(fā)者硬編碼到領(lǐng)域服務(wù)執(zhí)行器的各個(gè)標(biāo)準(zhǔn)步驟中。當(dāng)然框架已經(jīng)將領(lǐng)域能力執(zhí)行圖的觸發(fā)邏輯封裝成了相對(duì)易用的API(詳見下面截圖中doPreValidate方法中被標(biāo)注的代碼片段),因此這種設(shè)計(jì)的實(shí)現(xiàn)成本不算太高。但是隨著系統(tǒng)中領(lǐng)域服務(wù)執(zhí)行器數(shù)量的增長(zhǎng),它也的確在系統(tǒng)中引入了大量重復(fù)的膠水代碼,并且造成了領(lǐng)域服務(wù)中業(yè)務(wù)邏輯與領(lǐng)域能力值執(zhí)行圖之間的割裂,無(wú)法提供徹底的連貫開發(fā)體驗(yàn)。

wKgaoWc7BJiAVkOiAAcXGoy291o573.png

第一代能力編排框架依然存在一些問(wèn)題

近年來(lái),為了給廣告主提供簡(jiǎn)潔易用的投放體驗(yàn),系統(tǒng)正越來(lái)越多地向著智能化和集成化的方向發(fā)展,讓廣告主少操作、少輸入成為UI交互設(shè)計(jì)重要原則。在這樣的業(yè)務(wù)背景下,我們收到了越來(lái)越多的跨層級(jí)接口合并需求,這讓我們的業(yè)務(wù)在模塊化的基礎(chǔ)上又呈現(xiàn)出集成化特征,而且很多情況下,這種集成是“跨層級(jí)”的。如過(guò)去我們的廣告物料一直遵循經(jīng)典的計(jì)劃、單元、創(chuàng)意三層結(jié)構(gòu),計(jì)劃下可創(chuàng)建多個(gè)單元、單元下可綁定多個(gè)創(chuàng),創(chuàng)建物料時(shí)需要依次調(diào)用三個(gè)接口。然而在近期全站推廣、一頁(yè)投放等需求都有一鍵創(chuàng)建全層級(jí)物料的訴求。但是由于子層級(jí)物料的創(chuàng)編流程都依賴父層級(jí)的物料對(duì)象創(chuàng)建完成,這與第一代能力編排框架中的重組執(zhí)行機(jī)制底層邏輯相悖;另外,由于父層級(jí)物料對(duì)象與子層級(jí)物料對(duì)象之間都是一對(duì)多的關(guān)系,在一鍵創(chuàng)建全套物料的場(chǎng)景中,我們需要循環(huán)調(diào)用子層級(jí)物料的構(gòu)建流程,然而第一代能力編排框架中循環(huán)能力編排特性僅支持循環(huán)調(diào)用單個(gè)領(lǐng)域能力,而不支持對(duì)多個(gè)領(lǐng)域能力執(zhí)行鏈的重復(fù)觸發(fā)。

為了更好地適配業(yè)務(wù)發(fā)展趨勢(shì),同時(shí)解決上文提到的第一代能力編排框架中由于領(lǐng)域服務(wù)與領(lǐng)域能力執(zhí)行圖之間邏輯分離定義、需要手動(dòng)硬編碼完成串聯(lián)造成的業(yè)務(wù)邏輯割裂、會(huì)引入大量重復(fù)的膠水代碼等問(wèn)題,我們?cè)诘谝淮芰幣趴蚣艿幕A(chǔ)上進(jìn)行了升級(jí),引入了子流程編排機(jī)制。子流程編排最重要的升級(jí)是增加了分階段集成的特性,這也與當(dāng)下多接口集成的業(yè)務(wù)發(fā)展趨勢(shì)相符,一個(gè)完整的請(qǐng)求處理流程可以被拆分為不同的執(zhí)行階段,后置階段的執(zhí)行邏輯可能依賴于前置階段的執(zhí)行結(jié)果。PICASO框架允許開發(fā)者通過(guò)子流程編排DSL將一個(gè)完整的業(yè)務(wù)流程拆分定義為多個(gè)業(yè)務(wù)處理階段,其中每一個(gè)業(yè)務(wù)處理階段被稱為一個(gè)子流程,每一個(gè)子流程可以看做是一個(gè)小的領(lǐng)域服務(wù),有各自專屬的標(biāo)準(zhǔn)處理步驟及領(lǐng)域能力執(zhí)行圖。除了可以像第一代能力編排框架那樣為每一個(gè)子流程定義專屬的領(lǐng)域能力執(zhí)行圖之外,第二代子流程編排框架還讓開發(fā)者不需要再去重寫領(lǐng)域服務(wù)執(zhí)行器標(biāo)準(zhǔn)模板中的各個(gè)標(biāo)準(zhǔn)步驟,而是通過(guò)子流程編排DSL將每個(gè)子流程專屬的參數(shù)預(yù)校驗(yàn)、上下文初始化、上下文校驗(yàn)及業(yè)務(wù)邏輯處理四個(gè)標(biāo)準(zhǔn)步驟與領(lǐng)域能力執(zhí)行圖一起直接定義到子流程執(zhí)行圖中,從而徹底解決了第一代能力編排框架中領(lǐng)域服務(wù)維度的業(yè)務(wù)邏輯與能力執(zhí)行圖開發(fā)割裂的問(wèn)題。也正因?yàn)槿绱宋覀冊(cè)诘诙恿鞒叹幣臘SL中將第一代編排框架中的領(lǐng)域能力編排器(DomainAbilityOrchestrator)升級(jí)成了領(lǐng)域服務(wù)構(gòu)造器(DomainServiceBuilder)。

下圖給出了一個(gè)使用子流程編排DSL構(gòu)建領(lǐng)域服務(wù)執(zhí)行圖的例子,可以看到,與第一代能力編排框架類似,第二代子流程編排DSL為開發(fā)者提供了順序、條件、循環(huán)、捆綁及包裝五種子流程編排節(jié)點(diǎn)。其中循環(huán)子流編排節(jié)點(diǎn)支持子流程維度的循環(huán)調(diào)用,解決了第一代能力編排框架中FlatMap編排節(jié)點(diǎn)僅支持對(duì)單個(gè)能力進(jìn)行循環(huán)調(diào)用的問(wèn)題,因此在一對(duì)多的子層級(jí)物料創(chuàng)編場(chǎng)景中有廣泛的應(yīng)用,如下面例子中的在單元下綁定關(guān)鍵詞列表的場(chǎng)景:一個(gè)單元下可以綁定多個(gè)關(guān)鍵詞,而單個(gè)關(guān)鍵詞的綁定操作需要串聯(lián)執(zhí)行多個(gè)領(lǐng)域能力,此時(shí)我們可以將單個(gè)關(guān)鍵詞的構(gòu)建過(guò)程定義為一個(gè)基礎(chǔ)順序子流程,然后再通過(guò)循環(huán)子流程編排節(jié)點(diǎn)將這個(gè)基礎(chǔ)子流程循環(huán)集成到單元?jiǎng)?chuàng)建領(lǐng)域服務(wù)執(zhí)行圖中,這樣就可以實(shí)現(xiàn)在單元下批量綁定關(guān)鍵詞的功能;而捆綁子流程的作用類似于通用編程語(yǔ)言中的‘{}’,它能夠?qū)⒍鄠€(gè)子流程包裝成一個(gè)邏輯子流程節(jié)點(diǎn),這一特性讓子流程編排能夠支持任意不同子流程之間的組合及嵌套,進(jìn)一步提升了編排框架對(duì)各類業(yè)務(wù)場(chǎng)景的普適性;包裝子流程節(jié)點(diǎn)則是專門為已有領(lǐng)域服務(wù)執(zhí)行器的組合復(fù)用而設(shè)計(jì)的,它可以將一個(gè)現(xiàn)有的領(lǐng)域服務(wù)執(zhí)行器包裝成一個(gè)子流程編排節(jié)點(diǎn)添加到子流程編排執(zhí)行圖中,這一特性在復(fù)用現(xiàn)有接口進(jìn)行集成化改造或?qū)崿F(xiàn)批量操作等需求中會(huì)為開發(fā)者提供極大的便利。

wKgZoWc7BJqAG4zPAAcvgL_PGlg969.png

第二代子流程及能力編排框架示例

通過(guò)上面的幾個(gè)例子我們能感受到,領(lǐng)域能力編排DSL能夠以極高的信息密度描述業(yè)務(wù)流程的關(guān)鍵信息,通過(guò)構(gòu)建領(lǐng)域能力/子流程執(zhí)行圖的方式定義業(yè)務(wù)流程的措施不僅能夠減少膠水代碼、降低開發(fā)負(fù)擔(dān),還可以協(xié)助開發(fā)者快速建立起對(duì)業(yè)務(wù)的全景認(rèn)知,同時(shí),領(lǐng)域能力/子流程執(zhí)行圖也能作為詳細(xì)業(yè)務(wù)規(guī)則的目錄或索引,在進(jìn)行業(yè)務(wù)梳理或問(wèn)題排查時(shí)讓開發(fā)者可以按圖索驥快速定位目標(biāo)代碼的大致位置,提升業(yè)務(wù)知識(shí)傳承及問(wèn)題定位的效率。

盡管我們?cè)赑ICASO框架中舍棄了圖形化流程編排框架的設(shè)計(jì),但是我們并沒(méi)有否定它存在的意義,這種編排方式在低代碼編程領(lǐng)域占有重要的地位。但是它更適合在一些節(jié)點(diǎn)類型有限或者流程相對(duì)穩(wěn)定的業(yè)務(wù)場(chǎng)景下使用,比如審批流程,雖然審批流可能有很多,但是每個(gè)審批流程中的節(jié)點(diǎn)種類往往不多,可以用相對(duì)固定的模式進(jìn)行串聯(lián);或者廣告播放流程,盡管其業(yè)務(wù)流程同樣節(jié)點(diǎn)眾多、冗長(zhǎng)復(fù)雜,但是流程數(shù)量相對(duì)固定,基本上可以分為展示、搜索、推薦、站外、合約這五大核心流程,很多變更是對(duì)流程的微調(diào)或節(jié)點(diǎn)內(nèi)部的邏輯升級(jí)。與這些業(yè)務(wù)相比,廣告投放系統(tǒng)則維護(hù)了近300個(gè)能力節(jié)點(diǎn)、400多條請(qǐng)求處理流程,每次需求迭代都會(huì)涉及數(shù)十條業(yè)務(wù)流程的變更。在這樣的業(yè)務(wù)特點(diǎn)下,簡(jiǎn)潔、靈活、集中、連貫的開發(fā)體驗(yàn)要比一個(gè)炫酷的UI交互界面重要的多。當(dāng)然,我們依然十分認(rèn)可圖形化界面強(qiáng)大的呈現(xiàn)能力,事實(shí)上我們也在規(guī)劃為PICASO框架開發(fā)內(nèi)置的代碼元數(shù)據(jù)管理平臺(tái),其中一項(xiàng)重要的功能就是把代碼中基于能力編排DSL構(gòu)建出來(lái)的領(lǐng)域能力執(zhí)行圖解析為業(yè)務(wù)流程圖回顯到交互界面中,讓開發(fā)者可以直觀地查看所有領(lǐng)域服務(wù)的處理流程。但是該平臺(tái)只做能力編排邏輯的圖形化展示,領(lǐng)域能力執(zhí)行圖的構(gòu)造依然是通過(guò)代碼中的能力編排API實(shí)現(xiàn)的(當(dāng)然該平臺(tái)也可以提供各個(gè)能力編排節(jié)點(diǎn)配置屬性的動(dòng)態(tài)修改功能,但是這僅作為緊急情況下的應(yīng)急處理措施)。

PICASO的愿景:構(gòu)建圖書館式代碼架構(gòu)

至此我們已經(jīng)完成了對(duì)PICASO框架全部核心模塊的介紹,此刻讓我們?cè)俅位厥譖ICASO框架的設(shè)次初衷——竭盡所能地提升團(tuán)隊(duì)的研發(fā)效率,這也是一線業(yè)務(wù)開發(fā)團(tuán)隊(duì)的核心價(jià)值所在。

提到研發(fā)效率,有很多同學(xué)認(rèn)為少寫代碼就是研發(fā)效率高,也有同學(xué)認(rèn)為支持了配置化、使用了拓展點(diǎn)就能實(shí)現(xiàn)研發(fā)效率的提升。然而事實(shí)上代碼行數(shù)從來(lái)就是不是制約研發(fā)效率提升的核心要素,對(duì)業(yè)務(wù)問(wèn)題的抽象和封裝有時(shí)的確會(huì)導(dǎo)致我們多寫一些代碼,但是與多寫幾行代碼花費(fèi)的幾十分鐘相比,在實(shí)際工作中我們會(huì)把更多的時(shí)間和精力消耗在確定分工時(shí)與合作團(tuán)隊(duì)的扯皮上、需求設(shè)計(jì)時(shí)各團(tuán)隊(duì)方案對(duì)齊及歷史業(yè)務(wù)邏輯的確認(rèn)上、出現(xiàn)問(wèn)題時(shí)原因的定位與影響范圍的評(píng)估上、由于自己或者上下游系統(tǒng)的技術(shù)或架構(gòu)限制而不得不進(jìn)行的妥協(xié)方案開發(fā)上......解決這些問(wèn)題所花費(fèi)的時(shí)間才是阻礙研發(fā)效率提升和耗盡業(yè)務(wù)方對(duì)研發(fā)團(tuán)隊(duì)信任的根源。至于配置化和拓展點(diǎn),不可否認(rèn)的是它們的確是包括PICASO框架在內(nèi)的很多效率提升解決方案中經(jīng)常采用的措施,但是如果上述問(wèn)題得不到解決,這些措施也只能是治標(biāo)不治本。正如“鮑勃大叔”Robert C.Martin在其著作《架構(gòu)整潔之道》中所說(shuō):“不管你多敬業(yè)、加多少班,在面對(duì)爛系統(tǒng)時(shí)你仍然會(huì)寸步難行,因?yàn)槟愦蟛糠值木κ窃趹?yīng)對(duì)混亂?!倍覀冎砸獮榉止こ镀ぁ⒅圆桓疑?jí)系統(tǒng)以適配新的業(yè)態(tài)、之所以無(wú)法快速梳理出業(yè)務(wù)規(guī)則,都是因?yàn)檫^(guò)去大泥團(tuán)式的代碼架構(gòu)讓我們看不懂、不敢動(dòng)、動(dòng)不了。

為了解決上述問(wèn)題,PICASO框架在設(shè)計(jì)之初就確立了框架即規(guī)范、代碼即文檔的基本原則,從設(shè)計(jì)階段就開始引導(dǎo)開發(fā)者以統(tǒng)一的思想和方法論對(duì)業(yè)務(wù)進(jìn)行拆解分析,然后將業(yè)務(wù)規(guī)則淀到標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模版或拓展點(diǎn)接口中,確保每一處業(yè)務(wù)規(guī)則都有可檢索、可引用的實(shí)體邊界;接著通過(guò)能力編排框架以近乎白話文的方式將這些原子業(yè)務(wù)實(shí)體組裝起來(lái),以極高的信息密度清晰地描述完整的業(yè)務(wù)流程;最后通過(guò)通用可執(zhí)行實(shí)體發(fā)現(xiàn)及路由機(jī)制對(duì)各層實(shí)體進(jìn)行分類及分組,對(duì)上層實(shí)體暴露統(tǒng)一的調(diào)用門面,除了起到逐層向上屏蔽分組內(nèi)部場(chǎng)景復(fù)雜度的作用之外,PICASO框架維護(hù)的可執(zhí)行實(shí)體路由表也可以作為業(yè)務(wù)細(xì)節(jié)邏輯的速查索引。有了這些措施,開發(fā)者進(jìn)行業(yè)務(wù)邏輯梳理時(shí)就可以先通過(guò)領(lǐng)域服務(wù)維度的路由表定位到目標(biāo)場(chǎng)景對(duì)應(yīng)的領(lǐng)域服務(wù)實(shí)例,然后通過(guò)其領(lǐng)域能力執(zhí)行圖快速建立起對(duì)務(wù)流程的全景認(rèn)知,接著選擇執(zhí)行圖中感興趣的業(yè)務(wù)節(jié)點(diǎn),通過(guò)領(lǐng)域能力維度的路由表快速定位到具體的能力實(shí)例,最后到相應(yīng)的的標(biāo)準(zhǔn)步驟中定位具體的實(shí)現(xiàn)細(xì)節(jié)。這種由粗及細(xì)、逐層按索引查找的過(guò)程類似于圖書館的管理模式:借閱圖書時(shí),我們需要大體推斷目標(biāo)書籍所屬的類目,然后通過(guò)類目確定書籍所在的書架,在書架上找到目標(biāo)書籍后再通過(guò)其目錄快速概覽全書,最后通過(guò)目錄定位到感興趣的內(nèi)容,這就是圖書館式代碼架構(gòu)的由來(lái)。我們希望這種代碼架構(gòu)能夠讓業(yè)務(wù)知識(shí)通過(guò)系統(tǒng)代碼清晰、準(zhǔn)確、完整地表達(dá)出來(lái)并能流暢地傳承下去,進(jìn)而讓團(tuán)隊(duì)在面對(duì)業(yè)態(tài)更迭時(shí)能夠更加從容地承接業(yè)務(wù)方提出的各項(xiàng)訴求;在進(jìn)行多團(tuán)隊(duì)協(xié)作時(shí)能夠擁有足夠的底氣承擔(dān)更多的責(zé)任;在遇到線上問(wèn)題時(shí)能夠更加快速地定位問(wèn)題并制定解決方案;在接手新系統(tǒng)時(shí)也能夠快速梳理出業(yè)務(wù)主線并上手開發(fā),最終實(shí)現(xiàn)團(tuán)隊(duì)整體研發(fā)效率的提升。

(二)聚合與資源庫(kù):拒絕魔法邏輯,讓代碼直接表達(dá)業(yè)務(wù)規(guī)則

如果說(shuō)前文介紹的PICASO框架讓新架構(gòu)擁有了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之形,那么接下來(lái)將要介紹的聚合與資源庫(kù)機(jī)制將讓新架構(gòu)真正具備領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的靈魂。

長(zhǎng)期以來(lái),我們的開發(fā)行為中業(yè)務(wù)設(shè)計(jì)與代碼設(shè)計(jì)是分離的。接到需求后研發(fā)人員會(huì)和產(chǎn)品及業(yè)務(wù)方進(jìn)行大量的溝通,確認(rèn)業(yè)務(wù)流程及各項(xiàng)細(xì)節(jié)規(guī)則,這是業(yè)務(wù)設(shè)計(jì)的過(guò)程。但是在編碼階段,開發(fā)人員又會(huì)對(duì)業(yè)務(wù)設(shè)計(jì)結(jié)果重新進(jìn)行抽象,轉(zhuǎn)化為代碼實(shí)現(xiàn)方案。而此時(shí)傳統(tǒng)的三層架構(gòu)過(guò)于簡(jiǎn)單的層次劃分很容易將開發(fā)者的大部分注意力引導(dǎo)到數(shù)據(jù)庫(kù)設(shè)計(jì)上,代碼設(shè)計(jì)就變成了對(duì)數(shù)據(jù)庫(kù)增刪改查操作的設(shè)計(jì)。在這個(gè)過(guò)程中前期業(yè)務(wù)設(shè)計(jì)沉淀的大量領(lǐng)域知識(shí)往往會(huì)被丟棄,實(shí)現(xiàn)出來(lái)的代碼也失去了對(duì)業(yè)務(wù)規(guī)則的直接表達(dá)。而缺乏基礎(chǔ)模型設(shè)計(jì)的軟件充其量也只是一種機(jī)械化的產(chǎn)品,雖能實(shí)現(xiàn)功能卻無(wú)法解釋這樣操作的原因。更嚴(yán)重的是,如果底層數(shù)據(jù)庫(kù)存在表或字段的復(fù)用,那么業(yè)務(wù)規(guī)則被直接翻譯成庫(kù)表增刪改查操作邏輯之后,代碼甚至?xí)磉_(dá)出與業(yè)務(wù)規(guī)則完全不相符的含義出來(lái)。這些代碼就成了只有開發(fā)者自己才能看懂的魔法邏輯,甚至經(jīng)過(guò)一段時(shí)間之后開發(fā)者本人也會(huì)忘記這些代碼背后真正的業(yè)務(wù)含義......

如果整個(gè)程序設(shè)計(jì)或者其核心部分沒(méi)有與領(lǐng)域模型相對(duì)應(yīng),那么該模型就是沒(méi)有價(jià)值的,軟件的正確性也值得懷疑。(Eric Evans, Domain-Driven Design: Trackling Complexity in the Heart of Software, 2003)

面向數(shù)據(jù)庫(kù)編程的設(shè)計(jì)思維還容易引導(dǎo)我們最終以事務(wù)腳本(Transaction Script)的形式實(shí)現(xiàn)業(yè)務(wù)流程的處理,將業(yè)務(wù)規(guī)則與數(shù)據(jù)庫(kù)表操作邏輯糅合到一起,業(yè)務(wù)實(shí)體之間的關(guān)系也因此分散和隱藏到了整個(gè)工程中不同的接口實(shí)現(xiàn)里,這會(huì)給數(shù)據(jù)模型全景認(rèn)知的建立帶來(lái)極大的障礙。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的祖師爺及布道者Eric Evans曾提到自己項(xiàng)目組的成員曾花費(fèi)數(shù)月時(shí)間才梳理出一個(gè)完整的數(shù)據(jù)模型,而在我們自己的記憶里,似乎也沒(méi)有哪位同學(xué)有底氣敢宣稱自己掌握了完整的數(shù)據(jù)模型(我們可能清楚數(shù)據(jù)庫(kù)中有哪些庫(kù)表,但是由于底層庫(kù)表存在不同業(yè)務(wù)場(chǎng)景復(fù)用的情況,導(dǎo)致表中數(shù)據(jù)對(duì)應(yīng)的業(yè)務(wù)含義并不統(tǒng)一。這種復(fù)用表結(jié)構(gòu)的設(shè)計(jì)無(wú)可厚非,在很多情況下我們甚至都鼓勵(lì)這種縱向拓展方式,但這也的確是造成我們對(duì)數(shù)據(jù)模型認(rèn)識(shí)模糊和不完整的主要原因)。數(shù)據(jù)模型全景認(rèn)知的缺失讓開發(fā)者很難進(jìn)行統(tǒng)一的模型頂層設(shè)計(jì),在多需求并行推進(jìn)的開發(fā)模式下很容易在不同項(xiàng)目組之間形成信息孤島,無(wú)法實(shí)現(xiàn)模型復(fù)用與合并,導(dǎo)致數(shù)據(jù)模型野蠻膨脹,這反過(guò)來(lái)又進(jìn)一步加劇了數(shù)據(jù)模型全景認(rèn)知的構(gòu)建難度,從而陷入到惡性循環(huán)中無(wú)法自拔。除此之外,業(yè)務(wù)流程中不同的業(yè)務(wù)環(huán)節(jié)可能會(huì)依賴相同的底層數(shù)據(jù),事務(wù)腳本式的業(yè)務(wù)處理邏輯會(huì)將這些底層數(shù)據(jù)的讀寫操作分散到不同的接口或業(yè)務(wù)模塊的實(shí)現(xiàn)中,除了會(huì)造成重復(fù)編碼之外,還有可能在運(yùn)行時(shí)造成重復(fù)和碎片化的數(shù)據(jù)讀寫操作,進(jìn)而影響系統(tǒng)性能。

聚合與資源庫(kù)機(jī)制就是專門為解決上述問(wèn)題而生的。

聚合與資源庫(kù)機(jī)制實(shí)現(xiàn)數(shù)據(jù)模型與業(yè)務(wù)邏輯分離

聚合(Aggregate)是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想中重要概念,它是一組相關(guān)對(duì)象的集合,這些對(duì)象之間關(guān)聯(lián)密切,彼此之間已經(jīng)按照對(duì)象之間的關(guān)系以父子屬性的方式組裝好。每個(gè)聚合都有明確的邊界(boundary)和一個(gè)聚合根(root),其中邊界定義了這個(gè)聚合中都有哪些對(duì)象,而聚合根則是這些對(duì)象中的一個(gè)特定實(shí)體。聚合根是聚合內(nèi)唯一個(gè)允許被外部對(duì)象引用的實(shí)體,也是聚合中的所有實(shí)體的最頂層父級(jí)對(duì)象,因此通過(guò)聚合根可以訪問(wèn)到聚合內(nèi)所有對(duì)象,這會(huì)給上層業(yè)務(wù)規(guī)則的實(shí)現(xiàn)帶來(lái)了極大的便利。但是實(shí)際業(yè)務(wù)中聚合內(nèi)的實(shí)體關(guān)系通常都十分復(fù)雜,常常存在多級(jí)嵌套關(guān)系。比如廣告投放業(yè)務(wù)中計(jì)劃聚合內(nèi)就存在著“計(jì)劃->單元->創(chuàng)意->子創(chuàng)意->子創(chuàng)意審核記錄”這種多達(dá)5層的嵌套實(shí)體結(jié)構(gòu)。如果每次使用聚合時(shí)都需要開發(fā)者手動(dòng)實(shí)現(xiàn)聚合內(nèi)實(shí)體的組裝邏輯顯然會(huì)帶來(lái)大量的重復(fù)代碼及編碼負(fù)擔(dān),對(duì)系統(tǒng)未來(lái)的維護(hù)來(lái)說(shuō)也將是一場(chǎng)災(zāi)難,而資源庫(kù)就是為了解決這個(gè)問(wèn)題而設(shè)計(jì)的。

資源庫(kù)(Repository)是系統(tǒng)中所有聚合根對(duì)象的邏輯集合,當(dāng)然這并不意味著資源庫(kù)對(duì)象會(huì)直接加載和維護(hù)系統(tǒng)中所有的聚合根實(shí)例,它只是邏輯上的集合。事實(shí)上在實(shí)現(xiàn)層面業(yè)務(wù)數(shù)據(jù)始終保存在數(shù)據(jù)庫(kù)等存儲(chǔ)介質(zhì)中,資源庫(kù)只是定義了針對(duì)聚合根或聚合根集合的增刪改查操接口,并且維護(hù)了底層存儲(chǔ)介質(zhì)中的數(shù)據(jù)記錄與聚合實(shí)體對(duì)象之間的映射關(guān)系以及聚合中各個(gè)實(shí)體之間的關(guān)聯(lián)組合邏輯。因此開發(fā)者可以通過(guò)資源庫(kù)定義的標(biāo)準(zhǔn)接口一鍵獲取到一個(gè)組裝好的聚合根對(duì)象,就好像是從一個(gè)集合中取出一個(gè)元素那樣容易。如下圖所示,與傳統(tǒng)架構(gòu)業(yè)務(wù)層代碼在不同業(yè)務(wù)環(huán)節(jié)中直接訪問(wèn)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)方式相比,新架構(gòu)在上層業(yè)務(wù)(領(lǐng)域服務(wù))與底層數(shù)據(jù)庫(kù)訪問(wèn)層中間插入了一層資源庫(kù),上層業(yè)務(wù)需要獲取聚合數(shù)據(jù)時(shí),不需要自己實(shí)現(xiàn)聚合中各個(gè)業(yè)務(wù)實(shí)體的查詢、映射和組裝邏輯,而是通過(guò)資源庫(kù)提供的標(biāo)準(zhǔn)接口直接獲取已經(jīng)組裝好的聚合根對(duì)象。這種設(shè)計(jì)將聚合內(nèi)各個(gè)實(shí)體的查詢、映射及組裝邏輯收口屏蔽在資源庫(kù)內(nèi)部,讓上層業(yè)務(wù)聚焦在業(yè)務(wù)規(guī)則上,從而實(shí)現(xiàn)了數(shù)據(jù)模型與業(yè)務(wù)邏輯的分離。這樣不僅能夠避免在業(yè)務(wù)邏輯中頻繁穿插繁瑣的數(shù)據(jù)查詢和組裝邏輯,防止在運(yùn)行時(shí)出現(xiàn)重復(fù)及碎片化的數(shù)據(jù)讀寫操作,資源庫(kù)集中維護(hù)的各個(gè)聚合實(shí)體的查詢、映射和關(guān)聯(lián)邏輯也是一份重要的業(yè)務(wù)知識(shí),能夠輔助開發(fā)者快速建立對(duì)完整數(shù)據(jù)模型的全景認(rèn)知。

wKgZomc7BJuAYRM2AAQV10X_Rt8558.png

聚合與資源庫(kù)的引入實(shí)現(xiàn)了數(shù)據(jù)模型與上層業(yè)務(wù)模型的分離

六邊形架構(gòu)讓代碼從業(yè)務(wù)中來(lái)到業(yè)務(wù)中去

在上一個(gè)小節(jié)我們介紹了聚合和資源庫(kù)的定義及實(shí)現(xiàn)準(zhǔn)則,這其實(shí)有些本末倒置,因?yàn)榫酆虾唾Y源庫(kù)機(jī)制的關(guān)鍵不是如何實(shí)現(xiàn)聚合實(shí)體的查詢或者持久化,而是如何設(shè)計(jì)一套有價(jià)值的聚合(當(dāng)然,先了解聚合的實(shí)現(xiàn)準(zhǔn)則對(duì)理解聚合的設(shè)計(jì)準(zhǔn)則是有幫助的),而聚合的設(shè)計(jì)原則正是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想核心理念的直接體現(xiàn)。

網(wǎng)絡(luò)上很多介紹DDD思想的文章都是從統(tǒng)一語(yǔ)言、領(lǐng)域、界限上下文等術(shù)語(yǔ)和概念開始的,但是由于中英文語(yǔ)境的差異和國(guó)內(nèi)外軟件開發(fā)生態(tài)的不同,這些文章很容易讓初學(xué)者陷入到各種概念的泥沼中無(wú)法自拔。然而那些概念只是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的外在表現(xiàn)形式,其背后的思想內(nèi)核才是我們應(yīng)該優(yōu)先掌握的內(nèi)容。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的核心理念就是保持業(yè)務(wù)、領(lǐng)域模型和代碼三者之間的統(tǒng)一,而六邊形架構(gòu)就是為了保障系統(tǒng)能夠達(dá)成這一目標(biāo)而設(shè)計(jì)的,它的核心邏輯在于保護(hù)領(lǐng)域模型。

六邊形架構(gòu)實(shí)際上是在分層架構(gòu)的基礎(chǔ)上升級(jí)而來(lái)。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想作為一種設(shè)計(jì)的指導(dǎo)思想其實(shí)并不會(huì)限制使用某種特定的架構(gòu),在傳統(tǒng)的分層架構(gòu)上也可以實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的落地。下圖中最左側(cè)給出了應(yīng)用了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想之后的分層架構(gòu),它看上去就是將傳統(tǒng)三層架構(gòu)中的業(yè)務(wù)層拆分為應(yīng)用層與領(lǐng)域?qū)印F渲蓄I(lǐng)域?qū)迂?fù)責(zé)維護(hù)領(lǐng)域模型,所謂的領(lǐng)域模型就是由當(dāng)前領(lǐng)域內(nèi)的全部聚合、資源庫(kù)以及運(yùn)行在這些聚合實(shí)體上領(lǐng)域能力和領(lǐng)域服務(wù)構(gòu)成的;應(yīng)用層則從業(yè)務(wù)視角定義了系統(tǒng)應(yīng)該對(duì)外提供多少服務(wù)(也就是所謂用例(use case)和用戶故事(user story),如果你特別執(zhí)著于那些術(shù)語(yǔ)的話),這些服務(wù)接口最終都會(huì)調(diào)用領(lǐng)域?qū)又械念I(lǐng)域服務(wù)執(zhí)行器來(lái)實(shí)現(xiàn)接口功能;應(yīng)用層關(guān)注的是在系統(tǒng)應(yīng)該對(duì)外提供哪些服務(wù),但是并不關(guān)心這些服務(wù)的請(qǐng)求來(lái)源是RPC調(diào)用還是MQ通知,這是用戶接口層的職責(zé),它負(fù)責(zé)根據(jù)與調(diào)用方達(dá)成的約定將應(yīng)用層接口暴露給不同的接口協(xié)議:Http、RPC、MQ、事件通知等等;基礎(chǔ)設(shè)施層則負(fù)責(zé)維護(hù)系統(tǒng)中使用的眾多中間件、工具以及底層存儲(chǔ)介質(zhì)的訪問(wèn)邏輯。

wKgaomc7BJyAEPELAAH8YarQCrE467.png

多層架構(gòu)向六邊形架構(gòu)的演進(jìn)歷程

通過(guò)領(lǐng)域?qū)雍蛻?yīng)用層的抽象,我們讓分層架構(gòu)具備了實(shí)施領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的可能。但是分層架構(gòu)天然會(huì)引導(dǎo)開發(fā)者自上而下地以數(shù)據(jù)流的視角審視系統(tǒng)。而人腦的天性使我們更容易關(guān)注流程的始末,而容易輕視流程的中間環(huán)節(jié)。因此分層架構(gòu)容易讓開發(fā)者更多的關(guān)注底層數(shù)據(jù)的存儲(chǔ)邏輯,進(jìn)而再次陷入面向數(shù)據(jù)庫(kù)編程的思維,設(shè)計(jì)出與實(shí)際業(yè)務(wù)脫節(jié)的領(lǐng)域模型。當(dāng)然,我們自然可以通過(guò)不斷的宣貫和世界觀輸出來(lái)呼吁開發(fā)者緊貼業(yè)務(wù)實(shí)際設(shè)計(jì)代碼實(shí)體,避免在需求伊始就陷入到底層庫(kù)表結(jié)構(gòu)中無(wú)法自拔,但我們還是希望找到一種能夠在架構(gòu)模式上就凸顯領(lǐng)域模型的重要性,引導(dǎo)開發(fā)者從業(yè)務(wù)實(shí)際出發(fā)設(shè)計(jì)領(lǐng)域模型的架構(gòu),這就是六邊形架構(gòu)誕生的背景。在對(duì)六邊形架構(gòu)進(jìn)行詳細(xì)介紹之前,我們先回過(guò)頭來(lái)再次審視分層架構(gòu)中用戶接口層和基礎(chǔ)設(shè)施層的職責(zé)差異,這對(duì)理解六邊形架構(gòu)的本質(zhì)十分重要:用戶接口層將系統(tǒng)服務(wù)暴露成不同的協(xié)議接口,因此其內(nèi)部的代碼主要在執(zhí)行接口參數(shù)轉(zhuǎn)換和應(yīng)用層接口調(diào)用的邏輯;在領(lǐng)域服務(wù)返回處理結(jié)果之后,用戶接口層還需要將領(lǐng)域服務(wù)返回的響應(yīng)結(jié)果包裝成符合對(duì)外接口協(xié)議的響應(yīng)對(duì)象。而基礎(chǔ)設(shè)施層主要維護(hù)的是底層數(shù)據(jù)的訪問(wèn)邏輯,似乎與用戶接口層的職責(zé)千差萬(wàn)別。但是如果我們把數(shù)據(jù)庫(kù)看做是一個(gè)特殊的外部服務(wù),基礎(chǔ)設(shè)施層的代碼執(zhí)行邏輯就與用戶接口層幾乎一致了:基礎(chǔ)設(shè)施層負(fù)責(zé)的就是聚合實(shí)體與底層存儲(chǔ)PO對(duì)象之間的轉(zhuǎn)換及存儲(chǔ)介質(zhì)數(shù)據(jù)傳輸協(xié)議的調(diào)用。我們不難發(fā)現(xiàn)用戶接口層與基礎(chǔ)設(shè)施層都在針對(duì)領(lǐng)域模型做防腐處理,從這個(gè)視角看,用戶接口層與基礎(chǔ)設(shè)施層在架構(gòu)中的地位是相同的。因此,如上圖中間位置的架構(gòu)圖所示,在架構(gòu)模式上我們嘗試將用戶接口層與基礎(chǔ)設(shè)施層“掰”到一個(gè)同一個(gè)層級(jí)中合為一體,于是我們就能得到一個(gè)六邊形的對(duì)稱架構(gòu)(從上圖的呈現(xiàn)方式上看,將用戶接口層與基礎(chǔ)設(shè)施層合并后可能還需要再旋轉(zhuǎn)45°才能呈現(xiàn)出與上圖右側(cè)一致的六邊形架構(gòu))。

六邊形架構(gòu)本質(zhì)上還是一個(gè)分層架構(gòu),只是在呈現(xiàn)方式上(注意不是實(shí)現(xiàn)方式)將用戶接口層與基礎(chǔ)設(shè)施層合二為一,讓他們共同作為防腐層保護(hù)位于架構(gòu)中心的領(lǐng)域模型不被調(diào)用方的請(qǐng)求協(xié)議以及底層數(shù)據(jù)庫(kù)的特殊設(shè)計(jì)所污染。而人腦天然具備的找中心的特性能夠讓開發(fā)者將更多的注意力放到位于架構(gòu)中心的領(lǐng)域模型上,暫時(shí)忘記底層數(shù)據(jù)庫(kù)的存儲(chǔ)規(guī)則,進(jìn)而能夠緊貼業(yè)務(wù)實(shí)際設(shè)計(jì)聚合中的各個(gè)實(shí)體及值對(duì)象,讓代碼直接表達(dá)業(yè)務(wù)規(guī)則,最后通過(guò)資源庫(kù)實(shí)現(xiàn)聚合實(shí)體與底層存儲(chǔ)介質(zhì)PO對(duì)象之間的轉(zhuǎn)化。

下圖給出了一個(gè)廣告投放業(yè)務(wù)中聚合實(shí)體與底層庫(kù)表結(jié)構(gòu)設(shè)計(jì)的例子,投放系統(tǒng)作為一個(gè)業(yè)務(wù)集成平臺(tái)需要不斷地對(duì)接各種垂直業(yè)務(wù)系統(tǒng),在廣告物料中也需要不斷集成各類業(yè)務(wù)實(shí)體,這些數(shù)據(jù)也需要保存到底層的廣告物料數(shù)據(jù)中。在設(shè)計(jì)單元聚合時(shí),我們會(huì)緊貼業(yè)務(wù)實(shí)際為各類外部關(guān)聯(lián)對(duì)象定義含義明確的聚合實(shí)體:商品庫(kù)(ProductCategory)、抖音賬號(hào)信息(AweneAccount)、展示錨點(diǎn)信息(AnchorInfo)、直播信息(LiveInfo)等,從而確保領(lǐng)域模型與實(shí)際業(yè)務(wù)的統(tǒng)一。但是在設(shè)計(jì)底層庫(kù)表結(jié)構(gòu)時(shí)為了避免庫(kù)表結(jié)構(gòu)膨脹以及表字段稀疏化,我們采用了通用化的存儲(chǔ)結(jié)構(gòu):綁定到廣告物料上的不同外部業(yè)務(wù)對(duì)象都保存到同一張外部關(guān)聯(lián)對(duì)象表中,該表中的字段采用泛化設(shè)計(jì),不與任何一種特定的外部業(yè)務(wù)對(duì)象相綁定,而是通過(guò)type字段確定本條記錄對(duì)應(yīng)的外部對(duì)象類型以及表中其他字段的實(shí)際業(yè)務(wù)含義,業(yè)務(wù)層對(duì)這些外部業(yè)務(wù)對(duì)象讀寫操作都要通過(guò)資源庫(kù)集中維護(hù)的底層數(shù)據(jù)記錄與領(lǐng)域?qū)泳酆蠈?shí)體之間的映射邏輯做轉(zhuǎn)換。需要特別說(shuō)明的是,在新架構(gòu)中我們將PICASO框架中的拓展點(diǎn)機(jī)制延伸到了基礎(chǔ)設(shè)施層,引入了模型管理拓展點(diǎn)(Model Manage Extension, MME)來(lái)實(shí)現(xiàn)聚合組裝主流程與底層數(shù)據(jù)對(duì)象轉(zhuǎn)換邏輯的解耦。在本例中,我們將外部關(guān)聯(lián)對(duì)象表對(duì)應(yīng)的PO對(duì)象與領(lǐng)域?qū)泳酆蠈?shí)體之間的映射邏輯抽離成了一個(gè)模型管理拓展點(diǎn),以外部關(guān)聯(lián)對(duì)象類型作為路由鍵,通過(guò)PICASO框架的通用可執(zhí)行實(shí)體發(fā)現(xiàn)與路由機(jī)制實(shí)現(xiàn)具體拓展點(diǎn)實(shí)現(xiàn)的自動(dòng)定位??梢钥闯鲑Y源庫(kù)的存在不僅讓開發(fā)者可以聚焦業(yè)務(wù)實(shí)際設(shè)計(jì)領(lǐng)域模型,讓代碼直接反映業(yè)務(wù)實(shí)際,同時(shí)還讓通用化的底層數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)得到更加廣泛的應(yīng):庫(kù)表結(jié)構(gòu)的通用化設(shè)計(jì)雖然能夠解決數(shù)據(jù)庫(kù)模型膨脹的問(wèn)題,但是也的確降低了數(shù)據(jù)模型對(duì)業(yè)務(wù)的表達(dá)能力,而且如果讓上層業(yè)務(wù)直接操作這些庫(kù)表,勢(shì)必會(huì)造成代碼邏輯不明、編碼繁瑣的問(wèn)題。然而資源庫(kù)卻通過(guò)收口底層數(shù)據(jù)對(duì)象與上層業(yè)務(wù)實(shí)體之間的轉(zhuǎn)化邏輯很好地解決了這些問(wèn)題,讓我們?cè)诓杉{這種通用化的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)設(shè)計(jì)時(shí)少了很多顧慮。

wKgZomc7BKCACZ7nAAJtThWOwI0104.png

底層通用化存儲(chǔ)結(jié)構(gòu)(K-V模式)在資源庫(kù)中通過(guò)模型管理拓展點(diǎn)被映射為領(lǐng)域模型中業(yè)務(wù)含義明確的實(shí)體對(duì)象

在框架實(shí)現(xiàn)層面六邊形架構(gòu)與分層架構(gòu)其實(shí)并沒(méi)有太多的差異,頂多就是在六邊形中領(lǐng)域模型僅負(fù)責(zé)定義資源庫(kù)接口,而將資源庫(kù)的實(shí)現(xiàn)放到了基礎(chǔ)設(shè)施層中。在使用了Spring等控制反轉(zhuǎn)容器的項(xiàng)目中,一些宣稱采用了分層架構(gòu)的系統(tǒng)可能已經(jīng)在無(wú)意間實(shí)現(xiàn)了六邊形架構(gòu)了。那么六邊形架構(gòu)的意義又是什么呢?我們?cè)谇拔闹性昧薎EEE對(duì)“架構(gòu)”的定義:組織、組件以及指導(dǎo)思想,然而很多時(shí)候我們都忽略了指導(dǎo)思想對(duì)軟件開發(fā)行為的重要影響。一個(gè)好的架構(gòu)一定是包涵人性的,架構(gòu)思想直接決定了開發(fā)者分析業(yè)務(wù)的世界觀和方法論??蚣苤皇禽o助工具,基于框架思想對(duì)業(yè)務(wù)進(jìn)行抽象和設(shè)計(jì)而產(chǎn)出的代碼才是一個(gè)軟件系統(tǒng)的主要構(gòu)成部分。即使采用相同的框架,在不同架構(gòu)思想的引導(dǎo)之下,系統(tǒng)中的業(yè)務(wù)代碼也可能會(huì)走向全然不同的迭代路線。而六邊形架構(gòu)與分層架構(gòu)的差異正體現(xiàn)在二者對(duì)開發(fā)行為的指導(dǎo)思想上,六邊形架構(gòu)以領(lǐng)域模型為中心,引導(dǎo)開發(fā)者始終緊貼業(yè)務(wù)實(shí)際進(jìn)行模型設(shè)計(jì)。它與PICASO框架相輔相成,讓系統(tǒng)在應(yīng)對(duì)高復(fù)雜度業(yè)務(wù)時(shí)依然能保持對(duì)業(yè)務(wù)規(guī)則的清晰表達(dá)。

聲明式數(shù)據(jù)操作既要規(guī)范又要靈活

如前文所述,一個(gè)聚合會(huì)包含業(yè)務(wù)子域內(nèi)的全部實(shí)體與值對(duì)象,資源庫(kù)會(huì)維護(hù)這些業(yè)務(wù)實(shí)體的查詢、映射及組裝邏輯。但是并不是每一個(gè)領(lǐng)域服務(wù)都會(huì)用到聚合中的全部實(shí)體,如果每次獲取聚合根時(shí)都將聚合內(nèi)所有實(shí)體都查詢出來(lái),勢(shì)必會(huì)造成極大性能損耗。然而資源庫(kù)作為基礎(chǔ)設(shè)施層中的底層組件,也不可能為每一個(gè)領(lǐng)域服務(wù)提供專用的聚合查詢或者數(shù)據(jù)持久化接口。為了調(diào)和這兩者之間的矛盾,我們?cè)谫Y源庫(kù)實(shí)現(xiàn)上采用了聲明式數(shù)據(jù)操作設(shè)計(jì)。

在《能力編排領(lǐng)域特定語(yǔ)言》章節(jié)中我們已經(jīng)對(duì)聲明式編程有了比較具象的體會(huì),但聲明式編程并不限定于領(lǐng)域特定語(yǔ)言這一種實(shí)現(xiàn)形式,在資源庫(kù)的接口設(shè)計(jì)上我們采用了一種更加樸素的聲明式編程實(shí)現(xiàn)方法。如下圖所示,我們計(jì)劃聚合的查詢以及創(chuàng)意聚合的更新接口為例來(lái)闡述聲明式數(shù)據(jù)操作的設(shè)計(jì)細(xì)節(jié)。當(dāng)需要從資源庫(kù)中獲取聚合根對(duì)象時(shí),默認(rèn)情況下資源庫(kù)庫(kù)會(huì)自動(dòng)查詢聚合下所有子實(shí)體并完成組裝,但是開發(fā)者可以再提供一個(gè)額外的輔助查詢參數(shù)DomainQuery,并通過(guò)該對(duì)象的addSubEntityQuery方法聲明希望獲取哪些特定的子實(shí)體。默認(rèn)情況下資源庫(kù)會(huì)根據(jù)聚合根對(duì)應(yīng)的主表記錄ID做子實(shí)體查詢,如果開發(fā)者希望在子實(shí)體查詢時(shí)使用額外的匹配條件,則可以通過(guò)addSubEntityQuery方法同時(shí)聲明希望獲取的子實(shí)體類型以及執(zhí)行該類型子實(shí)體查詢時(shí)使用的額外匹配參數(shù),這一特性在與聚合根存在一對(duì)多的子實(shí)體查詢場(chǎng)景中會(huì)發(fā)揮重要作用。上述聲明式接口在資源庫(kù)實(shí)現(xiàn)中并不復(fù)雜,只需要在執(zhí)行每個(gè)子實(shí)體的查詢或修改操作之前,判斷一下上層調(diào)用方是否限定了僅對(duì)部分子實(shí)體進(jìn)行操作,如果設(shè)置了則進(jìn)一步判斷當(dāng)前子實(shí)體是否在上層調(diào)用方指定的子實(shí)體范圍之內(nèi),如果當(dāng)前子實(shí)體業(yè)務(wù)上允許調(diào)用方指定自定義的匹配邏輯,還需要嘗試從輔助查詢/修改參數(shù)中提取調(diào)用方為該子實(shí)體指定的自定義匹配邏輯。需要特別說(shuō)明的是,我們不推薦在新架構(gòu)內(nèi)部各個(gè)模塊之間采用任何形式的黑盒調(diào)用模式,不管是能力編排還是資源庫(kù)調(diào)用,上層調(diào)用方都有責(zé)任和義務(wù)理清底層模塊的內(nèi)部執(zhí)行邏輯,確保編排配置或調(diào)用參數(shù)設(shè)置正確。

wKgaomc7BKGAOfQ5AAWhkCGo63w963.png

基于資源庫(kù)進(jìn)行聲明式聚合數(shù)據(jù)查詢

四、新架構(gòu)下的業(yè)務(wù)開發(fā)流程速覽

業(yè)務(wù)建模

新架構(gòu)作為領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的戰(zhàn)術(shù)落地框架,能夠讓其發(fā)揮出最大價(jià)值的前提是對(duì)業(yè)務(wù)進(jìn)行良好的領(lǐng)域建模,下圖給出了廣告投放平臺(tái)中競(jìng)價(jià)及合約廣告投放服務(wù)的業(yè)務(wù)架構(gòu)。由于本文的主題聚焦在如何腳踏實(shí)地地實(shí)現(xiàn)一個(gè)基于領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想而設(shè)計(jì)的系統(tǒng),因此關(guān)于DDD思想戰(zhàn)略設(shè)計(jì)相關(guān)的內(nèi)容本文將不做過(guò)多闡述,相關(guān)內(nèi)容我們將在后續(xù)《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)與PICASO框架》一文中進(jìn)行詳細(xì)闡述。需要說(shuō)明的是,圖中的聚合服務(wù)層、領(lǐng)域能力層及數(shù)據(jù)模型層共同構(gòu)成了廣告投放平臺(tái)和核心領(lǐng)域模型。

wKgZomc7BKOAcvHsAArWhlqeZiw375.png

競(jìng)價(jià)及合約廣告投放服務(wù)分層業(yè)務(wù)架構(gòu)

工程結(jié)構(gòu)

下圖給出了新架構(gòu)思想落地時(shí)工程結(jié)構(gòu)的最佳實(shí)踐案例,工程中各module的職責(zé)以及與上文中業(yè)務(wù)分層架構(gòu)圖中各層的對(duì)應(yīng)關(guān)系依次為:

rtbad-framework:框架包,承載架構(gòu)標(biāo)準(zhǔn)規(guī)約及分層架構(gòu)圖中基礎(chǔ)設(shè)施層中的各項(xiàng)基礎(chǔ)組件。

rtbad-module/rtbad-support-module:模型包,對(duì)應(yīng)的是分層架構(gòu)圖中的模型層,承載領(lǐng)域模型中各個(gè)聚合及聚合實(shí)體對(duì)象的定義,另外用于實(shí)現(xiàn)底層存儲(chǔ)介質(zhì)中持久化數(shù)據(jù)與領(lǐng)域模型中實(shí)體對(duì)象映射邏輯的資源庫(kù)也定義在此類module中。其中support-module定義的是支撐域中的實(shí)體對(duì)象,該module下會(huì)按照業(yè)務(wù)子域進(jìn)一步進(jìn)行子module劃分。

rtbad-event:事件包,屬于特殊的數(shù)據(jù)模型層,承載這領(lǐng)域事件對(duì)象的定義,新架構(gòu)底層融合了事件驅(qū)動(dòng)架構(gòu)(篇幅的關(guān)系我們會(huì)在其他文章中進(jìn)行專門地介紹),因此事件體系建設(shè)也被納入到統(tǒng)一建模的工作中。

rtbad-composite/rtbad-support-compoaite:聚合服務(wù)包,對(duì)應(yīng)分層架構(gòu)中的領(lǐng)域能力及聚合服務(wù)層,承載領(lǐng)域能力及領(lǐng)域服務(wù)執(zhí)行器的實(shí)現(xiàn),其中support-Composite用于承載支撐域領(lǐng)域能力及領(lǐng)域服務(wù)執(zhí)行器的實(shí)現(xiàn),該module下會(huì)按照業(yè)務(wù)子域進(jìn)一步進(jìn)行子module劃分。

rtbad-app:部署層,內(nèi)部分為不同的子module,每一個(gè)子module對(duì)應(yīng)一個(gè)部署應(yīng)用,其實(shí)現(xiàn)邏輯就是根據(jù)應(yīng)用職責(zé)組裝底層各個(gè)子module,進(jìn)而實(shí)現(xiàn)不同應(yīng)用下能力及模型共享。

rtbad-api:對(duì)外接口SDK包,承載了對(duì)外提供的API接口定義。

wKgaomc7BKWASYayAAaI6h5NmFQ064.png

代碼開發(fā)流程示例

以下內(nèi)容以一次計(jì)劃新建請(qǐng)求為例,通過(guò)從流量入口到數(shù)據(jù)落庫(kù)的完整請(qǐng)求流程展示使用新架構(gòu)實(shí)現(xiàn)業(yè)務(wù)需求的全部過(guò)程,我們希望通過(guò)這個(gè)例子讓大家建立對(duì)新架構(gòu)的直觀感受。

領(lǐng)域服務(wù)統(tǒng)一入口

領(lǐng)域服務(wù)統(tǒng)一入口(Domain Service Faced)的作用是為HTTP、RPC、MQ等上層不同的請(qǐng)求流量暴露統(tǒng)一的服務(wù)入口。他將同一個(gè)業(yè)務(wù)子域內(nèi)領(lǐng)域服務(wù)執(zhí)行器集中到一起,便于流量介入層調(diào)用,同時(shí)也可以集中進(jìn)行方法性能監(jiān)控、調(diào)用量統(tǒng)計(jì)、請(qǐng)求日志記錄等通用功能。

wKgZomc7BKaABFDeAAJdCw-cgPo681.png

領(lǐng)域服務(wù)門面執(zhí)行器

領(lǐng)域服務(wù)統(tǒng)一入口引用的是各個(gè)領(lǐng)域服務(wù)門面執(zhí)行器,它負(fù)責(zé)從參數(shù)中提取業(yè)務(wù)標(biāo)識(shí)并定位到具體的領(lǐng)域服務(wù)實(shí)例。如下圖所示,所有具體的領(lǐng)域服務(wù)實(shí)例都繼承自領(lǐng)域服務(wù)門面執(zhí)行器,請(qǐng)求到來(lái)時(shí)領(lǐng)域服務(wù)門面先通過(guò)generateRouteKey方法從當(dāng)前參數(shù)中提取出本次請(qǐng)求的業(yè)務(wù)標(biāo)識(shí),然后與各個(gè)領(lǐng)域服務(wù)實(shí)例能夠支持的業(yè)務(wù)標(biāo)識(shí)做匹配,從而定位到應(yīng)該處理本次請(qǐng)求的服務(wù)實(shí)例。

wKgaomc7BKeAURU4AAPIDEJWJeA994.png

領(lǐng)域服務(wù)實(shí)例執(zhí)行器

能力執(zhí)行圖的作用是定義業(yè)務(wù)執(zhí)行流程,框架提供了豐富的API及大量的語(yǔ)法糖和默認(rèn)規(guī)則,配合鏈?zhǔn)秸{(diào)用的風(fēng)格,在支持靈活編排的同時(shí)減少了開發(fā)者的負(fù)擔(dān)。

wKgZomc7BKuARYsFAB9Nis5QbY0534.png

領(lǐng)域能力門面執(zhí)行器

負(fù)責(zé)從參數(shù)中提取場(chǎng)景標(biāo)識(shí)并定位具體的領(lǐng)域能力實(shí)例。

wKgaomc7BKyAGQZGAAI1IMWlhzQ653.png

領(lǐng)域能力實(shí)例執(zhí)行器

能力內(nèi)主要負(fù)責(zé)實(shí)現(xiàn)具體的業(yè)務(wù)規(guī)則,并對(duì)聚合根中相關(guān)屬性進(jìn)行設(shè)置/修改,一個(gè)領(lǐng)域服務(wù)編排的所有領(lǐng)域能力執(zhí)行完成之后,就能獲取一個(gè)完整的、全新的聚合根對(duì)象。

wKgZomc7BK2AdRd4AAEYXbj3iwU805.png

拓展點(diǎn)

拓展點(diǎn)的作用是作為任意維度的差異點(diǎn)補(bǔ)充分離工具。需要注意的是在新架構(gòu)中拓展點(diǎn)不是唯一的差異分離工具,在通用對(duì)象發(fā)現(xiàn)及路由機(jī)制下,領(lǐng)域服務(wù)、領(lǐng)域能力和拓展點(diǎn)都在不同維度上起著差異點(diǎn)分離的作用。相對(duì)與前兩者拓展點(diǎn)更加靈活,通常用來(lái)承接領(lǐng)域服務(wù)及能力自身路由維度之外的邏輯差異。比如出價(jià)設(shè)置能力已經(jīng)按照出價(jià)方式做了能力維度的差異分離,但是在相同的出價(jià)方式下,京準(zhǔn)通與流量貨幣化還存在一些細(xì)微的邏輯差異,那么這個(gè)時(shí)候就可以在該能力實(shí)例中通過(guò)拓展點(diǎn)來(lái)補(bǔ)充實(shí)現(xiàn)平臺(tái)維度的差異邏輯分離。

wKgaomc7BK-Aed3VAAJBxg6hF6c374.png

上面的例子是在計(jì)劃名稱設(shè)置能力中獲取不同產(chǎn)品線計(jì)劃名稱長(zhǎng)度上下限配置的拓展點(diǎn),計(jì)劃名稱設(shè)置能力自身按照產(chǎn)品線類型進(jìn)行業(yè)務(wù)模式路由,但是站內(nèi)計(jì)劃名稱設(shè)置主要業(yè)務(wù)邏輯基本一致,僅在部分校驗(yàn)邏輯上不同產(chǎn)品線有各自的要求,此時(shí)就可以使用一個(gè)名稱設(shè)置能力實(shí)例服務(wù)所有的站內(nèi)產(chǎn)品線,定義名稱設(shè)置主體業(yè)務(wù)規(guī)則,而把不同產(chǎn)品線的細(xì)微差異抽象成一個(gè)拓展點(diǎn)接口。

資源庫(kù)

上層業(yè)務(wù)通過(guò)聲明式接口實(shí)現(xiàn)聚合實(shí)體讀寫操作。

wKgZomc7BLCAb0g1AAIybkGeybI247.png

wKgaomc7BLGAKR8WAAHSu84Tghg241.png


五、結(jié)語(yǔ)

很多同學(xué)對(duì)業(yè)務(wù)開發(fā)一直存在一種偏見,認(rèn)為業(yè)務(wù)開發(fā)很簡(jiǎn)單,甚至有業(yè)務(wù)開發(fā)同學(xué)自己也時(shí)常調(diào)侃自己是CRUD工程師,認(rèn)為自己的工作沒(méi)什么技術(shù)含量。但其實(shí)業(yè)務(wù)開發(fā)一點(diǎn)都不簡(jiǎn)單,只是過(guò)去我們一直把它做簡(jiǎn)單了。如今業(yè)務(wù)形態(tài)復(fù)雜多變,商機(jī)轉(zhuǎn)瞬即逝,如何在快速變化著的復(fù)雜業(yè)務(wù)需求中維持系統(tǒng)健康、穩(wěn)定、持續(xù)迭代,要做到這一點(diǎn)的難度其實(shí)一點(diǎn)都不比底層技術(shù)差。程序員應(yīng)該是一門充滿學(xué)術(shù)性與創(chuàng)造性的職業(yè),我們唯有堅(jiān)守初心,不斷夯實(shí)自己的技術(shù)功底,沉淀提升抽象與建模能力,培養(yǎng)自己的系統(tǒng)化思維,不斷學(xué)習(xí)精進(jìn),追求極致編碼,這才是我們無(wú)法被AI替代的核心競(jìng)爭(zhēng)力與價(jià)值所在。

有同學(xué)可能會(huì)關(guān)注本文介紹PICASO框架未來(lái)是否可以對(duì)外共享,關(guān)于這個(gè)問(wèn)題我們的答案是肯定的。在PICASO框架開發(fā)之初我們的野心就沒(méi)有局限在京東廣告投放平臺(tái)這一個(gè)業(yè)務(wù)場(chǎng)景上,而是希望它可以走出廣告部,甚至走出京東,接受全社會(huì)開發(fā)者的檢驗(yàn),成為一個(gè)被業(yè)界認(rèn)可的復(fù)雜B端業(yè)務(wù)通用解決方案。然而作為一個(gè)一線業(yè)務(wù)團(tuán)隊(duì),快速支持業(yè)務(wù)方需求是我們的首要職責(zé)。盡管我們?cè)谶M(jìn)行PICASO底層框架開發(fā)時(shí)盡力維持與具體業(yè)務(wù)分離的開發(fā)原則,但是在需求排期比較緊張的時(shí)候,為了快速支持業(yè)務(wù)需求的開發(fā),還是存在將與廣告投放業(yè)務(wù)相關(guān)的邏輯耦合到了PICASO框架底層源碼中的情況。如果大家有興趣閱讀或者試玩PICASO的源碼,請(qǐng)聯(lián)系筆者為您開放一個(gè)示例版本的框架源碼權(quán)限,該版本框架的功能與筆者負(fù)責(zé)的線上系統(tǒng)使用的框架功能完全相同,只是去除了廣告投放業(yè)務(wù)相關(guān)的邏輯。您可以在該版本源碼上執(zhí)行任意的功能及性能測(cè)試,但是在我們對(duì)外發(fā)布正式的共享版本之前,我們并不建議您直接將該示例版本的源碼應(yīng)用到線上系統(tǒng)。目前框架功能已趨于穩(wěn)定,我們也將PICASO框架的開源化改造提上日程,也歡迎感興趣或者有開源社區(qū)維護(hù)經(jīng)驗(yàn)的同學(xué)一起交流共建。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)模型
    +關(guān)注

    關(guān)注

    0

    文章

    51

    瀏覽量

    10085
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4858

    瀏覽量

    69551
  • 架構(gòu)
    +關(guān)注

    關(guān)注

    1

    文章

    523

    瀏覽量

    25648
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    京東廣告投放平臺(tái)整潔架構(gòu)演進(jìn)之路

    設(shè)計(jì)思想到落地框架都進(jìn)行了徹底的革新,涉及內(nèi)容比較多,因此我們希望通過(guò)一系列文章循序漸進(jìn)地闡述本次架構(gòu)升級(jí)的始末。新架構(gòu)并不是一日而成的,而是經(jīng)過(guò)了多次架構(gòu)升級(jí)的演進(jìn),因此我們將本文作
    的頭像 發(fā)表于 09-18 10:26 ?1003次閱讀
    京東廣告投放平臺(tái)<b class='flag-5'>整潔</b><b class='flag-5'>架構(gòu)</b>演進(jìn)之路

    【「大模型時(shí)代的基礎(chǔ)架構(gòu)」閱讀體驗(yàn)】+ 未知領(lǐng)域的感受

    再到大模型云平臺(tái)的構(gòu)建,此書都有提及和講解,循序漸進(jìn),讓讀者可以由點(diǎn)及面,由面到體的來(lái)認(rèn)識(shí)大數(shù)據(jù)模型的體系架構(gòu)。 前言中,作者通過(guò)提出幾個(gè)問(wèn)題來(lái)引導(dǎo)讀者閱讀思考——分布式AI計(jì)算依賴哪些硬件特性
    發(fā)表于 10-08 10:40

    大學(xué)生循序漸進(jìn)的學(xué)習(xí)歷程如何開啟

    本帖最后由 gk320830 于 2015-3-9 01:50 編輯 作為一個(gè)本科電子專業(yè)大二的學(xué)生,剛接觸專業(yè)課的我,對(duì)于如何更好的掌握專業(yè)知識(shí),同時(shí)從更多的渠道豐富自己感到有些茫然。剛接觸電子專業(yè)知識(shí)感到十分生澀。如何開啟我大學(xué)的電子之路,更好借鑒大家的經(jīng)驗(yàn),達(dá)到博而精的效果請(qǐng)教各位前輩。我會(huì)時(shí)刻駐足論壇,誠(chéng)懇接受諸位的建議!謝謝!
    發(fā)表于 09-29 20:20

    《鴻蒙設(shè)備學(xué)習(xí)菜鳥指南》之 【五、搭建開發(fā)環(huán)境】

    ,然后再配置復(fù)雜的方案,循序漸進(jìn)。最簡(jiǎn)化方案:? Windows系統(tǒng):安裝VSCode就好了,就是這么簡(jiǎn)單? MacOS系統(tǒng):同上? Linux系統(tǒng)
    發(fā)表于 10-30 13:59

    如何系統(tǒng)的學(xué)習(xí)嵌入式?

    都說(shuō)嵌入式很難,即使去嵌入式培訓(xùn)機(jī)構(gòu)做系統(tǒng)訓(xùn)練,其實(shí)只是沒(méi)有掌握正確的學(xué)習(xí)嵌入式的方法,學(xué)習(xí)講究的是一個(gè)循序漸進(jìn)的過(guò)程,誰(shuí)也不能一口吃出一個(gè)大胖子,從基礎(chǔ)到專業(yè),從簡(jiǎn)單到高深,下面達(dá)內(nèi)講解一下系統(tǒng)學(xué)習(xí)嵌入式培訓(xùn)的基本步驟:
    發(fā)表于 03-09 06:23

    【電子書】Linux命令速查手冊(cè)PDF

    `本書以目前最熱門的Linux發(fā)行版——Ubuntu為平臺(tái),從零開始循序漸進(jìn)地介紹了Linux系統(tǒng)的基礎(chǔ)知識(shí)和實(shí)用操作。`
    發(fā)表于 04-02 14:05

    Linux嵌入式學(xué)習(xí)過(guò)程 精選資料推薦

    Linux嵌入式學(xué)習(xí)過(guò)程循序漸進(jìn)學(xué)習(xí)嵌入式開發(fā)技術(shù)一、練好基本功二、嵌入式Linux應(yīng)用開發(fā)誤區(qū)一、全身投入學(xué)習(xí)桌面或服務(wù)器版本linux系統(tǒng)誤區(qū)二、直接閱讀linux內(nèi)核源代碼如何正確的嵌入式
    發(fā)表于 07-19 09:07

    VB6循序漸進(jìn)教程

    VB6循序漸進(jìn)教程
    發(fā)表于 02-06 16:52 ?50次下載

    操作系統(tǒng)原理及應(yīng)用(linux)_王紅

    本書在內(nèi)容編排上力求由淺入深,循序漸進(jìn),舉一反三,突出重點(diǎn),通俗易懂。采用模塊化結(jié)構(gòu),兼顧不同層次的需求。
    發(fā)表于 12-12 14:58 ?0次下載
    操作<b class='flag-5'>系統(tǒng)</b>原理及應(yīng)用(linux)_王紅

    PERL編程24學(xué)時(shí)教程(完整版)

    perl語(yǔ)言的學(xué)習(xí)資料,由淺入深。循序漸進(jìn)
    發(fā)表于 11-17 10:21 ?0次下載

    Linux系統(tǒng)的保護(hù),四個(gè)步驟循序漸進(jìn)有奇效

    如今,互連設(shè)備比以往更易受到安全威脅,但是錯(cuò)綜復(fù)雜的平臺(tái)讓開發(fā)人員很難顧及到每一個(gè)潛在的入口點(diǎn)。為幫助減少基于 Linux 的系統(tǒng)所遭受到的威脅,英特爾?公司旗下的 Wind River 提出了 OEM 應(yīng)該遵循的四步流程:監(jiān)控、評(píng)估、通知和修復(fù)。
    發(fā)表于 09-11 16:32 ?8次下載
    Linux<b class='flag-5'>系統(tǒng)</b>的保護(hù),四個(gè)步驟<b class='flag-5'>循序漸進(jìn)</b>有奇效

    2021 年問(wèn)世!蘋果自研 5G 芯片

    預(yù)計(jì)將先從 iPad 等「次級(jí)」產(chǎn)品循序漸進(jìn)采用自家芯片。
    的頭像 發(fā)表于 08-01 17:48 ?2922次閱讀

    人工智能在發(fā)展的路上怎樣避免陷阱

    為了更好的推動(dòng)其發(fā)展,人工智能的落地與應(yīng)用必然會(huì)是一個(gè)循序漸進(jìn)的過(guò)程。
    發(fā)表于 11-12 14:31 ?600次閱讀

    電力電子技術(shù)從基礎(chǔ)到運(yùn)用

    想學(xué)習(xí)電力電子技術(shù)的 這本書可以啊 從簡(jiǎn)單到基礎(chǔ) 循序漸進(jìn)
    發(fā)表于 03-11 14:54 ?0次下載

    高級(jí)進(jìn)階:復(fù)雜業(yè)務(wù)系統(tǒng)的通用架構(gòu)設(shè)計(jì)

    兩點(diǎn)的規(guī)模和復(fù)雜性直接決定了系統(tǒng)復(fù)雜程度。比如就拿我們的電商系統(tǒng)舉例,分成很多部分,商品、庫(kù)存、采購(gòu)、訂單、物流、財(cái)務(wù),這個(gè)只是大的分類,還有針對(duì) C
    的頭像 發(fā)表于 08-14 11:33 ?687次閱讀
    高級(jí)進(jìn)階:<b class='flag-5'>復(fù)雜</b>業(yè)務(wù)<b class='flag-5'>系統(tǒng)</b>的通用<b class='flag-5'>架構(gòu)</b>設(shè)計(jì)