作者:京東零售 趙嘉鐸
前言
從去年開(kāi)始京東廣告投放系統(tǒng)做了一次以領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)為思想內(nèi)核的架構(gòu)升級(jí),在深入理解DDD思想的同時(shí),我們基于廣告投放業(yè)務(wù)的本質(zhì)特征大膽地融入了自己的理解和改造。新架構(gòu)是從設(shè)計(jì)思想到落地框架都進(jìn)行了徹底的革新,涉及內(nèi)容比較多,因此我們希望通過(guò)一系列文章循序漸進(jìn)地闡述本次架構(gòu)升級(jí)的始末。新架構(gòu)并不是一日而成的,而是經(jīng)過(guò)了多次架構(gòu)升級(jí)的演進(jìn),因此我們將本文作為該系列的第一篇文章,先讓大家通過(guò)廣告投放平臺(tái)的架構(gòu)演進(jìn)歷程來(lái)了解新架構(gòu)的設(shè)計(jì)初衷。
如前言所述,本文主要聚焦于廣告投放系統(tǒng)歷代代碼架構(gòu)的演進(jìn)歷程,我們也不希望本文的篇幅過(guò)于冗長(zhǎng),因此對(duì)新架構(gòu)中具體框架及API的說(shuō)明淺嘗輒止,我們會(huì)在本系列接下來(lái)的數(shù)篇文章中逐步給出愈加具象的描述。
什么是好的代碼架構(gòu)
大家都清楚在當(dāng)前的工作中我們所面臨的主要矛盾是“越來(lái)越多的多場(chǎng)景化復(fù)雜業(yè)務(wù)需求與有限的研發(fā)人力之間的矛盾”。而要解決這一矛盾,就要求我們的系統(tǒng)能做到:設(shè)計(jì)易拓展、代碼易復(fù)用、邏輯易傳承、運(yùn)行更穩(wěn)定。這看起來(lái)像是一句空喊的口號(hào),但其實(shí)每一個(gè)特性都有具體的要求:
?設(shè)計(jì)易拓展
一個(gè)好的架構(gòu)應(yīng)該能夠?qū)崿F(xiàn)業(yè)務(wù)與技術(shù)組件的分離,使設(shè)計(jì)者能夠?qū)W⒂跇I(yè)務(wù)流程,以填空的方式直接套用開(kāi)箱即用的組件、框架和解決方案,不必進(jìn)行大量的重復(fù)設(shè)計(jì);另外好的架構(gòu)也能夠引導(dǎo)設(shè)計(jì)者完成最小子問(wèn)題的正交分解,將設(shè)計(jì)者從錯(cuò)綜復(fù)雜的上層業(yè)務(wù)邏輯中拯救出來(lái),逐個(gè)擊破,降低需求的復(fù)雜度和理解成本。
?代碼易復(fù)用
一個(gè)好的架構(gòu)應(yīng)該有良好的分層,強(qiáng)調(diào)正交子模塊的拆分與封裝,同層原子模塊之間避免互相依賴(lài)和耦合,讓上層系統(tǒng)能夠輕松實(shí)現(xiàn)底層業(yè)務(wù)邏輯的組合復(fù)用,以
?的實(shí)現(xiàn)復(fù)雜度支撐
?業(yè)務(wù)復(fù)雜度;另外我們的業(yè)務(wù)邏輯是建立在數(shù)據(jù)之上的,一個(gè)封裝良好的代碼架構(gòu)在實(shí)現(xiàn)業(yè)務(wù)邏輯復(fù)用的同時(shí)應(yīng)該有健全的數(shù)據(jù)模型維護(hù)和共享機(jī)制,避免同一個(gè)數(shù)據(jù)對(duì)象的重復(fù)查詢(xún),并能夠輕松通過(guò)批量操作降低系統(tǒng)的I/O負(fù)載。
?邏輯易傳承
我們歷史上多次嘗試通過(guò)維護(hù)文檔的方式來(lái)建立業(yè)務(wù)知識(shí)庫(kù),但都以失敗告終了。在這個(gè)過(guò)程中我們意識(shí)到業(yè)務(wù)功能都是由我們的代碼承接的,它天然具備業(yè)務(wù)知識(shí)庫(kù)的功能。因此一個(gè)好的代碼架構(gòu)不僅能夠?qū)崿F(xiàn)業(yè)務(wù)功能,而且要承擔(dān)起傳遞業(yè)務(wù)知識(shí)的職責(zé):當(dāng)有新同學(xué)加入時(shí),代碼能夠以最直接的方式幫助他快速建立起對(duì)整個(gè)業(yè)務(wù)的宏觀認(rèn)知,而進(jìn)行具體的需求開(kāi)發(fā)時(shí),又能夠按圖索驥,快速定位改動(dòng)點(diǎn)并深入了解其業(yè)務(wù)細(xì)節(jié)。
?運(yùn)行更穩(wěn)定
一個(gè)好的代碼架構(gòu)在面對(duì)多場(chǎng)景化的需求時(shí),可以做到場(chǎng)景隔離,避免不同場(chǎng)景的特有邏輯之間互相耦合干擾,出現(xiàn)一個(gè)場(chǎng)景需求上線后影響其他業(yè)務(wù)場(chǎng)景的問(wèn)題;除此之外,一個(gè)好的架構(gòu)應(yīng)該通過(guò)設(shè)計(jì)良好的框架和標(biāo)準(zhǔn)模板在有益的程度上對(duì)開(kāi)發(fā)者的編碼行為進(jìn)行約束,把規(guī)范框架化,而不是過(guò)多的依賴(lài)人置和code review來(lái)實(shí)現(xiàn)規(guī)范統(tǒng)一。
架構(gòu)演進(jìn)之路
在上一章節(jié)列舉出來(lái)的特質(zhì)也是評(píng)判一個(gè)架構(gòu)優(yōu)劣的標(biāo)準(zhǔn),而我們新的架構(gòu)方案也正是在一次次為了實(shí)現(xiàn)這些目標(biāo)而采取的摸索中逐漸成型的。在接下來(lái)的幾個(gè)章節(jié)中,我們將從最早期的代碼架構(gòu)開(kāi)始,逐代剖析架構(gòu)演進(jìn)的歷程,通過(guò)這種方式讓大家了解每一次改進(jìn)背后的設(shè)計(jì)動(dòng)機(jī)和思路,從而更好的理解新架構(gòu)的設(shè)計(jì)思想,也為大家推動(dòng)架構(gòu)向下一代演進(jìn)打好基礎(chǔ)。
第一代:沒(méi)有架構(gòu)的架構(gòu)
最開(kāi)始的時(shí)候我們的架構(gòu)如下圖所示,這也是我們目前最常見(jiàn)一種代碼架構(gòu)??梢钥闯鏊奶攸c(diǎn)就是“簡(jiǎn)單”,沒(méi)有過(guò)多的封裝和設(shè)計(jì),平鋪直敘,數(shù)據(jù)查詢(xún)和業(yè)務(wù)邏輯處理互相交織,是面向數(shù)據(jù)庫(kù)編程的典型案例。這種架構(gòu)在早期場(chǎng)景單一、需求簡(jiǎn)單的階段可以快速實(shí)現(xiàn)功能,沒(méi)有多余的設(shè)計(jì)成本,但是隨著業(yè)務(wù)的發(fā)展,系統(tǒng)服務(wù)的場(chǎng)景越來(lái)越多,這套架構(gòu)就變得越來(lái)越不簡(jiǎn)單了。
??
問(wèn)題主要體現(xiàn)在兩個(gè)方面:
1.由于大家習(xí)慣“打補(bǔ)丁”式的開(kāi)發(fā),來(lái)了一個(gè)業(yè)務(wù)需求就在現(xiàn)有的流程中增加一個(gè)if...else分支,然后直接在新分支內(nèi)實(shí)現(xiàn)業(yè)務(wù)邏輯。當(dāng)業(yè)務(wù)流程積攢的足夠冗長(zhǎng)時(shí),就很容易忽視前置流程已經(jīng)查詢(xún)好的數(shù)據(jù)對(duì)象,造成數(shù)據(jù)重復(fù)查詢(xún)。同時(shí)為了實(shí)現(xiàn)邏輯的復(fù)用,我們開(kāi)始把一些常用邏輯封裝為單獨(dú)的方法,然后在上層業(yè)務(wù)流程中直接調(diào)用,然而我們?cè)?strong>封裝底層方法往往會(huì)把數(shù)據(jù)的獲取邏輯封裝下來(lái),這進(jìn)一步加劇了數(shù)據(jù)重復(fù)查詢(xún)的問(wèn)題,在有循環(huán)調(diào)用的場(chǎng)景中這個(gè)問(wèn)題會(huì)更加突出。另外這種邏輯的復(fù)用方式還會(huì)造成數(shù)據(jù)庫(kù)訪問(wèn)碎片化,我們很難利用批量操作的優(yōu)勢(shì)優(yōu)化系統(tǒng)性能。在最近剛結(jié)束的大促中,我們前期暴露的幾個(gè)性能問(wèn)題基本都是這中模式導(dǎo)致的。
2.除了性能問(wèn)題之外,由于不同業(yè)務(wù)場(chǎng)景的邏輯互相交織,代碼分支判斷邏輯缺少統(tǒng)一的規(guī)劃,if分支層層嵌套,導(dǎo)致我們的代碼邏輯圈復(fù)雜度不斷飆升,本來(lái)應(yīng)該通用的邏輯對(duì)不同場(chǎng)景的適配性越來(lái)越低。漸漸的,我們發(fā)現(xiàn)新增需求的開(kāi)發(fā)越來(lái)越“不簡(jiǎn)單”了:在試圖復(fù)用一段看起來(lái)相似的代碼邏輯時(shí)會(huì)有很多糾結(jié)和不盡人意的地方,對(duì)代碼執(zhí)行流程的認(rèn)知也不似以往那么清晰了,為了防止對(duì)舊的業(yè)務(wù)流程造成影響,我們開(kāi)始增加更多的if分支,這反過(guò)來(lái)進(jìn)一步加劇了情況的惡化,于是我們的代碼中充斥著重復(fù)代碼、多達(dá)5、6層的嵌套...
為了緩解舊架構(gòu)中的這些問(wèn)題,我們引入了上下文機(jī)制,嘗試將數(shù)據(jù)的查詢(xún)邏輯與業(yè)務(wù)流程分離開(kāi)來(lái),由此引出了第二代基于上下文機(jī)制的代碼架構(gòu)。
第二代:略有改善的上下文機(jī)制
上下文主要是為了解決數(shù)據(jù)重復(fù)查詢(xún)問(wèn)題引入的,思路特別樸素,就是把一個(gè)完整業(yè)務(wù)流程中要用到的全部數(shù)據(jù)提前在方法一開(kāi)始就查詢(xún)好,并做好校驗(yàn)。查詢(xún)出來(lái)的數(shù)據(jù)對(duì)象保存到一個(gè)上下文對(duì)象中,這個(gè)上下文對(duì)象會(huì)貫穿整個(gè)業(yè)務(wù)流程,業(yè)務(wù)邏輯中需要用得到底層數(shù)據(jù)實(shí)體的時(shí)候統(tǒng)一從上下文對(duì)象中獲取。
??
所有的數(shù)據(jù)集中在“上下文構(gòu)造”步驟中查詢(xún),整個(gè)業(yè)務(wù)流程運(yùn)行在上下文對(duì)象中
通過(guò)上下文的引入,我們基本上解決了數(shù)據(jù)重復(fù)查詢(xún)的問(wèn)題,另外我們數(shù)據(jù)的提前集中查詢(xún)也有助于啟發(fā)我們主動(dòng)通過(guò)數(shù)據(jù)庫(kù)批量查詢(xún)進(jìn)一步提升系統(tǒng)性能。而且上下文構(gòu)造的過(guò)程其實(shí)也是數(shù)據(jù)校驗(yàn)的過(guò)程,通過(guò)上下文的提前構(gòu)建,我們?cè)谝欢ǔ潭壬蠈?shí)現(xiàn)了預(yù)校驗(yàn)的邏輯,從而可以提前發(fā)現(xiàn)異常數(shù)據(jù),避免寫(xiě)入臟數(shù)據(jù)和不必要的數(shù)據(jù)回滾操作。
上下文的引入其實(shí)并不算什么架構(gòu)上的改進(jìn),它主要是解決了數(shù)據(jù)對(duì)象重復(fù)查詢(xún)的問(wèn)題,但是也引入了一些新的痛點(diǎn),首先就是我們的數(shù)據(jù)模型中數(shù)據(jù)對(duì)象往往比較多且關(guān)系復(fù)雜,這導(dǎo)致我們的上下文構(gòu)造邏輯十分冗長(zhǎng)。而且同一個(gè)業(yè)務(wù)域內(nèi)不同的接口使用的上下文對(duì)象中屬性有較大重疊,但是也有各自的差異,因此這些上下文對(duì)象的構(gòu)造邏輯又開(kāi)始出現(xiàn)大量的重復(fù)編碼或者混亂的封裝。比如詢(xún)量單的新建接口與修改接口對(duì)應(yīng)的上下文中80%的屬性是相同的,這些屬性的查詢(xún)和關(guān)聯(lián)邏輯造成了大量的重復(fù)編碼。除了重復(fù)編碼問(wèn)題之外,上下文機(jī)制也并沒(méi)有從根本上解決多場(chǎng)景下業(yè)務(wù)流程差異復(fù)雜度高的問(wèn)題。
第三代:數(shù)據(jù)模型與業(yè)務(wù)模型的分離
在第二代架構(gòu)中我們雖然將數(shù)據(jù)對(duì)象的查詢(xún)集中到了上下文構(gòu)造步驟中執(zhí)行,但是上下文對(duì)象的定義是和接口方法綁定的。對(duì)外暴露多少服務(wù)我們就會(huì)定義多少上下文對(duì)象,甚至不同的場(chǎng)景也會(huì)有各自的上下文構(gòu)造邏輯,此時(shí)系統(tǒng)的數(shù)據(jù)模型依然隱藏在了具體的業(yè)務(wù)邏輯中。
在一次次的改進(jìn)嘗試中,我們逐漸意識(shí)到多場(chǎng)景化的業(yè)務(wù)特性賦予我們一個(gè)動(dòng)態(tài)的業(yè)務(wù)模型(或者說(shuō)業(yè)務(wù)規(guī)則集),但是我們的數(shù)據(jù)模型卻是靜態(tài)的,數(shù)據(jù)模型的多場(chǎng)景化程度遠(yuǎn)小于業(yè)務(wù)規(guī)則的多場(chǎng)景化程度,即:同一個(gè)功能模塊在不同場(chǎng)景下的業(yè)務(wù)規(guī)則存在差異,但卻始終在操作同一套數(shù)據(jù)模型。有些同學(xué)可能會(huì)對(duì)這一結(jié)論產(chǎn)生質(zhì)疑:在不同的場(chǎng)景下我們對(duì)數(shù)據(jù)對(duì)象的構(gòu)造也是不同的,比如只有快車(chē)的單元下才會(huì)有關(guān)鍵詞,京X的單元上綁定的是應(yīng)用集,而直投單元上綁定的是流量包等等,這些例子是不是都說(shuō)明我們的數(shù)據(jù)模型也在隨業(yè)務(wù)規(guī)則一起動(dòng)態(tài)變化著呢?對(duì)于這個(gè)問(wèn)題我們需要“細(xì)品”一下:“數(shù)據(jù)對(duì)象屬性值的設(shè)置和校驗(yàn)”到底是屬于業(yè)務(wù)模型的范疇還是數(shù)據(jù)模型的范疇?其實(shí)我們所說(shuō)的數(shù)據(jù)模型指的是實(shí)體及實(shí)體之間的關(guān)系, 不論某個(gè)產(chǎn)品線或計(jì)劃類(lèi)型是否會(huì)去設(shè)置某個(gè)子屬性的值,只要我們的數(shù)據(jù)模型完成了定義,那么在任何場(chǎng)景下數(shù)據(jù)模型的中實(shí)體的定義及實(shí)體之間的關(guān)系都是不變的,實(shí)體只要定義出來(lái),它會(huì)一直在那,只是某些場(chǎng)景下其屬性值為null而已。而實(shí)體屬性值的設(shè)置邏輯則是典型的業(yè)務(wù)模型的范疇。
在明確了“多場(chǎng)景化的動(dòng)態(tài)業(yè)務(wù)模型是建立在一個(gè)相對(duì)靜態(tài)的數(shù)據(jù)模型之上”這一本質(zhì)之后,為了解決上下文對(duì)象構(gòu)造復(fù)雜度高及重復(fù)編碼的問(wèn)題,我們需要做的就是數(shù)據(jù)模型的分離和下沉,為此我們引入了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想中的“聚合”概念。在這篇文章里我們不需要教條地引用DDD中關(guān)于聚合的定義,它的含義可以通俗的理解為:一組關(guān)聯(lián)密切且關(guān)系明確的實(shí)體或值對(duì)象的集合,一個(gè)聚合通常會(huì)支撐著一個(gè)功能極其內(nèi)聚的上層業(yè)務(wù)模塊。一個(gè)聚合中會(huì)定義唯一個(gè)聚合根對(duì)象,聚合根是整個(gè)聚合中實(shí)體操作的中心,聚合中的全部實(shí)體都可以通過(guò)聚合根直接或間接的訪問(wèn)到。聚合根通常并不難確定,比如計(jì)劃聚合的聚合根自然就是Campaign實(shí)體,我們可以直接通過(guò)Campaign聚合根對(duì)象直接引用到計(jì)劃下的預(yù)算、投放時(shí)段等子實(shí)體信息。
將聚合的概念落地到代碼架構(gòu)中我們需要做以下升級(jí):
1.根據(jù)業(yè)務(wù)流程設(shè)計(jì)合理的數(shù)據(jù)模型,需要注意的是數(shù)據(jù)模型中的實(shí)體并不一定要與底層的庫(kù)表一一對(duì)應(yīng),而是應(yīng)該從業(yè)務(wù)本質(zhì)出發(fā)完成實(shí)體劃分和定義,另外在模型中也需要體現(xiàn)實(shí)體之間的關(guān)聯(lián)關(guān)系。
2.在業(yè)務(wù)流程和底層數(shù)據(jù)庫(kù)之間增加一個(gè)聚合層,在這一層中將第一步設(shè)計(jì)的數(shù)據(jù)模型定義為Java對(duì)象,其中實(shí)體之間的關(guān)系則轉(zhuǎn)化為類(lèi)與屬性的關(guān)系。比如AdGroup領(lǐng)域?qū)ο髢?nèi)屬性除了體現(xiàn)ad_group表中定義的字段之外,也定義了單元下的人群、流量包、創(chuàng)意列表等子實(shí)體對(duì)應(yīng)的屬性。
3.上層的業(yè)務(wù)流程對(duì)聚合中實(shí)體的訪問(wèn)和修改都是通過(guò)聚合根實(shí)現(xiàn)的,而要想獲取聚合根則必須通過(guò)聚合層暴露出來(lái)的Repository接口。
第三步提到的Repository層接口是完全面向數(shù)據(jù)模型定義的,幾乎與業(yè)務(wù)無(wú)關(guān),通常不會(huì)為某個(gè)特殊的業(yè)務(wù)場(chǎng)景定義專(zhuān)用的數(shù)據(jù)查詢(xún)或?qū)懭敕椒ǎx的都是通用的數(shù)據(jù)訪問(wèn)接口,讓上層業(yè)務(wù)以聲明式的方法獲取所需的聚合根對(duì)象(或集合)。Repository的將數(shù)據(jù)對(duì)象的查詢(xún)和實(shí)體關(guān)系的組裝邏輯屏蔽在其接口實(shí)現(xiàn)中,上層業(yè)務(wù)不需要再次執(zhí)行聚合根下子實(shí)體對(duì)象的查詢(xún)和關(guān)聯(lián)邏輯。
?
??
引入聚合后上下文的構(gòu)造和數(shù)據(jù)的寫(xiě)入流程得以極大地簡(jiǎn)化
?
從上圖可以看出,由于上下文中的很多數(shù)據(jù)對(duì)象都被轉(zhuǎn)移到了聚合中,之前繁瑣的數(shù)據(jù)查詢(xún)和關(guān)聯(lián)邏輯被分離下沉到了Repository的實(shí)現(xiàn)中,業(yè)務(wù)模型中不同的服務(wù)接口可以直接復(fù)用Repository中沉淀的數(shù)據(jù)查詢(xún)和組裝邏輯,上下文構(gòu)造得以極大的精簡(jiǎn),重復(fù)編碼問(wèn)題也得到了根本性的解決,體現(xiàn)了我們架構(gòu)目標(biāo)中“代碼易復(fù)用”的要求。
除了更加靈活和優(yōu)雅的復(fù)用數(shù)據(jù)查詢(xún)和組裝邏輯之外,聚合的引入讓我們實(shí)現(xiàn)了數(shù)據(jù)模型和業(yè)務(wù)模型的分離,聚合層幾乎與業(yè)務(wù)流程無(wú)關(guān),直接體現(xiàn)數(shù)據(jù)模型的完整全貌。當(dāng)有新同學(xué)加入的時(shí)候,可以通過(guò)閱讀聚合層代碼獲取最全、最準(zhǔn)確的數(shù)據(jù)模型定義,不再需要從代碼中四處搜集對(duì)象關(guān)聯(lián)關(guān)系的蛛絲馬跡,這體現(xiàn)了我們架構(gòu)目標(biāo)中“邏輯易傳承”的要求。
??
RE降級(jí)后的補(bǔ)數(shù)邏輯一直是一件令人頭痛的事情,聚合的引入可以極大地簡(jiǎn)化這一流程
本文主要探討的是我們引入聚合的動(dòng)機(jī),關(guān)于數(shù)據(jù)模型的設(shè)計(jì)、Repository接口的實(shí)現(xiàn)和使用相關(guān)的實(shí)戰(zhàn)內(nèi)容只是點(diǎn)到為止,關(guān)于這部分的詳細(xì)內(nèi)容屬于多體系架構(gòu)中的數(shù)據(jù)模型管理體系,我們將在該體系的設(shè)計(jì)中進(jìn)行深入的探討。 其實(shí)就我個(gè)人的實(shí)踐經(jīng)驗(yàn)而言,在實(shí)現(xiàn)架構(gòu)升級(jí)所作出的眾多嘗試中,聚合的引入是給我?guī)?lái)幸福感最強(qiáng)的一項(xiàng)改進(jìn),但是我始終沒(méi)能找到一種合適的表達(dá)方式將我之所感無(wú)所保留地傳遞給大家,所言之語(yǔ)總是蒼白,或許聚合引入帶來(lái)的收益只有讓大家在實(shí)踐中去親身感受了。 另外熟悉領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的同學(xué)可能已經(jīng)從上面的設(shè)計(jì)中嗅到了一絲DDD的味道,但是可能又會(huì)覺(jué)得沒(méi)有那么DDD,關(guān)于這個(gè)問(wèn)題限于當(dāng)前陳述上下文的原因還不好直接給予解答,容筆者在這里賣(mài)個(gè)關(guān)子,在后面的系列文章中我們會(huì)詳細(xì)闡明這種設(shè)計(jì)的細(xì)節(jié)和考量。
第四代:領(lǐng)域能力拆分與編排
通過(guò)引入聚合我們基本上解決了數(shù)據(jù)查詢(xún)邏輯復(fù)用的問(wèn)題,但是由于多平臺(tái)、多維度和多場(chǎng)景化帶來(lái)的業(yè)務(wù)復(fù)雜度的問(wèn)題卻依然存在。而解決這個(gè)問(wèn)題的基本思路其實(shí)祖師爺已經(jīng)給我們準(zhǔn)備好了,那就是組合復(fù)用原則。
作為一個(gè)典型的2B的平臺(tái),我們的業(yè)務(wù)特點(diǎn)就是流程冗長(zhǎng)復(fù)雜,一個(gè)業(yè)務(wù)流程通常由多個(gè)流程節(jié)點(diǎn)組成,比如單元新建流程,可以分為:基礎(chǔ)信息設(shè)置、單元名稱(chēng)設(shè)置、投放周期設(shè)置、投放位置設(shè)置、定向設(shè)置、出價(jià)設(shè)置、關(guān)鍵詞設(shè)置等多個(gè)節(jié)點(diǎn)組成。這些節(jié)點(diǎn)再疊加上不同產(chǎn)品線(展位、快車(chē)、觸點(diǎn))、站外不同媒體(頭、騰、百、快、京X)、不同的投放平臺(tái)(京準(zhǔn)通、流量貨幣化、京易投)以及不同的站點(diǎn)(國(guó)內(nèi)、泰國(guó)、印尼、出海)等多維度的業(yè)務(wù)場(chǎng)景,就使系統(tǒng)具備了
?業(yè)務(wù)復(fù)雜度,其中
?為不同業(yè)務(wù)細(xì)分維度下的場(chǎng)景復(fù)雜度,而組合復(fù)用原則就是專(zhuān)門(mén)為解決這一問(wèn)題而生的。
組合復(fù)用原則強(qiáng)調(diào)復(fù)雜問(wèn)題的拆分,拆分出來(lái)的最小子問(wèn)題可以互不干擾地進(jìn)行獨(dú)立的迭代。在此基礎(chǔ)上,上層模塊可以通過(guò)對(duì)最小子問(wèn)題的組合編排實(shí)現(xiàn)一項(xiàng)完整的業(yè)務(wù)功能。由于最小子問(wèn)題之間彼此正交,我們獨(dú)立維護(hù)各個(gè)最小子問(wèn)題的編碼復(fù)雜度就可以降級(jí)為
??;谠撍枷?,我們?cè)谛录軜?gòu)中引入了領(lǐng)域能力拆分與編排機(jī)制。
領(lǐng)域能力的識(shí)別與拆分
在新架構(gòu)中我們會(huì)將一個(gè)完整的業(yè)務(wù)流程正交分解為多個(gè)“能力節(jié)點(diǎn)”。這里所說(shuō)的“正交分解”是指拆分出來(lái)的各個(gè)子模塊之間互不干擾,可以獨(dú)立進(jìn)行迭代。舉個(gè)例子來(lái)說(shuō),在早期大家進(jìn)行能力梳理的時(shí)候,有同學(xué)從單元新建流程中拆分出了“出價(jià)信息校驗(yàn)”和“出價(jià)設(shè)置”兩個(gè)能力節(jié)點(diǎn),這其實(shí)是不合理的。因?yàn)槌鰞r(jià)信息的校驗(yàn)和出價(jià)屬性的設(shè)置并不正交,他們互相依賴(lài),我們應(yīng)該這兩段邏輯合并到一起,抽象為一個(gè)“出價(jià)設(shè)置”節(jié)點(diǎn)。
能力節(jié)點(diǎn)主要定義了系統(tǒng)中各個(gè)原子模塊的功能范圍。一般來(lái)說(shuō),一個(gè)能力節(jié)點(diǎn)通常包含一個(gè)能力門(mén)面和0到多個(gè)能力實(shí)例。能力門(mén)面并不承接具體的業(yè)務(wù)邏輯,它的作用是對(duì)外暴露統(tǒng)一的調(diào)用入口及請(qǐng)求轉(zhuǎn)發(fā),具體的業(yè)務(wù)邏輯則由能力門(mén)面下的能力實(shí)例承接。比如出價(jià)設(shè)置節(jié)點(diǎn)下會(huì)按照出價(jià)類(lèi)型劃分為:手動(dòng)出價(jià)、tCPA智能出價(jià)、MC智能出價(jià)、eCPC智能出價(jià)幾個(gè)具體的領(lǐng)域能力實(shí)例,而在人群定向設(shè)置節(jié)點(diǎn)下則有京選店鋪人群設(shè)置、樂(lè)高人群設(shè)置和自定義人群設(shè)置幾個(gè)領(lǐng)域能力實(shí)例。
能力編排與請(qǐng)求路由
將整個(gè)系統(tǒng)劃分為多個(gè)獨(dú)立的能力節(jié)點(diǎn)之后,接下來(lái)就需要通過(guò)能力編排將這些能力節(jié)點(diǎn)串聯(lián)到一起組裝成一個(gè)完成的服務(wù)。如下圖所示,所謂的能力編排就是將業(yè)務(wù)流程中所需要的原子模塊對(duì)應(yīng)的能力節(jié)點(diǎn)串聯(lián)起來(lái),定義好他們之間數(shù)據(jù)傳遞的方式和編排規(guī)則。需要注意的是,能力編排操作的是能力節(jié)點(diǎn)而不是能力實(shí)例,在處理服務(wù)請(qǐng)求時(shí),每一個(gè)能力節(jié)點(diǎn)負(fù)責(zé)將請(qǐng)求路由到正確的領(lǐng)域能力實(shí)例中進(jìn)行處理。之所以這樣設(shè)計(jì)是因?yàn)槲覀兊臉I(yè)務(wù)流程相對(duì)穩(wěn)定,系統(tǒng)對(duì)外提供的服務(wù)流程中業(yè)務(wù)節(jié)點(diǎn)及節(jié)點(diǎn)間的執(zhí)行順序很少會(huì)發(fā)生變化,需求迭代往往是對(duì)某個(gè)能力節(jié)點(diǎn)進(jìn)行橫向的拓展,也就是對(duì)具體的領(lǐng)域能力實(shí)例進(jìn)行增刪或者修改。通過(guò)能力節(jié)點(diǎn)的抽象及路由機(jī)制的引入,我們將動(dòng)態(tài)變化著的部分從相對(duì)穩(wěn)定的業(yè)務(wù)流程中分離出去,從而保障核心流程的穩(wěn)定性不被頻繁變化著的需求所影響,這一點(diǎn)與我們當(dāng)時(shí)做數(shù)據(jù)模型與業(yè)務(wù)模型分離的動(dòng)機(jī)是一致的,本質(zhì)上都是在隔離變化。
??
一個(gè)能力編排示例(點(diǎn)擊放大查看)
除了能力編排框架之外,能力實(shí)例的路由機(jī)制也是實(shí)現(xiàn)復(fù)雜度降維的關(guān)鍵。如下圖所示,路由機(jī)制通過(guò)將能力門(mén)面及門(mén)面下用于承接不同場(chǎng)景下具體業(yè)務(wù)規(guī)則的能力實(shí)例打包到一起,同時(shí)也將原子業(yè)務(wù)模塊內(nèi)的場(chǎng)景復(fù)雜度封裝屏蔽在了模塊內(nèi)部,使上層的業(yè)務(wù)流程定義只需要關(guān)注一次完整的請(qǐng)求需要使用哪些原子業(yè)務(wù)模塊(也就是能力節(jié)點(diǎn)),而無(wú)需關(guān)注這個(gè)節(jié)點(diǎn)下具體的能力實(shí)例,當(dāng)請(qǐng)求到來(lái)時(shí),處理流程流經(jīng)相應(yīng)的能力節(jié)點(diǎn)時(shí),將通過(guò)當(dāng)前請(qǐng)求上下文中的參數(shù)自動(dòng)識(shí)別業(yè)務(wù)身份并將請(qǐng)求路由到相應(yīng)的能力實(shí)例上進(jìn)行處理。
??
能力編排操作的是能力節(jié)點(diǎn)而不是領(lǐng)域能力實(shí)例,這樣可以讓能力實(shí)例更靈活的進(jìn)行橫向拓展(點(diǎn)擊放大查看)
上文提到了能力編排和路由機(jī)制都已經(jīng)在新工程中提供了框架化的實(shí)現(xiàn),本文主要是為了分享我們架構(gòu)設(shè)計(jì)的動(dòng)機(jī),所以不會(huì)介紹這些功能的實(shí)現(xiàn)原理和使用方法,對(duì)此感興趣的同學(xué)可以觀看能力編排框架專(zhuān)門(mén)的視頻教程:https://cf.jd.com/pages/viewpage.action?pageId=954674772?
標(biāo)準(zhǔn)的業(yè)務(wù)執(zhí)行模版
在第二、三代架構(gòu)中,系統(tǒng)處理請(qǐng)求時(shí)會(huì)先執(zhí)行全部參數(shù)的校驗(yàn),校驗(yàn)通過(guò)后再將單元新建處理所需的全部數(shù)據(jù)對(duì)象查詢(xún)出來(lái)。在這個(gè)過(guò)程中可以充分利用批量查詢(xún)接口提升系統(tǒng)性能,同時(shí)也會(huì)對(duì)查詢(xún)出來(lái)的數(shù)據(jù)對(duì)象進(jìn)行校驗(yàn),如果存在不合法的數(shù)據(jù)則終止處理流程,如果數(shù)據(jù)對(duì)象查詢(xún)一切正常,則執(zhí)行后續(xù)的數(shù)據(jù)組裝和處理邏輯,最后批量執(zhí)行數(shù)據(jù)的持久化。盡管會(huì)存在上文分析的一些問(wèn)題,但是這種模式所帶來(lái)的收益依然具備十分重要的意義。
然而在新架構(gòu)中我們將原先連貫的業(yè)務(wù)邏輯打散,按照邏輯的內(nèi)聚性將他們重組到一個(gè)能力實(shí)例中,然后在領(lǐng)域服務(wù)中通過(guò)能力編排將這些能力實(shí)例組裝成一個(gè)完整的業(yè)務(wù)流程。這雖然貫徹了組合復(fù)用的原則,但是如果我們只是簡(jiǎn)單地通過(guò)順序執(zhí)行多個(gè)能力實(shí)例來(lái)組裝領(lǐng)域服務(wù),那么由于每個(gè)能力內(nèi)部又依次執(zhí)行與一小撮業(yè)務(wù)屬性相關(guān)的參數(shù)校驗(yàn)、依賴(lài)數(shù)據(jù)查詢(xún)、邏輯處理乃至數(shù)據(jù)持久化操作,從代碼邏輯的執(zhí)行流程上看我們又回退到了“數(shù)據(jù)訪問(wèn)與邏輯處理互相交織”的第一代架構(gòu)上。除此之外,雖然服務(wù)之間邏輯上互相獨(dú)立,但是他們可能會(huì)依賴(lài)相同的數(shù)據(jù)對(duì)象,比如人群包的綁定與預(yù)算調(diào)整兩個(gè)能力都會(huì)依賴(lài)AdGroup對(duì)象,如果框架只是簡(jiǎn)單地串聯(lián)執(zhí)行這兩個(gè)能力,那么必然會(huì)造成數(shù)據(jù)的重復(fù)查詢(xún)。
為了解決上述問(wèn)題,我們引入了標(biāo)準(zhǔn)的業(yè)務(wù)流程Executor模板,它把業(yè)務(wù)業(yè)務(wù)流程抽象為:參數(shù)校驗(yàn)、上下文初始化、上下文校驗(yàn)、業(yè)務(wù)邏輯處理、數(shù)據(jù)持久化、發(fā)布事件幾個(gè)標(biāo)準(zhǔn)步驟,不論是領(lǐng)域能力的封裝還是領(lǐng)域服務(wù)的實(shí)現(xiàn)都必須繼承該模板。標(biāo)準(zhǔn)業(yè)務(wù)執(zhí)行模板的引入一方面能夠規(guī)范開(kāi)發(fā)者的設(shè)計(jì)和實(shí)現(xiàn),另一方面也將代碼邏輯的串聯(lián)執(zhí)行權(quán)從開(kāi)發(fā)者手中轉(zhuǎn)移到了能力編排框架中,讓框架能夠?qū)崿F(xiàn)邏輯的自動(dòng)重組和執(zhí)行,而開(kāi)發(fā)者專(zhuān)注于業(yè)務(wù)邏輯并進(jìn)行填空式開(kāi)發(fā)。而框架在獲取到了代碼邏輯的串聯(lián)執(zhí)行權(quán)之后就可以在領(lǐng)域服務(wù)的每個(gè)標(biāo)準(zhǔn)步驟中按照能力編排執(zhí)行圖組裝調(diào)用的各個(gè)能力實(shí)例中相應(yīng)標(biāo)準(zhǔn)步驟,從而將打散到不同能力實(shí)例中的業(yè)務(wù)邏輯次按照標(biāo)準(zhǔn)步驟的類(lèi)別還原回連貫完整的業(yè)務(wù)邏輯,如下圖所示:
??
標(biāo)準(zhǔn)業(yè)務(wù)流程模版的引入讓框架進(jìn)行業(yè)務(wù)流程還原成為可能
除了實(shí)現(xiàn)業(yè)務(wù)邏輯按標(biāo)準(zhǔn)步驟自動(dòng)還原之外,由于標(biāo)準(zhǔn)流程模板對(duì)每一個(gè)標(biāo)準(zhǔn)步驟方法的執(zhí)行參數(shù)、依賴(lài)的上下文及返回值對(duì)象都進(jìn)行了通用化的抽象,能力編排框架也得以在各個(gè)能力標(biāo)準(zhǔn)步驟調(diào)用之間插入?yún)?shù)及上下文的映射和傳遞邏輯,從而在不同能力之間以及能力與領(lǐng)域服務(wù)之間實(shí)現(xiàn)數(shù)據(jù)分發(fā)和共享。需要說(shuō)明的是盡管這些流程都可以采用默認(rèn)的自動(dòng)處理規(guī)則,開(kāi)發(fā)者也可以通過(guò)能力編排框架提供的DSL對(duì)默認(rèn)的串聯(lián)執(zhí)行、數(shù)據(jù)傳遞、異常處理等規(guī)則進(jìn)行修改。
在我們新架構(gòu)中,我們通過(guò)領(lǐng)域能力拆分將復(fù)雜的問(wèn)題域正交分解為多個(gè)互相獨(dú)立的最小問(wèn)題域,讓設(shè)計(jì)者可以分而治之,逐個(gè)擊破,降低了問(wèn)題的復(fù)雜度和設(shè)計(jì)成本,同時(shí)單個(gè)能力節(jié)點(diǎn)下不同業(yè)務(wù)場(chǎng)景下的業(yè)務(wù)邏輯被分離到了不同的領(lǐng)域能力實(shí)例中,避免出現(xiàn)不同業(yè)務(wù)場(chǎng)景互相交織,便于快速梳理業(yè)務(wù)邏輯,定位改動(dòng)點(diǎn),這些都體現(xiàn)了“設(shè)計(jì)易拓展”的設(shè)計(jì)目標(biāo)。
由于拆分出來(lái)的各個(gè)能力節(jié)點(diǎn)彼此正交,內(nèi)部邏輯十分內(nèi)聚,因此可以在各自的維度上進(jìn)行迭代,比如同樣是在單元維度下的出價(jià)設(shè)置和人群設(shè)置能力就分別在出價(jià)類(lèi)型和人群類(lèi)型這兩個(gè)場(chǎng)景維度上各自進(jìn)行路由,避免了不同場(chǎng)景互相交織帶來(lái)的圈復(fù)雜度上升問(wèn)題,也能夠更加靈活在不同的業(yè)務(wù)場(chǎng)景中實(shí)現(xiàn)能力復(fù)用。同時(shí)由于我們的業(yè)務(wù)本質(zhì)上就是對(duì)物料的創(chuàng)編,物料新建流程中的能力往往可以直接在物料修改流程中復(fù)用。還有一個(gè)特殊的場(chǎng)景就是批量物料操作類(lèi)型的請(qǐng)求,借助能力編排框架提供的循環(huán)編排和數(shù)據(jù)共享機(jī)制,我們可以在領(lǐng)域服務(wù)的開(kāi)始先批量完成所需數(shù)據(jù)的查詢(xún),然后通過(guò)循環(huán)編排機(jī)制循環(huán)復(fù)用單個(gè)請(qǐng)求處理能力中的純內(nèi)存調(diào)用的數(shù)據(jù)校驗(yàn)及數(shù)據(jù)處理邏輯,最后在批量操作領(lǐng)域服務(wù)中批量完成聚合根對(duì)象集合的寫(xiě)入,在實(shí)現(xiàn)邏輯復(fù)用的同時(shí)又能保證數(shù)據(jù)準(zhǔn)確性及性能,以上特性都體現(xiàn)了“代碼易復(fù)用”的設(shè)計(jì)目標(biāo)。
領(lǐng)域能力的編排邏輯提供了一個(gè)業(yè)務(wù)流程的全景視圖,當(dāng)有新同學(xué)加入時(shí),可以迅速通過(guò)閱讀能力編排邏輯快速建立起對(duì)業(yè)務(wù)的宏觀認(rèn)知,再結(jié)合在第三代架構(gòu)中引入的聚合機(jī)制,可以讓新同學(xué)快速熟悉數(shù)據(jù)模型與業(yè)務(wù)流程。同時(shí)通過(guò)路由機(jī)制系統(tǒng)中全部的業(yè)務(wù)規(guī)則打包拆分成數(shù)量有限且邊界清晰的能力節(jié)點(diǎn),當(dāng)需要快速梳理需求點(diǎn)對(duì)應(yīng)業(yè)務(wù)規(guī)則時(shí),可以由粗及細(xì),先確定需求點(diǎn)歸屬的能力節(jié)點(diǎn),然后根據(jù)場(chǎng)景定位到具體的能力實(shí)例,進(jìn)而可以從代碼中獲取業(yè)務(wù)規(guī)則,這些特性都體現(xiàn)了“邏輯易傳承”的設(shè)計(jì)目標(biāo)。
??
基于能力拆分與編排的代碼架構(gòu),最顯著的收益就是同一個(gè)能力可以在不同的領(lǐng)域服務(wù)中直接復(fù)用(點(diǎn)擊放大查看)
總結(jié)
以上便是我們?yōu)閷?shí)現(xiàn)新架構(gòu)所進(jìn)行的種種嘗試,這些設(shè)計(jì)是否正確我們也正在通過(guò)需求實(shí)戰(zhàn)來(lái)進(jìn)行驗(yàn)證,把他們發(fā)出來(lái)不是要說(shuō)服大家認(rèn)同,而是想通過(guò)對(duì)架構(gòu)演進(jìn)歷程的推演幫助大家更好的理解我們新架構(gòu)中各項(xiàng)功能的設(shè)計(jì)動(dòng)機(jī),從而更快的上手進(jìn)行開(kāi)發(fā);另一方面也希望能夠激發(fā)大家的思考和討論,哪怕是對(duì)上述方案的質(zhì)疑和批判,一個(gè)好的架構(gòu)一定是在一次次批評(píng)聲中改進(jìn)出來(lái)的,我至今還在懷念當(dāng)初摸索新架構(gòu)時(shí)那些與永亮(我的良師益友,部門(mén)內(nèi)探索中臺(tái)化及領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)思想的第一人)爭(zhēng)論到凌晨2、3點(diǎn)的日子。
審核編輯 黃宇
-
DSL
+關(guān)注
關(guān)注
2文章
61瀏覽量
38712 -
數(shù)據(jù)模型
+關(guān)注
關(guān)注
0文章
52瀏覽量
10185 -
架構(gòu)
+關(guān)注
關(guān)注
1文章
528瀏覽量
25992
發(fā)布評(píng)論請(qǐng)先 登錄
DSP系統(tǒng)的技術(shù)架構(gòu)圖
京東IM工具的架構(gòu)演進(jìn)

代碼質(zhì)量和其整潔度成正比有什么道理如何進(jìn)行代碼整潔教材免費(fèi)下載
AI“理性”邏輯,如何幫助廣告營(yíng)銷(xiāo)實(shí)現(xiàn)精準(zhǔn)投放
人工智能算法如何為廣告服務(wù)
Google將不會(huì)投放任何與選舉相關(guān)的廣告
京東金融APP就短視頻廣告爭(zhēng)議正式致歉
京東再次為低俗廣告道歉 京東金融低俗借貸廣告被吐槽
蘋(píng)果iPhone隱私保護(hù)功能或限制廣告投放
液晶廣告機(jī)的投放優(yōu)勢(shì)?
ES 集群架構(gòu)演進(jìn)之路
廣告投放公司運(yùn)用大數(shù)據(jù)分析,實(shí)現(xiàn)精準(zhǔn)投放
特斯拉在馬斯克的社交平臺(tái)X平臺(tái)投放廣告
大促高并發(fā)系統(tǒng)性能優(yōu)化實(shí)戰(zhàn)--京東聯(lián)盟廣告推薦系統(tǒng)

評(píng)論