柚子快報(bào)激活碼778899分享:業(yè)務(wù)系統(tǒng)架構(gòu)實(shí)踐總結(jié)
柚子快報(bào)激活碼778899分享:業(yè)務(wù)系統(tǒng)架構(gòu)實(shí)踐總結(jié)
我從2015年起至今2022年,在業(yè)務(wù)平臺(結(jié)算、訂購、資金)、集團(tuán)財(cái)務(wù)平臺(應(yīng)收應(yīng)付、賬務(wù)核算、財(cái)資、財(cái)務(wù)分析、預(yù)算)、本地生活財(cái)務(wù)平臺(發(fā)票、結(jié)算、預(yù)算、核算、稽核)所經(jīng)歷的業(yè)務(wù)系統(tǒng)研發(fā)實(shí)踐的一個(gè)總結(jié)。
1.核心是面向復(fù)雜性業(yè)務(wù)支撐的實(shí)踐經(jīng)驗(yàn)(個(gè)人概念里的“復(fù)雜業(yè)務(wù)“,大概至少面向5類行業(yè)若干業(yè)務(wù)線且業(yè)態(tài)差異很大),文章不涉及性能、穩(wěn)定性、資損防控、大數(shù)據(jù)離線研發(fā),聚焦在線業(yè)務(wù)系統(tǒng)架構(gòu)對多態(tài)業(yè)務(wù)的包容性、開放性、靈活性、可讀性。
2.文章較多強(qiáng)調(diào)”個(gè)人”兩字,因?yàn)閮H是我個(gè)人在實(shí)踐上歸納總結(jié)的一些方式方法。
3.實(shí)踐經(jīng)驗(yàn)主要來自兩類,一類是接手舊系統(tǒng),得以見識不一樣的設(shè)計(jì),文中“見過”特指。二類是自己新建的,之后又一而再三重構(gòu)的,這類姿勢下,人的反思記憶尤為深刻。
作者:關(guān)斌(修覺)
一、單系統(tǒng)內(nèi)架構(gòu)形態(tài)(反面)
下面是個(gè)人經(jīng)歷過的反面案例
1.1?業(yè)務(wù)層臃腫,能力層單薄
這類形態(tài)挺常見的,最初設(shè)計(jì)時(shí)做四層劃分,也比較清晰。最核心的設(shè)計(jì)點(diǎn)是,biz層編排“可復(fù)用的”service層,完成一個(gè)場景邏輯表達(dá)。
問題在于:
第一:Service本身的劃分、定位,相對隨意,從第一感覺而來,并未經(jīng)過領(lǐng)域劃分這樣的設(shè)計(jì)。所以這里特意叫service,從而區(qū)別于domain的表達(dá),我采用的領(lǐng)域劃分方法下文會闡述;
第二:Service本身不可擴(kuò)展,多態(tài)業(yè)務(wù)沖擊下,為適配此service能力而存在的個(gè)性向共性的轉(zhuǎn)換邏輯上浮。
因?yàn)檫@些問題的存在隨更多業(yè)務(wù)接入的架構(gòu)演進(jìn)下,組織擴(kuò)大后的人員差異擴(kuò)大下,會導(dǎo)致幾個(gè)問題:
biz層越發(fā)的膨脹,service層越發(fā)的萎縮。biz層里充斥了各種本該往下沉淀的可復(fù)用業(yè)務(wù)邏輯,service層則幾乎萎縮為dao; 人員的差異下,service實(shí)例顆粒度不一(有萎縮有膨脹),有重復(fù)相似的。biz實(shí)例亦是如此; 由于biz膨脹,層內(nèi)會發(fā)展為兩小層,上小層為面向單一業(yè)務(wù)場景的“業(yè)務(wù)biz層”,下小層為通用可復(fù)用場景的“通用biz層”,且這兩層隱約存在,隨研發(fā)個(gè)體認(rèn)知差異或隱或現(xiàn)。由于service的萎縮,biz層會干脆直接調(diào)用dao,且因人而異的隨機(jī)性差異,導(dǎo)致biz往下的調(diào)用關(guān)系呈亂麻態(tài)。
這個(gè)形態(tài),我印象最深的是2015年在結(jié)算研發(fā)時(shí),經(jīng)歷過。當(dāng)時(shí)設(shè)計(jì)理念是,biz層通過hjbpm編排底層可復(fù)用的service,表達(dá)業(yè)務(wù)邏輯,甚至試圖以可視化界面讓業(yè)務(wù)運(yùn)營自編排,當(dāng)時(shí)的設(shè)計(jì)理念和初衷都是很好的。在經(jīng)歷大規(guī)模多態(tài)業(yè)務(wù)接入的沖擊下,biz層產(chǎn)生了嚴(yán)重的膨脹,導(dǎo)致最復(fù)雜最難懂最易出問題的邏輯都堆積在biz層。我們大概在2016年做了分層、分域、SPI式開放化架構(gòu)升級。
1.2?service間網(wǎng)狀調(diào)用
這類形態(tài)也挺常見的。同樣,核心的問題還是對service這層的顆粒度、職責(zé)定位不清晰,對增量service的架構(gòu)監(jiān)管不足,業(yè)務(wù)壓力下(禍起于常見的倒排需求,常以需求完整度妥協(xié)+架構(gòu)方案妥協(xié)追趕deadline死線,挖下?lián)p害長期利益的坑),一線研發(fā)同學(xué)就容易憑感覺去新增service。
與上文形態(tài)1不同的是,該組研發(fā)未明確和共識biz層的“編排”作用,基于原本bizA -> serviceA 的實(shí)現(xiàn)鏈路下,隨新增業(yè)務(wù)邏輯新起的serviceB(事是A的事,但不合適放serviceA,所以新建),鏈路演變成了bizA -> serviceA -> serviceB。
這樣的趨勢持續(xù)發(fā)展下去,會發(fā)現(xiàn)bizA下的service調(diào)用鏈路越發(fā)的復(fù)雜,呈現(xiàn)為一顆深度調(diào)用樹,而biz層失去了業(yè)務(wù)編排的作用退化為一個(gè)業(yè)務(wù)場景入口的標(biāo)志符。從代碼閱讀層面講,以為是做A (因?yàn)閎izA -> serviceA的起手勢),實(shí)則帶著一串B+C+D+E等等,且越挖越深,不見盡頭。
一個(gè)新同學(xué)除非步履維艱的推演完成全串調(diào)用代碼,否則不能知曉bizA業(yè)務(wù)場景真實(shí)完全的意義。步履維艱在于,這是一顆深度調(diào)用樹,當(dāng)你閱讀到serviceE時(shí),很可能你已經(jīng)忘記了最初是從哪個(gè)biz入口看過來的,以及接下來你將還會遇到哪些service講述剩余的業(yè)務(wù)邏輯。
而一個(gè)寫的并不那么好的代碼,還常將這些service的調(diào)用關(guān)系,隱藏在極其隱晦的角落,給閱讀者增加復(fù)雜。譬如通過一個(gè)叫util的類,完成了service B -> C這一主干鏈路的傳遞。一個(gè)service的改動,幾乎無法評估其產(chǎn)生的影響面。而因?yàn)闊o法評估,又怕改出故障,在業(yè)務(wù)需求壓力下,會出現(xiàn)各類fork行為加劇加速腐化。這是惡性循環(huán)。
1.3?混合態(tài)
真實(shí)世界里,1、2兩種形態(tài)會同時(shí)發(fā)生,事實(shí)上都是混合態(tài),只是問題偏重度不同,分開闡述便于理解。
二、單系統(tǒng)內(nèi)架構(gòu)形態(tài)(采用)
個(gè)人在實(shí)踐里采用的形態(tài):
看起來似乎和前面列的案例沒大區(qū)別,但在實(shí)踐結(jié)果上是有差異的。個(gè)人認(rèn)為核心有2個(gè)點(diǎn):
起步時(shí)的domain設(shè)計(jì):有理論支撐的domain劃分和憑經(jīng)驗(yàn)感覺劃分的service,對整個(gè)架構(gòu)生命力的影響是完全不同的; 過程中的架構(gòu)原則:必須有幾個(gè)易記牢記并堅(jiān)持貫徹的架構(gòu)原則(見下文的實(shí)踐細(xì)節(jié)),在實(shí)操層面上,對整個(gè)架構(gòu)性命的維持是完全不同的。
2.1?細(xì)項(xiàng)1 - 分層
??2.1.1?4層定位:
api層:我比較更喜歡叫應(yīng)用服務(wù)層(因?yàn)锳PI其實(shí)是個(gè)協(xié)議范疇的語義,非實(shí)現(xiàn)),面向應(yīng)用層面對外的服務(wù)表達(dá)。靈活為主,薄,可隨業(yè)務(wù)、渠道定義獨(dú)立API; biz層:業(yè)務(wù)層,面向一種業(yè)務(wù)場景的邏輯表達(dá),如下單。負(fù)責(zé)業(yè)務(wù)編排,一個(gè)業(yè)務(wù)場景的主流程應(yīng)在這層直觀可視。靈活為主,薄,可特定場景獨(dú)立biz類; domain層:領(lǐng)域服務(wù)層,圍繞一簇模型操作的邏輯表達(dá),例如費(fèi)用單域、賬單域。核心能力的所在地,要規(guī)范、要厚、沉淀復(fù)用為主,通過SPI兼顧業(yè)務(wù)個(gè)性化; dao層:存儲層,面向一個(gè)存儲對象操作的邏輯表達(dá),無論是內(nèi)部存儲 or 外部存儲(如rpc外調(diào))。靈活為主,可隨db差異、性能需求獨(dú)立dao方法。
?
??2.1.2?實(shí)踐中的細(xì)節(jié)問題
api和biz層冗余了?不是
語義上,api層是站在應(yīng)用的角度與外部應(yīng)用交互約定的實(shí)現(xiàn),是向外表達(dá)。biz層是應(yīng)用承載業(yè)務(wù)里的某一類場景,是向內(nèi)表達(dá)。雖然大多數(shù)情況下,兩者是1:1的關(guān)系,且api層幾乎薄到只做透傳,但畢竟語義不同,也會出現(xiàn)N:M的情況。
舉例
業(yè)務(wù)線共用API A,然為B業(yè)務(wù)專開API B(如考慮其鑒權(quán)體系特異),因服務(wù)場景相同(如下單)以同一BIZ A表達(dá),此刻API 和BIZ = N:1; 兩個(gè)服務(wù)場景,一個(gè)叫新增(如新建用戶),一個(gè)叫更新(如更新用戶資料),兩個(gè)場景的流程、邏輯截然不同,故為兩個(gè)BIZ。應(yīng)用服務(wù)層面,上游調(diào)用方希望”若有則更新、若無則創(chuàng)建“的語義,故而是一個(gè)API(假設(shè)上游強(qiáng)勢,且不愿自行編排)。此刻API和BIZ = 1:N; 前兩類同時(shí)發(fā)生,則為N:M關(guān)系。
所以個(gè)人觀點(diǎn)是,api層并不冗余,雖然常見較薄,依然尤其獨(dú)立的職責(zé)。譬如特殊業(yè)務(wù)的定制API、同一業(yè)務(wù)不同渠道(PC、APP)不同API、同一業(yè)務(wù)權(quán)限控制力度不同的API。
?
能否跨層調(diào)用dao?可以
2016年在做集團(tuán)結(jié)算的重構(gòu)設(shè)計(jì)時(shí),個(gè)人的理念是,四層應(yīng)有嚴(yán)格的次第順序,上層只可見到直接下層,跨層是萬萬不可的,否則就講不清楚架構(gòu)規(guī)范了,否則就會導(dǎo)致調(diào)用關(guān)系混亂,否則本該沉淀在domain的邏輯因biz可直連dao而錯(cuò)放了位置,等等擔(dān)心。
近幾年,反問“為什么一定不能跨層調(diào)用?”,覺得就算跨層調(diào)用,四層還是那四層,只要每層的職責(zé)定位清晰。反之,縱使跨層調(diào)用被禁止,落地上依然會從分層合理性的切入點(diǎn)腐化架構(gòu)。
實(shí)踐上看,”禁止跨層“會導(dǎo)致很多變形的動作出來。
舉例:query and do something,依據(jù)A的存在與否做后續(xù)邏輯的判斷,這是biz層里十分常見的片段。而query往往僅是一個(gè)daoA里最簡單的sql,無復(fù)雜邏輯無業(yè)務(wù)屬性。在”禁止跨層“禁令下,則只能在domain里新添類似proxy的服務(wù)供biz層使用,這是所謂的”變形“,不僅大大增加了程序員的無效代碼量,更使domain里充斥了無關(guān)代碼(至于什么是應(yīng)該放domain里的有關(guān)代碼,下文在”領(lǐng)域設(shè)計(jì)“里會涉及)。
此例僅是biz跨到dao的問題,若推想至api里需以查詢daoB做鑒權(quán)之類的判斷依據(jù)時(shí),則更要憑空專造一個(gè)biz場景出來,那就越發(fā)的怪異了。
因此個(gè)人會選擇放開跨層調(diào)用。本質(zhì)還是四層的職責(zé)定位要清晰,過程中的架構(gòu)維護(hù)要遇歪立扶,是要靠人和機(jī)制的。
?
代碼該放哪層?厚domain薄biz
首先問題大致出現(xiàn)在biz和domain這兩層,因?yàn)楹喲灾琣pi和dao這一頂一底是不該帶業(yè)務(wù)邏輯的,故糾結(jié)點(diǎn)集中在中間這兩層。當(dāng)然,我也見過把a(bǔ)pi層業(yè)務(wù)化甚至頂替了biz層的,也見過dao層業(yè)務(wù)化之下冗長難懂的sql,但都是極少數(shù)。
然后定義下,什么叫個(gè)人認(rèn)為的“復(fù)雜業(yè)務(wù)邏輯”。
本文起頭就提到“至少面向5類行業(yè)若干業(yè)務(wù)線且業(yè)態(tài)差異很大”,在此之下是流程、邏輯分支、模型、業(yè)務(wù)個(gè)性化的規(guī)模性爆炸,各業(yè)務(wù)線交織在龐大的邏輯空間中,從業(yè)務(wù)角度看很難看清單一業(yè)務(wù)在此空間中占據(jù)的全貌,從空間的組成單元看很難看清多少個(gè)業(yè)務(wù)依托其上牽一發(fā)而動全身,這個(gè)叫做“雜”,非沉心梳理而不得全。
局部邏輯上,如周期訂購類業(yè)務(wù)在訂退升降下的剩余價(jià)值計(jì)算,營銷類業(yè)務(wù)的優(yōu)惠價(jià)格計(jì)算,結(jié)算類業(yè)務(wù)的賬期賬齡切賬調(diào)賬票款匹配,其本身呈現(xiàn)在PRD里就很復(fù)雜,這個(gè)叫做"燒腦“,燒的是思維邏輯的縝密性。
回到正題,這些復(fù)雜業(yè)務(wù)邏輯,在biz和domain里,擺放的姿勢,一句話講就是”biz編排domain service“實(shí)現(xiàn)業(yè)務(wù)表達(dá)。
細(xì)講開去,有2個(gè)點(diǎn):
從系統(tǒng)架構(gòu)內(nèi)觀的角度看,必須要先有domain再有biz,domain承載了該應(yīng)用最核心的業(yè)務(wù)能力,好的架構(gòu)里,依domain代碼逐個(gè)閱讀便能鳥瞰該應(yīng)用的核心能力,厚domain薄biz,domain要敦實(shí)、包容、開放; biz是面向場景的,核心是復(fù)用下面的domain搭建出一個(gè)業(yè)務(wù)場景,講究靈活。biz是面向場景的一個(gè)動詞視角,domain是面向一簇模型的名詞視角,場景必發(fā)散,模型需收斂,動詞表靈動,名詞代沉穩(wěn),這是核心的差異。所以,如前文之例,切勿出現(xiàn)common biz這類在biz層做大復(fù)用的事情。
倘若兩個(gè)場景間有同性的,且不歸屬于domain、util、infra范疇的,個(gè)人觀點(diǎn)是寧肯做一定的代碼冗余。biz管好自己是第一位,妄求復(fù)用反倒復(fù)雜化了。
?
能否跨域調(diào)用dao?可以
首先解釋下問題,即,dao何來跨域一說?由于domain是從模型間關(guān)聯(lián)度的角度切割而來的,一簇緊密相關(guān)的模型確定一個(gè)domain,反過來說即一個(gè)模型歸屬于某個(gè)domain。而dao又是對模型存儲操作的表達(dá),推論出dao亦歸屬于某個(gè)域,故而domain調(diào)用dao時(shí)必有是否跨域一說。
我?guī)状温犨^“不可跨域訪問dao”的說法,dao只允許被所屬的domain訪問,跨域則生亂,如同跨層一般,乃大忌。這個(gè)論調(diào),很容易想當(dāng)然的被理解、認(rèn)可和接受。然遇事之后,細(xì)想之下,便會反思。
?
舉個(gè)例子:結(jié)算系統(tǒng)里,會有費(fèi)用單、對賬單、客商這些概念和域。對賬單生成,這個(gè)領(lǐng)域服務(wù)里,需要訪問客商db表check存在性(簡單的pk query sql),變更上一節(jié)點(diǎn)模型的狀態(tài)(此處是費(fèi)用單)。
這類跨域的操作,可以在biz層做編排,如custom.isExist(), reconBill.create(), feeBill.updateState(),是可以實(shí)現(xiàn)的。
但是,個(gè)人觀點(diǎn)里,檢查客戶存在性、生成對賬單、變更費(fèi)用單狀態(tài),這三個(gè)動作,本就是“對賬單生成領(lǐng)域服務(wù)”不可或缺的組成,因在一個(gè)領(lǐng)域服務(wù)里完整表達(dá),而不僅僅是對賬單dao insert的封裝。而biz層所應(yīng)承擔(dān)的”編排“,需在更大顆粒度上,譬如”生成對賬單“ 并 ”生成應(yīng)收發(fā)票“。
所以在“厚domain薄biz”的理念下,domain勢必要對”隔壁域"的存儲對象做些簡單的無業(yè)務(wù)邏輯的操作。
澄清一點(diǎn),不論是《跨層調(diào)用》里我舉例的biz層下跳dao,還是此處domain的橫跨dao,都僅限于“簡單操作”。倘若是復(fù)雜操作,如涉及狀態(tài)圖控制、值枚舉控制、值映射控制、對象狀態(tài)變更的對外通知等業(yè)務(wù)層邏輯轉(zhuǎn)換之后才能做的存儲操作,是必須收口到對應(yīng)的domain的。而至于簡單 or 復(fù)雜的明確定義,說實(shí)話我是定義不出來的,但又不想因噎廢食,所以我站在了“厚domain薄biz”架構(gòu)原則這一面,而對dao合理調(diào)用控制這一面就得靠執(zhí)行中的架構(gòu)把控了。
再補(bǔ)充一個(gè)觀點(diǎn),上文舉例“service間網(wǎng)狀調(diào)用”使得下層呈現(xiàn)盤根錯(cuò)節(jié)的形態(tài),不好,所以要按域垂直分桶隔離。而此處,dao不用按域分桶隔離,是因?yàn)閐ao之間是不會橫向互調(diào)的,它是非常獨(dú)立的個(gè)體。某種角度講,dao像infra層,僅是錘子、起子、鉆頭這樣的工具,功能單一人皆可用,只要保證使用者domain層守規(guī)矩,dao作為工具可盡量靈活。
?
事務(wù)放哪一層?視所在業(yè)務(wù)特性而定,同一應(yīng)用內(nèi)需持一套標(biāo)準(zhǔn)
這個(gè)問題核心還是biz層和domain層的選擇,把事務(wù)控制放在api頂層或是dao底層,我是未曾見過的。
討論前提是,我在架構(gòu)設(shè)計(jì)里一個(gè)biz層的函數(shù)調(diào)用,僅限于一個(gè)rpc同步調(diào)用的內(nèi)存函數(shù)流,而非異步工作流。在此前提下,倘若biz和domain是1:1的情況(也是大多數(shù)情況),事務(wù)控制在上還是下,區(qū)別不大(因?yàn)閎iz本就很?。?。
倘若是1:n的情況(編排若干個(gè)domain服務(wù)),放biz則有大事務(wù)的性能風(fēng)險(xiǎn),放domain則有一致性風(fēng)險(xiǎn),需視所在行業(yè)特性決定了。
我近幾年都在財(cái)務(wù)信息化研發(fā),性能挑戰(zhàn)相對小些,所以從架構(gòu)的簡約性考慮,留在biz層做事務(wù)控制。倘使有極個(gè)別協(xié)調(diào)多域成為大事務(wù)而RT不可接受的,把長流程biz拆散并以事務(wù)內(nèi)消息(本地DB)進(jìn)行串聯(lián),當(dāng)然這會犧牲biz在語義上的完整性,個(gè)案下是個(gè)妥協(xié)性方案。
譬如交易,需要編排商品、庫存、營銷、履約等完成一個(gè)下單,且都是rpc外部調(diào)用,即使是一個(gè)同步調(diào)用的內(nèi)存函數(shù)流,也沒法在biz層做事務(wù)控制,只能下放到單域服務(wù)內(nèi)部,并以重試補(bǔ)償?shù)葯C(jī)制保證一致性。
同一個(gè)應(yīng)用內(nèi)部,須是同一標(biāo)準(zhǔn),忽而在biz層控制事務(wù),忽而在domain層控制事務(wù),不可。
2.2?細(xì)項(xiàng)2 - 領(lǐng)域
領(lǐng)域設(shè)計(jì),最核心的是劃分,切的位置、切出來的大小,極大的決定了一個(gè)架構(gòu)的生命長度。
個(gè)人在方法論上,最核心就兩步走:
畫全局模型圖,確定網(wǎng)狀交錯(cuò)的關(guān)聯(lián)關(guān)系,找出耦合度最高的一簇圈為一個(gè)域,從語義的角度確定每個(gè)域的職責(zé)和服務(wù)。 按場景逐個(gè)畫泳道圖推演調(diào)用關(guān)系,檢查調(diào)用的合理性、域定義的完整度和內(nèi)聚度。
當(dāng)然,從教科理論上講,最前面還欠缺了一步“按user case + 魯棒圖推演出核心模型”。略過這一步是在實(shí)操層面上,這種系統(tǒng)性的架構(gòu)設(shè)計(jì)幾乎全都發(fā)生在一個(gè)業(yè)務(wù)系統(tǒng)已經(jīng)發(fā)展了若干年,而非成立之初。對應(yīng)研發(fā)同學(xué)憑經(jīng)驗(yàn)感覺就能羅列出核心模型,且當(dāng)時(shí)架構(gòu)的核心矛盾不在模型完整度而在于分層分域的問題。所以這是實(shí)操上的變通簡化。
??2.2.1?一個(gè)反面案例:
上面以模型驅(qū)動的領(lǐng)域切分方式,看起來似乎順理成章,本該如此,理所當(dāng)然的事情,似乎沒那么復(fù)雜。這里給一個(gè)2017年自己經(jīng)歷的案例,走野路沒摸到道前,會有糾結(jié)點(diǎn)的。
背景基于資金平臺,系統(tǒng)可以理解為一個(gè)資金類的營銷系統(tǒng)(如紅包、儲值卡、積分等等),營銷的業(yè)務(wù)本來就邏輯復(fù)雜,系統(tǒng)也因業(yè)務(wù)拓展的速度而有腐化,還有收并的系統(tǒng)亟待整合,故而打算做2.0的架構(gòu)升級。
這是對當(dāng)時(shí)架構(gòu)形態(tài)一個(gè)極簡版的表達(dá),舍去了預(yù)算等其它域,聚焦在對賬戶操作這個(gè)點(diǎn)上,便于表達(dá)案例的關(guān)鍵點(diǎn)。
紅包、儲值卡、積分這些金本位的營銷工具的表達(dá),且屬性各不相同。例如紅包是平臺or商家直發(fā)給c用戶的,積分是隨單購買返給c用戶的,儲值卡是用戶自行開通并充值的。但在隨單消耗時(shí),又是相通的,按序或等比例作用于訂單抵扣原應(yīng)付金額; 發(fā)放、充值、消耗、退回,這些場景,是各個(gè)營銷工具都可能涉及到的; 有系統(tǒng)整合的訴求,如xx積分系統(tǒng)。
謬解一,按產(chǎn)品劃域
這是想到的一種解法,考慮各工具的差異性,就劃分為獨(dú)立的域了。當(dāng)時(shí)這種想法,有個(gè)潛在的思維導(dǎo)向是,產(chǎn)品架構(gòu)設(shè)計(jì)和產(chǎn)品表達(dá)上,就是這么歸類的。
這種方案下,問題有:
幾種資金工具雖有差異,然亦有極大的相同點(diǎn)(如隨單消耗),且不能排除差異變相同(例如在發(fā)展后期,紅包亦可充值),分開表達(dá)會有很大的邏輯重復(fù); 不穩(wěn)定。紅包、儲值卡、積分,本質(zhì)是產(chǎn)品層面的語義,隨時(shí)可能發(fā)展出新的類別(譬如會員卡,即有儲蓄功能、亦有會員優(yōu)惠功能、還有充值送功能),也可能將現(xiàn)有的形態(tài)做整合; 性能問題,如下單抵扣這個(gè)鏈路,雙十一時(shí),biz的隨單抵扣消耗場景,編排底下各域的領(lǐng)域服務(wù)進(jìn)行消耗,性能上走不通。
?
謬解二,按場景劃域
這是想到的另一種解法,這里思考的突破是,資金工具的分類僅是產(chǎn)品層面的語義,不能以此為系統(tǒng)架構(gòu)設(shè)計(jì)的要素,而其差異性應(yīng)以邏輯擴(kuò)展的方式表達(dá)。而此處用發(fā)放、充值、消耗這些場景來切割域,思維邏輯上是認(rèn)為資金工具的操作估摸也就這幾個(gè)大類了,以此切分,能完整涵蓋整域的表達(dá)。
這種方案下,問題有:
biz和domain的定位更模糊了:譬如紅包發(fā)放和積分發(fā)放,其流程和邏輯是不同的,故而在biz單獨(dú)出兩個(gè)場景。然而,這樣兩個(gè)流程上都全然不同的事情,就能夠在發(fā)放domain里歸一嗎?顯然會很變扭,要么就是發(fā)放domain做的很薄,邏輯都上浮到biz層,要么就是domain內(nèi)部其實(shí)有兩套完全獨(dú)立的邏輯和流程來承載上面兩個(gè)截然不同的場景biz,總之domain和biz的關(guān)系會很模糊,因?yàn)檎f不清“發(fā)放”這兩個(gè)字到底誰負(fù)責(zé)。 不穩(wěn):前面用產(chǎn)品名切domain的問題一樣,這些動詞本質(zhì)上是一種資金工具使用場景的表達(dá),是產(chǎn)品功能層面的范疇,隨時(shí)可能被演化和升級。
?
正解,按模型劃域
這是最后采用的方案,當(dāng)然僅是極簡圖,真實(shí)遠(yuǎn)比此復(fù)雜,還有預(yù)算域、支付域等等等等。這里的思考突破是,把所有動詞、邏輯剝離,從模型上看,最后剩下的只有一個(gè)賬戶域?!爸挥幸粋€(gè)域”,是前
面的思考里一直不愿面對的,總覺得這么復(fù)雜的一件事情,怎么也要找個(gè)角度多切幾刀,多整幾個(gè)域來分而治之。
然而從理論方法的角度看,這里涉及的模型簇就只有一個(gè),叫做賬戶,所有復(fù)雜度都是操作賬戶前的邏輯計(jì)算(例如消耗里,一個(gè)訂單按順序or比例對各個(gè)資金工具抵扣計(jì)算、主子單的抵扣方式、某個(gè)資金工具抵扣上限控制、用戶類型及商戶類型和資金工具可用性的匹配度,等等等等),而這些復(fù)雜邏輯所操作的對象,都是賬戶,所以就是一個(gè)域而已。
那么從圖上看,可能會有一個(gè)疑問,”發(fā)放“在此處標(biāo)志為賬戶域的一個(gè)domainService,相較于上一例作為”發(fā)放域“,從biz層視角看,有啥區(qū)別呢?換言之,發(fā)放、充值、消耗,分離為單域表達(dá),會有什么問題呢?
發(fā)放、充值、消耗,都是對賬戶做操作,勢必會有些共性的地方,譬如賬戶的狀態(tài)圖管控。狀態(tài)圖是帶業(yè)務(wù)屬性的,若冗余散落在各域必然不好,若歸攏到一個(gè)叫util or infra的地方也不好(不應(yīng)含業(yè)務(wù)),若下沉到dao也不好(不應(yīng)含業(yè)務(wù)),若隨機(jī)放到其中一個(gè)域并提供給其它域使用也不好(域間不可互調(diào))。這類業(yè)務(wù)屬性本該安置在一個(gè)領(lǐng)域內(nèi),但現(xiàn)在卻無處安放,本因是把不可分割的事情分開表達(dá)了。
?
??2.2.2?實(shí)踐中的細(xì)節(jié)問題
配置和單據(jù)同屬一域嗎?否
這個(gè)問題挺魔幻的,沒在實(shí)操中去思考過不會覺得這是個(gè)問題。但最近某結(jié)算系統(tǒng)的重構(gòu)、發(fā)票系統(tǒng)的重構(gòu)都有同學(xué)涉及到了里面的誤區(qū)。
舉個(gè)例子:結(jié)算系統(tǒng)的費(fèi)用、對賬、應(yīng)收域里,原先都會畫進(jìn)一個(gè)配置模型,譬如費(fèi)用單配置,表達(dá)是費(fèi)用單從哪個(gè)業(yè)務(wù)來,哪類單據(jù)、哪類業(yè)務(wù)細(xì)項(xiàng)、聚合維度、賬期日切or月切、是否需要拋賬、等等等等。
從某種角度說,這是個(gè)業(yè)務(wù)模型,因?yàn)樗鼛Я撕芏鄻I(yè)務(wù)語義,且費(fèi)用單配置、對賬單配置、應(yīng)收單配置,都是獨(dú)立結(jié)構(gòu)化表達(dá)的,而非schemaless的kv表達(dá)。再往下推演,這個(gè)模型在大圖里往哪里放?既然叫費(fèi)用單配置,那么就應(yīng)該和費(fèi)用單、費(fèi)用單明細(xì)一起,組成一個(gè)費(fèi)用域。邏輯似乎很順。
然而,把費(fèi)用單配置和費(fèi)用單擠到一個(gè)域,帶來一些變扭的地方。例如,模型關(guān)系上,費(fèi)用單配置和費(fèi)用單是什么關(guān)系?好像沒啥關(guān)系,費(fèi)用單里不會有一個(gè)字段表達(dá)是從哪個(gè)配置id來的,因?yàn)榕渲弥皇巧少M(fèi)用單的前置邏輯,可以是結(jié)構(gòu)化配置,也可以是一段代碼,跑完就完了,和目標(biāo)對象費(fèi)用單沒直接關(guān)聯(lián)關(guān)系。
再譬如,聚合根是誰?應(yīng)該是費(fèi)用單吧, 可如果這樣,那似乎費(fèi)用單配置并不會依賴費(fèi)用單而存在吧?沒有單據(jù),也還是可以有配置先存在的。這么說,難道有兩個(gè)聚合根在同一個(gè)域?再例如,從域服務(wù)上,對上提供的域服務(wù)既要有對單據(jù)的操作服務(wù),也要有對配置的操作服務(wù),似乎就有兩個(gè)核心對象服務(wù)了,這個(gè)和聚合根的疑問是同源的。
本質(zhì)上,配置和單據(jù)本身是沒關(guān)系的,它僅是表達(dá)對單據(jù)操作的一種邏輯,可以是代碼或者結(jié)構(gòu)化配置,但配置不是核心業(yè)務(wù)模型,不屬于費(fèi)用單域。
那么這個(gè)配置單模型,能否成為一個(gè)單據(jù)的域?看必要性。假使對配置的操作,更注重配置本身的信息存儲功能,沒有很復(fù)雜的業(yè)務(wù)邏輯,那么就由費(fèi)用單域直接調(diào)用費(fèi)用單配置dao讀取就好了。假使對配置的操作,有豐富的控制邏輯,那就可以成為一個(gè)單獨(dú)的域。
例如,從結(jié)算視角看,合同也是一種配置,承載了賬期、收付款方式等信息,但從合同管理本身的角度看,合同的管理亦是有著一套復(fù)雜的控制邏輯,則可以成為一個(gè)單獨(dú)的域。(此處討論的不是另外一個(gè)合同系統(tǒng),而是客商系統(tǒng)中對于結(jié)算要用到的合同片段的信息管理)
?
域太小怎么辦?沒事,如果它需要是一個(gè)域
很多次會被問到這個(gè)問題,并發(fā)生在不同系統(tǒng)重構(gòu)的語境下。我首先會反問,這能夠成為一個(gè)域嗎?
不是底層有個(gè)模型依托,就可以成為一個(gè)域的。我對可成為域的定義是,對模型做CURD操作之前,需要有些業(yè)務(wù)屬性的邏輯承載,否則它僅僅只是一個(gè)dao實(shí)例。
舉例,某系統(tǒng)里會有個(gè)oplog的模型,它會記錄操作的單據(jù)類型、單據(jù)id、操作人、操作時(shí)間、操作內(nèi)容、操作結(jié)果,會和單據(jù)操作同一事務(wù)內(nèi)保存在db表里,且會有用戶查看以及操作記錄的合理性審計(jì)。
所以它并不能完全歸屬到infra,它帶有一點(diǎn)點(diǎn)的業(yè)務(wù)屬性。然而這個(gè)模型在領(lǐng)域劃分時(shí),怎么都沒法融入到一個(gè)具體的單據(jù)域,所以它可能是一個(gè)域。但它又不是一個(gè)域,它只是一個(gè)業(yè)務(wù)對象存儲的dao,因?yàn)樵趯@個(gè)dao做CURD前,對此oplog的組裝加工,并不需要一個(gè)單獨(dú)的域服務(wù)負(fù)責(zé)。應(yīng)收域在對應(yīng)收單做操作時(shí),順便就把操作信息組裝到了oplog里,然后一起保存到dao。應(yīng)付域在對應(yīng)付單據(jù)做操作時(shí),也同樣把應(yīng)付操作相關(guān)的語義組裝到了oplog里。
所以這么看來,這個(gè)“對模型做CURD操作之前的業(yè)務(wù)邏輯承載”并不需要一個(gè)單獨(dú)的域承載,而是散落在需要使用到oplog的地方。其實(shí),上文提到的“配置模型”也是一樣的道理,看似是一個(gè)哪兒都放置不了只能獨(dú)立為一個(gè)域的業(yè)務(wù)模型,然而卻并不需要一個(gè)單獨(dú)的域來承載對其操作的業(yè)務(wù)邏輯。
舉例,上文某資金系統(tǒng)里提到過,充值并不是一個(gè)域,只是一個(gè)使用動作和場景。但此處要補(bǔ)充一點(diǎn),充值動作下是有個(gè)充值單來記錄狀態(tài)和過程信息的。那是否要單拎一個(gè)充值域來表達(dá)對充值單操作的邏輯表達(dá)呢?
當(dāng)時(shí)的選擇是先不單拎一個(gè)域,因?yàn)楫?dāng)時(shí)充值這個(gè)動作僅儲值卡產(chǎn)品有用到,不多的業(yè)務(wù)邏輯在biz層組裝掉,并由biz層直接訪問chargeBillDao完成記錄。若發(fā)展到后期,充值這個(gè)事情變復(fù)雜了,例如可以合并充值、涉及到對充值單本身的分?jǐn)傆?jì)算等,則可再行拎出為域。
所以,個(gè)人的觀點(diǎn)是,如果它是一個(gè)域,那么再小也別和別人硬擠到一起。如果它暫時(shí)還構(gòu)不成一個(gè)域,那先以dao存在著。
?
域太大怎么辦?沒事,如果它只含一個(gè)聚合根
太龐大有兩層意思,一是模型太多,二是代碼邏輯太多。
對于模型太多,實(shí)操層面我沒見過,因?yàn)橐粋€(gè)域只能由一個(gè)聚合根,而聚合根和其附屬模型間有個(gè)共生死的約定(附屬不可獨(dú)自茍存),所以一般以聚合根為中心的模型簇不會太大。如果有人說這個(gè)域里模型怎么這么多,一般來說應(yīng)該是切的不夠細(xì)了,有幾簇模型組同擠一個(gè)域了。
對于代碼邏輯太多,我見過挺多的。尤其是核心域,譬如資金系統(tǒng)里面的賬戶域,代碼特別多,因?yàn)樽顝?fù)雜。如果以代碼量為一個(gè)領(lǐng)域圈的大小邊界,則一個(gè)系統(tǒng)的所有域,一個(gè)個(gè)圈圈陳列在一張大圖上,呈現(xiàn)的景象往往是一家獨(dú)大,或是雙雄爭霸,最多三國鼎立,很少出現(xiàn)諸侯割據(jù)的局面。這是正常且常見的,不要害怕一個(gè)域的代碼越寫越多,只要你確定它底下是單簇模型。
?
它是聚合根么?看獨(dú)立性
怎么看兩個(gè)模型是獨(dú)立的兩個(gè)聚合根,還是歸屬到一個(gè)聚合根?
個(gè)人的實(shí)操經(jīng)驗(yàn)是,需要根據(jù)場景推演,看它是否有獨(dú)立被操作、被存在的情況。切記不可以憑感覺,一定要找業(yè)務(wù)場景推演。
舉個(gè)例子,賬戶和流水,看起來是賬戶是聚合根,流水是附屬模型,因?yàn)榱魉粫?dú)立存在,流水的操作都是因賬戶而起。流水查詢類不能算,雖然流水查詢可能是獨(dú)立的,譬如按用戶查出所有近期流水,不論哪個(gè)賬戶。
但是,展開去講,從DDD里面CQRS command query responsibility segregation角度看,復(fù)雜部分更聚集在command上,所以設(shè)計(jì)考量上會更聚焦在寫操作部分。所以流水是賬戶的附屬模型,它的變化是賬戶金額變化的一個(gè)體現(xiàn),它不獨(dú)立。所以賬戶是聚合根,流水是附屬模型,這是大多數(shù)余額類業(yè)務(wù)系統(tǒng)共性存在的部分,譬如資金、預(yù)算、庫存系統(tǒng)等。
但這個(gè)經(jīng)驗(yàn)值也有例外場景。譬如,財(cái)務(wù)領(lǐng)域里有個(gè)銀行流水認(rèn)領(lǐng),背景是to B大額交易,客戶常選擇銀行渠道付款,常是手工打款。集團(tuán)從銀行賬戶看到的收款流水,和業(yè)務(wù)語義的應(yīng)收單據(jù)比對,往往金額不一致??赡苁强蛻艉喜⒏犊睢㈩A(yù)付款,且付款備注格式不一,需要財(cái)務(wù)手工去識別、打標(biāo)、拆解金額,最終核銷應(yīng)收單。這是銀行流水認(rèn)領(lǐng)系統(tǒng)的定位職責(zé)。此時(shí),流水是獨(dú)立的,它是財(cái)務(wù)操作的核心目標(biāo)模型,它的操作和賬戶無關(guān),故而是獨(dú)立的聚合根。
所以,我們需要根據(jù)場景推導(dǎo),看模型的獨(dú)立性來判斷是否為聚合根。
?
充血模型?不建議
對于域服務(wù)內(nèi)部的實(shí)現(xiàn)方式,會有兩種,一種我稱之為“充血模型驅(qū)動式”,一種我稱之為“平鋪直敘式”。
DDD這本書里,一個(gè)經(jīng)典的框架方式為:
以模型劃分域,確定該域的聚合根; 以聚合根為核心模型,基于此構(gòu)造若干對模型的操作函數(shù),并以此為邏輯載體; 模型操作函數(shù),負(fù)責(zé)復(fù)雜邏輯的表達(dá),并將處理和加工結(jié)果體現(xiàn)在模型實(shí)例本身的結(jié)構(gòu)改變、段值改變; 最后以repository.save(模型)的方式將其持久化至DB。
所以對域服務(wù)的實(shí)現(xiàn),有些同學(xué)會基于該域的聚合根模型做邏輯操作,將結(jié)果反饋至模型實(shí)例,最后再轉(zhuǎn)換為存儲模型完成持久化。然而,實(shí)操層面上,事情會往雜亂的形態(tài)演化,代碼和模型結(jié)構(gòu)越來越復(fù)雜難懂。
2016年在資金平臺、2022在預(yù)算平臺見過這類情況,尤其是涉及“余額”類的模型,第一感覺是很適合用此種方式表達(dá)。但事實(shí)情況是資金平臺里對紅包的金額計(jì)算、預(yù)算平臺里對預(yù)算池的操作,兩者的代碼都很復(fù)雜難懂,需要沉下心來耐心梳理方能得解。而事實(shí)上這些代碼背后需要表達(dá)的邏輯又并沒有代碼本身表現(xiàn)的那么復(fù)雜,把本來略復(fù)雜的事情搞更復(fù)雜了,這是實(shí)操層面的效果。
個(gè)人理解,這里核心是兩個(gè)原因:
以模型為中心的邏輯承載。DDD里面的例子都是極其簡單的。生產(chǎn)實(shí)踐上,尤其是面對和營銷業(yè)務(wù)沾邊的資金操作,和復(fù)雜樹狀組織架構(gòu)匹配的預(yù)算類操作,都不是一個(gè)簡單的模型能表達(dá)的。模型本身是復(fù)雜的,并且會隨著業(yè)務(wù)的迭代越發(fā)復(fù)雜,呈現(xiàn)出父子模型、樹狀關(guān)系、多層級嵌套結(jié)構(gòu)、稀疏態(tài)模型空間等特征。模型不能像代碼那樣通過SPI有很好的擴(kuò)展性,不論是共性邏輯還是個(gè)性邏輯,都要在一個(gè)模型上體現(xiàn),各種需求和邏輯的疊加下,模型會極限膨脹。而對于某一個(gè)域服務(wù)(對應(yīng)到充血模型的一個(gè)函數(shù)方法),需要了解其邏輯,則必須要先把該模型本身整個(gè)全部摸透,就算該域服務(wù)的邏輯只涉及模型的很小一部分改動,但你還是得“沉下心來“看清楚全貌,因?yàn)槟悴恢肋@個(gè)邏輯將牽扯多少模型的結(jié)構(gòu)、關(guān)系、段值的聯(lián)動性改變,心慌。 以模型為中間載體的兩段邏輯的疊加效應(yīng)。DDD里的例子是簡單的,所以可以模型.operateA()后,repository.save(模型)即可。現(xiàn)實(shí)中,模型是復(fù)雜臃腫的,將其轉(zhuǎn)換回存儲模型本身的邏輯代碼亦會很復(fù)雜。于是在業(yè)務(wù)空間的一段需求邏輯,到了實(shí)現(xiàn)層面將被切割成三段:a.模型本身的理解 b.對模型基于需求翻譯的操作邏輯 ?c.域模型到存儲模型的轉(zhuǎn)換。到這里可能會有個(gè)疑問,c.域模型到存儲模型的轉(zhuǎn)換,不是一套固定代碼解決的嗎,所以我們只要搞懂了a、b即可。然而事實(shí)情況是,在互聯(lián)網(wǎng)應(yīng)用里,往往不是像hibernate那種映射式save,而是mybatis那種對存儲對象局部修改的實(shí)現(xiàn)方式,將模型里關(guān)于該域服務(wù)這一個(gè)切面的信息量,轉(zhuǎn)換為sql進(jìn)行持久化。因?yàn)樾阅?。所以對每個(gè)域服務(wù),到c階段的代碼都是不同的,還是得老老實(shí)實(shí)的看全abc才能全面知曉代碼邏輯。
所以我個(gè)人是偏向于“平鋪直敘”的表達(dá)方式,從業(yè)務(wù)需求到邏輯計(jì)算并直接轉(zhuǎn)換為db存儲的指令,從人的理解上也更容易,就算這里的局部代碼是面條式,也比所謂的“充血模型”的高級設(shè)計(jì)模型,更好理解,這是個(gè)人體感上面的表達(dá)。
從理論上面講,兩個(gè)點(diǎn):
平鋪直敘的方式,僅針對該域服務(wù)函數(shù)的邏輯表達(dá)。典型的是query出存儲對象的片段、邏輯計(jì)算加工、更新回存儲對象。注意,這里的片段,僅針對當(dāng)前函數(shù),不會像充血模型式每次都撈全了。也就是一碼歸一碼,一事一議。 互聯(lián)網(wǎng)的復(fù)雜業(yè)務(wù)邏輯下,更注重的是邏輯的承載和表達(dá)。在多態(tài)業(yè)務(wù)的沖擊下,流程+邏輯上的代碼表達(dá),更有利于以SPI的方式將個(gè)性化和共性化解耦,這比模型為承載體更有優(yōu)勢。
?
域間可以互調(diào)嗎?絕對不行
這個(gè)問題顯而易見,似乎都不應(yīng)該是一個(gè)問題。其實(shí)我想強(qiáng)調(diào)的是,包結(jié)構(gòu)的設(shè)計(jì)對“域間不可見”這一原則的落實(shí)性影響。
左邊這種對領(lǐng)域包的組織方式,我見過,不好。建議采用右邊這種,從根上隔開,表達(dá)的是領(lǐng)域和領(lǐng)域之間是完全獨(dú)立的,就算它們內(nèi)部的包分層組織類同,也請不要按分層的包名合并。隔離方式可以是module,也可以是package,視系統(tǒng)大小,但別混用。
?
域內(nèi)聚到什么程度?考慮ROI
這個(gè)問題的出發(fā)點(diǎn)是,曾經(jīng)有同學(xué)覺得,既然領(lǐng)域要獨(dú)立。那么,領(lǐng)域內(nèi)部也要分API、biz、core、model、dao幾層,因?yàn)轭I(lǐng)域們要像獨(dú)立應(yīng)用那樣,雖然現(xiàn)在擠在一個(gè)應(yīng)用里,但只要它們間解耦,隨著業(yè)務(wù)增長和應(yīng)用的龐大,我們隨時(shí)可以將它們單拎成獨(dú)立的應(yīng)用。
那么,領(lǐng)域內(nèi)部也要做到盡量的內(nèi)聚,通過倒置依賴的方式(見下面《倒置依賴》有具體圖例),將外界的一切實(shí)現(xiàn)層面的概念都屏蔽掉,讓自己像個(gè)周身遍布各種型號插口的充電寶一樣,只關(guān)注和知曉自己的內(nèi)核,不感知不在乎將和哪個(gè)哪類型號的手機(jī)集成。
這個(gè)問題放最后闡述,因要再次強(qiáng)調(diào)。一般教科書上講的“領(lǐng)域”,更多的是類似交易、營銷、商品之于電商售賣平臺的L1層面的領(lǐng)域,它們有獨(dú)立的應(yīng)用群、產(chǎn)研團(tuán)隊(duì)。而本文通篇提及的“領(lǐng)域”,僅限于L1域內(nèi)的子域,乃至單個(gè)應(yīng)用內(nèi)部的域,它們共處于一個(gè)應(yīng)用群、一個(gè)產(chǎn)研團(tuán)隊(duì)?!皠澐帧边@個(gè)詞背后,本質(zhì)是化整為零、化繁為簡,在龐大的研發(fā)協(xié)同中尋求最大的效率。而在面對的是單一小組內(nèi)部的協(xié)同問題時(shí),“解耦”的效率收益則需要和“劃分”付出的代價(jià)做權(quán)衡,從實(shí)操上做一定的耦合性妥協(xié),未嘗不可。
2.3?細(xì)項(xiàng)3 - 開放性
?
上圖借用自集團(tuán)某同事的技術(shù)分享文檔,非本人原創(chuàng)。
多態(tài)業(yè)務(wù)必有共性和個(gè)性,理想世界里,希望有一個(gè)平臺將共性部分集中支撐,且同時(shí)能保持個(gè)性部分的靈動調(diào)整?,F(xiàn)實(shí)世界里,平臺集中復(fù)用和業(yè)務(wù)自主靈動,呈反向相關(guān)。實(shí)現(xiàn)層面,無非有四種:
平臺中心保姆式; 平臺托管SPI開放式; 平臺組件化被集成式; 行業(yè)煙囪自研式。
A、D為兩個(gè)方向的極端,一個(gè)極度集中復(fù)用但行業(yè)自主性極差,一個(gè)行業(yè)極度自由但無共享復(fù)用。B、C是兼顧平臺集中復(fù)用和行業(yè)自主性的開放化架構(gòu)方案,B以TMF2.0架構(gòu)為代表,C在“大前臺小中臺”技術(shù)大方向下的一種全新的行業(yè)和平臺架構(gòu)關(guān)系。
本文話題聚焦在一個(gè)應(yīng)用內(nèi)部對多態(tài)業(yè)務(wù)兼容性的架構(gòu)表述,不涉及行業(yè)和平臺關(guān)系的論證。然而不論是煙囪式、中心式、還是SPI開放式,被多業(yè)態(tài)業(yè)務(wù)沖擊的系統(tǒng),能做到“系統(tǒng)能力和業(yè)務(wù)個(gè)性化解耦”,“業(yè)務(wù)間解耦”,總是好的。
就算整套系統(tǒng)就是同一方人員研發(fā),研發(fā)內(nèi)部能把業(yè)務(wù)定制代碼和系統(tǒng)能力代碼隔離開來,總是好的。就算是行業(yè)研發(fā),也不表示面對的業(yè)務(wù)就單一了就不復(fù)雜了,這是誤區(qū)。
2018~2020我在集團(tuán)研發(fā),所謂的平臺。2020至今,我在本地生活研發(fā),所謂的前臺行業(yè)。然而到了前臺才發(fā)現(xiàn),前臺之前還有前臺,我需要面對餓了么的餐飲、新零售、物流、城代、企餐、口碑等行業(yè)的財(cái)務(wù)需求支撐。
當(dāng)然,在餓了么,不會有所謂的行業(yè)財(cái)務(wù)研發(fā)來和我協(xié)同,所有的餓了么財(cái)務(wù)需求我需要全部支撐,即是平臺中心保姆式。其實(shí)在人員配比合適的前提下,這種生產(chǎn)關(guān)系非常高效,但這并不阻礙我很需要在架構(gòu)和代碼層面將行業(yè)特性和系統(tǒng)能力區(qū)分開來的自我期望。
??2.3.1?實(shí)踐中的細(xì)節(jié)問題
定制點(diǎn)開哪層?領(lǐng)域?qū)?/p>
無非是在biz和domain層選擇,api和dao里開定制點(diǎn),我是沒見過的。
按照我”厚domain薄biz“的架構(gòu)思想,肯定會選擇domain里開定制點(diǎn)。并且,從biz定位上講,希望能靈動,針對一類場景即可,不奢求復(fù)用。倘若要開定制點(diǎn),必然是在”有復(fù)用有個(gè)異“的基礎(chǔ)上,邏輯上講,就和biz層不求復(fù)用的宗旨矛盾了。所以必然是在domain身上動刀。
反面案例講,原來在結(jié)算經(jīng)歷過,見上文描述《biz層臃腫,service層單薄》這一節(jié)。原來老結(jié)算系統(tǒng)的架構(gòu)思想下,上層做編排(甚至有工作流式的非同步調(diào)用編排),下層提供service被編排。而下層的service沒有擴(kuò)展點(diǎn),不可被定制,語義和內(nèi)容實(shí)現(xiàn)上很明確,一就是一且僅只能是一,明明確確。
我個(gè)人喜歡稱這類底層能力叫”實(shí)心磚“。一旦這個(gè)”實(shí)心磚“的內(nèi)部實(shí)現(xiàn)不匹配新業(yè)務(wù)形態(tài)了(就算磚頭的形態(tài)、大小還是可以用的,只是局部不匹配),就得升級它。如果升級本身和其它業(yè)務(wù)沖突,則得再造一塊磚。而架構(gòu)上覺得這個(gè)再造同類磚是不合適的,是腐化,那就得拜托上層轉(zhuǎn)換成完全匹配該磚的契合要求。
于是一點(diǎn)點(diǎn)的業(yè)務(wù)差異邏輯逐漸往上浮,直至biz層開始臃腫后。通用biz類和它的業(yè)務(wù)定制點(diǎn)就出現(xiàn)了。再往后發(fā)展,biz層越發(fā)的腫脹,底層service層越發(fā)的萎縮,這是一個(gè)逐漸且必然的發(fā)展趨勢。
而在domain里開定制點(diǎn),我個(gè)人喜好稱這類底層能力叫”空心磚”,磚還是那塊磚,大小、形狀都沒變,依然能契合原先的繼承點(diǎn),只是局部材質(zhì)、重量、柔韌度上可通過往空心里加不同材料實(shí)現(xiàn)。
?
定制點(diǎn)開多大?不要太小
這個(gè)問題很難有個(gè)概況性的詞匯來解答,本身就是個(gè)設(shè)計(jì)的”顆粒度“問題,”度“這個(gè)詞本身就是一種類似”火候“這樣的經(jīng)驗(yàn)值,只能依菜而定。定制點(diǎn)開很大,達(dá)到幾乎把所在的域服務(wù)挖空了,所有邏輯均在一個(gè)定制點(diǎn)里表達(dá)掉了,這個(gè)我是沒見過的,估計(jì)也不會有。所以問題的核心還是”定制點(diǎn)要開多?。?/p>
舉個(gè)例子:
2018年結(jié)算完成架構(gòu)升級后,按組織要求,需要做商業(yè)能力透出。按照當(dāng)時(shí)的要求,由于要在XH平臺對商業(yè)能力做顯性化表達(dá),包括流程、擴(kuò)展點(diǎn),乃至可以直接在XH運(yùn)營平臺配置一個(gè)商業(yè)能力的業(yè)務(wù)規(guī)則,所以要求擴(kuò)展點(diǎn)需細(xì),細(xì)到等同于一個(gè)配置項(xiàng)。
以結(jié)算記賬看,擴(kuò)展點(diǎn)要細(xì)到”賬期日“,“賬期周期類型-月、周、日“,”賬單類型-僅記賬、直接結(jié)算、周期結(jié)算“,”扣款渠道-CAE、網(wǎng)商、資金賬戶“,具體收入賬戶id,等等,這些顆粒度正好和運(yùn)營配置界面的配置項(xiàng)顆粒度一樣,因此一個(gè)擴(kuò)展點(diǎn)的實(shí)現(xiàn)邏輯直接對應(yīng)到配置項(xiàng)值。
然而,我個(gè)人是不認(rèn)同這么細(xì)顆粒度的擴(kuò)展性的。因?yàn)閿U(kuò)展點(diǎn)本質(zhì)上是一種業(yè)務(wù)個(gè)性化邏輯的表達(dá),不論是代碼表達(dá),還是配置表達(dá),甚至是一個(gè)proxy代碼callback外部服務(wù)獲得邏輯,而這段邏輯若是太碎,一方面不好管理,一方面不直觀,一方面站在實(shí)現(xiàn)擴(kuò)展點(diǎn)的研發(fā)同學(xué)的視角就更是為難了。
站在SPI實(shí)現(xiàn)者的視角,本來平臺對其就是個(gè)黑盒,面對一堆只見其名不見其實(shí)的SPI,要把業(yè)務(wù)邏輯精準(zhǔn)的擺放到位實(shí)為其難,更不說一堆數(shù)量龐大口徑更細(xì)的SPI了。當(dāng)然,站在當(dāng)時(shí)設(shè)計(jì)者的角度,是希望更細(xì)的顆粒度讓SPI的語義更精確。然我個(gè)人的看法是,SPI不是一個(gè)獨(dú)立的個(gè)體,譬如上文提到的”賬期日“,字面上確實(shí)更清晰和精確,但背后的隱憂是,我對這個(gè)SPI的實(shí)現(xiàn),會聯(lián)動這個(gè)平臺發(fā)生什么樣的變化,我是不清楚的。
就像一個(gè)開關(guān),只有on off兩個(gè)選擇,語義上非常的精確,但你按下去之后是關(guān)一盞燈還是關(guān)一排燈,我是不知道的。心慌。
?
所以,我個(gè)人的實(shí)踐經(jīng)驗(yàn)是,SPI代表一段邏輯,可以以代碼表達(dá)的邏輯,之后才是兩種實(shí)現(xiàn)方式:
一段業(yè)務(wù)定制代碼; 一段系統(tǒng)默認(rèn)實(shí)現(xiàn)代碼,并讀取業(yè)務(wù)配置獲得定制邏輯。方式1、2是并存的,根據(jù)業(yè)務(wù)code路由實(shí)現(xiàn)方式。以代碼邏輯打底的SPI口徑不會太小。
?
業(yè)務(wù)身份定義?沒標(biāo)準(zhǔn),能橫豎切開業(yè)務(wù)邏輯分而治之的,就挺好。
這個(gè)地方,我不用專業(yè)術(shù)語去定義一個(gè)什么叫業(yè)務(wù),因?yàn)闆]法有標(biāo)準(zhǔn)方法定義。同樣一個(gè)組織,看的視角不同,認(rèn)出來的結(jié)果也是不同的。
舉個(gè)例子,財(cái)務(wù)研發(fā)線,有很多對財(cái)務(wù)崗位的業(yè)務(wù)線定義,因?yàn)樨?cái)務(wù)系統(tǒng)要支持他們的訴求,必須在某個(gè)維度將其分而治之,找出異同。然而,站在業(yè)務(wù)平臺,尤其是交易、支付、營銷這些toC的系統(tǒng),它們會怎么看待財(cái)務(wù)崗位,是一個(gè)業(yè)務(wù)么?按照我依稀記得的2019年之前XH架構(gòu)對業(yè)務(wù)的定義,是必須有獨(dú)立own商品且有自己的商業(yè)KPI的,才叫業(yè)務(wù)。
大約2016年起,業(yè)務(wù)平臺搞TMF架構(gòu),提出平臺和業(yè)務(wù)隔離、業(yè)務(wù)和業(yè)務(wù)隔離、業(yè)務(wù)有業(yè)務(wù)身份的概念,我是極度認(rèn)同的。尤其是有縱向業(yè)務(wù)和橫向業(yè)務(wù)的區(qū)別,尤其是主架構(gòu)師經(jīng)常時(shí)常常常拿口碑的例子普及他的架構(gòu)思想(口碑是橫向,淘寶是縱向,因?yàn)榭诒疀]有自有的商品,只是一個(gè)售賣渠道),我是極以為是的。這么橫豎的切分,一些問題迎刃而解。
舉例,在做資金平臺架構(gòu)設(shè)計(jì)時(shí),當(dāng)時(shí)對于紅包、儲值卡、積分等產(chǎn)品的差異性怎么表達(dá),很是困惑。
你說它們不是身份吧,但他們確實(shí)在同一個(gè)域服務(wù)里有極大的邏輯差異,不用bizCode怎么把這個(gè)差異分支路由開呢?難不成對紅包的邏輯定制,每個(gè)業(yè)務(wù)插件實(shí)現(xiàn)包里都寫一份?
你說它是業(yè)務(wù)身份吧,可是和淘寶、天貓、飛豬、盒馬這些正牌業(yè)務(wù)身份,是啥關(guān)系?難不成要二元疊加為淘系-紅包、飛豬-紅包、盒馬-儲值卡。好像也挺好,記得當(dāng)初真有往這個(gè)方向去想過,因?yàn)閺漠a(chǎn)品形態(tài)上講,業(yè)務(wù)和產(chǎn)品類別間,是一個(gè)稀疏矩陣關(guān)系。例如集團(tuán)淘系共用一類紅包,XX業(yè)務(wù)紅包是獨(dú)立的,YY業(yè)務(wù)強(qiáng)調(diào)儲值卡弱化紅包??墒堑胶髞?,業(yè)務(wù)要打通,XX業(yè)務(wù)的紅包,集團(tuán)也能用,但規(guī)則不同,于是出現(xiàn)了XX業(yè)務(wù)紅包在XX業(yè)務(wù)下是規(guī)則A(例如使用上限之類的),在淘寶下是規(guī)則B。到這里就玩不轉(zhuǎn)了。
而今回看,不論這些產(chǎn)品類型的差異,叫做是橫向業(yè)務(wù)差異也好,還是產(chǎn)品差異也好(TMF里也有橫向產(chǎn)品一說),本質(zhì)就同系統(tǒng)自身能力中有A、B、C三種模式來實(shí)現(xiàn)流程的同一節(jié)點(diǎn),然后不同業(yè)務(wù)各自選擇適合自己的模式執(zhí)行該節(jié)點(diǎn)。只是紅包、儲值卡,更像是若干個(gè)節(jié)點(diǎn)的實(shí)現(xiàn)模式的一個(gè)大集合,或者叫做一個(gè)實(shí)現(xiàn)套餐,也是很合適的。它們本質(zhì)并不是一個(gè)業(yè)務(wù)。
前面說口碑這個(gè)經(jīng)典案例,引出了縱橫業(yè)務(wù)的概念。這是站在交易的視角,以貨權(quán)有無來定義一個(gè)業(yè)務(wù),而當(dāng)時(shí)以交易為首的交易鏈路視角幾乎代表了整個(gè)業(yè)務(wù)平臺的視角。
然而,到了結(jié)算的視角,從資金往來、發(fā)票稅務(wù)單元、財(cái)務(wù)核算單元、乃至經(jīng)營分析單元進(jìn)行業(yè)務(wù)的切分,此刻雖無貨權(quán)的口碑堂堂正正的成為了一個(gè)獨(dú)立的垂直業(yè)務(wù)。所以我說業(yè)務(wù)身份如何定義?因域而異
2.4?細(xì)項(xiàng)4 - 解耦
?上面是一張畫在2018年12月27日的圖,強(qiáng)調(diào)如果一個(gè)系統(tǒng)在各方面都能做到解耦,其靈活性一定是高級的。這圖到現(xiàn)在看,基本原則也沒變,就直接拿來貼上了。
補(bǔ)充幾個(gè)點(diǎn):
1.不是切的越碎越好
例如層次越多、領(lǐng)域越多、應(yīng)用越多,矯枉過正了。解耦的本質(zhì)是把事情分而治之,但解耦需要付出代價(jià),收益和代價(jià)之間需根據(jù)所在業(yè)務(wù)特色、研發(fā)團(tuán)隊(duì)狀況做權(quán)衡。當(dāng)然這是句正確的廢話,總之沒有標(biāo)準(zhǔn),思考事物的本質(zhì)很重要。
見過一些具體的“矯枉過正”的案例。譬如圖例,在調(diào)度器應(yīng)用和執(zhí)行器應(yīng)用之間加了個(gè)消息隊(duì)列做指令的傳遞,注意,背景是這兩個(gè)應(yīng)用群歸屬同一研發(fā)小組同一業(yè)務(wù)域。為何不以rpc同步調(diào)用方式直接傳達(dá)指令?兩者的qps、機(jī)器數(shù)量、穩(wěn)定性保障各方面看解耦性,兩種架構(gòu)模式差異不大。
這是一個(gè)具體的案例。再譬如應(yīng)用切太細(xì)的,看似很解耦,實(shí)則因全局調(diào)用鏈路的復(fù)雜化而付出了更大的代價(jià)。
2.“語義”的獨(dú)立性是關(guān)鍵
業(yè)務(wù)空間有業(yè)務(wù)空間的語義,到了技術(shù)架構(gòu)空間就應(yīng)該做轉(zhuǎn)換、收斂。譬如預(yù)算熔斷判斷和預(yù)算余額查詢兩類業(yè)務(wù)需求,到了技術(shù)空間,可以是同一個(gè)應(yīng)用服務(wù)對接。應(yīng)用間也有各自的語義,譬如2017年在資金平臺時(shí),支付應(yīng)用層面的資金組成標(biāo)xyz到了資金系統(tǒng)內(nèi)部就應(yīng)該轉(zhuǎn)換為紅包等資金工具實(shí)例的出資來源組成語義(當(dāng)時(shí)卻把這個(gè)xyz的外部語義一直一直深入傳遞到底層,并在各層以此外部語義做邏輯判斷依據(jù),這是資金1.0系統(tǒng)代碼晦澀的一個(gè)方面)。
分層間也有各自的語義,在domain層叫做cancel的一個(gè)服務(wù),到了dao層就應(yīng)該理解為update(statue=-1),然而我見過上下層語義保持一個(gè)頻道的,譬如biz、domain里的service有叫update的函數(shù),看起來是做了一個(gè)很通用的函數(shù),然而違背了本該在業(yè)務(wù)層保持語義精準(zhǔn)性的原則。
這里扯開去一點(diǎn),做業(yè)務(wù)研發(fā),在實(shí)操中,我對關(guān)鍵概念名詞的精準(zhǔn)度,比較較真。不論是在研發(fā)內(nèi)部的系分設(shè)計(jì),還是對PRD的評審,都會在意一個(gè)概念的取名是否精準(zhǔn)。因?yàn)椴徽撌谴a、還是業(yè)務(wù)邏輯的傳承,都和它緊密掛鉤。如果一個(gè)關(guān)鍵概念取名有二義性,很容易造成在協(xié)同上雞同鴨講,造成巨大的潛在的協(xié)同成本。
往大了講,一個(gè)業(yè)務(wù)系統(tǒng)架構(gòu)的腐化,往往是從這些含糊其義的類名、方法名、變量名萌芽的。取名不精準(zhǔn),也反映出研發(fā)同學(xué)對業(yè)務(wù)本質(zhì)的思考理解不夠。
3.倒置依賴
一刀兩段切開,變?yōu)檎{(diào)用方和服務(wù)方。站在調(diào)用方的角度,倒置依賴講的是“我作為A,需要有服務(wù)D1供我使用”,D1這個(gè)交互協(xié)議是站在調(diào)用者需求角度提出的,是個(gè)體需求的表達(dá)。非倒置依賴講的是“我作為A,看到有服務(wù)D適合我使用”,D是站在服務(wù)方本身具備的角度描述的,是公用資源的表達(dá)。
?
落地上有兩種實(shí)現(xiàn)方式:
第一種站在領(lǐng)域?qū)蛹壙吹怪靡蕾?,譬如域A要D1服務(wù),域B要D2服務(wù),而對應(yīng)服務(wù)只有D,因此D1->D, D2->D是需要分別轉(zhuǎn)換的,且D1、D2這個(gè)定義的交互協(xié)議,僅能在領(lǐng)域內(nèi)部復(fù)用,作為防腐層的轉(zhuǎn)換代碼也應(yīng)在領(lǐng)域內(nèi)部;
第二種站在應(yīng)用的高度看倒置依賴,應(yīng)用說我要有一個(gè)D服務(wù)放在infra層以供所有領(lǐng)域使用,因此A、B兩個(gè)域可以共用D服務(wù),不用做轉(zhuǎn)換,甚至可以把服務(wù)協(xié)議D耦合進(jìn)A、B的領(lǐng)域代碼。而在應(yīng)用層面,可以有D的不同實(shí)現(xiàn),譬如針對不同表結(jié)構(gòu)的dao實(shí)現(xiàn)、針對外部服務(wù)的rpc調(diào)用實(shí)現(xiàn),可以起到對環(huán)境解耦的倒置依賴。
本質(zhì)上講,這是兩種內(nèi)聚顆粒度的問題。顆粒度越小,成本越高。個(gè)人會選擇應(yīng)用角度的倒置依賴。
第一,教科書中“領(lǐng)域”的概念,很多是L1的,譬如交易、營銷、商品這些站在銷售電商平臺角度講的域,應(yīng)用和團(tuán)隊(duì)都是要劃開的。而本文的架構(gòu)設(shè)計(jì),圍繞的是單域內(nèi)的架構(gòu)設(shè)計(jì),可以理解為單應(yīng)用內(nèi)部的子域。
第二,因?yàn)槭菃螒?yīng)用內(nèi)部的,故而它并非真正需要那么的獨(dú)立。而D1、D2這些語義的倒置依賴實(shí)現(xiàn)成本是巨大的,且大多數(shù)應(yīng)用內(nèi)劃出來的域都是不大的,ROI上就要權(quán)衡了。故而,個(gè)人會選擇在領(lǐng)域語言獨(dú)立性基礎(chǔ)上,做一定的耦合妥協(xié),譬如此例中領(lǐng)域感知到infra層的協(xié)議語義D。
4.業(yè)務(wù)模型和存儲模型解耦?
一般來說新建的系統(tǒng),應(yīng)用和DB都是完全掌控的,都是自家的東西,就沒那么容易分清楚彼此。發(fā)展到后期,因?yàn)橄到y(tǒng)和團(tuán)隊(duì)的歸并,需要和異構(gòu)系統(tǒng)做整合,此時(shí)前期的業(yè)務(wù)模型和存儲模型是否解耦就很關(guān)鍵了。
舉例,資金平臺,發(fā)展到后來,需要收口集團(tuán)原散落在行業(yè)的資金營銷工具,例如XX業(yè)務(wù)積分。此時(shí)形態(tài),在domain層都是積分模型,到了dao層一邊是自行設(shè)計(jì)的db存儲模型,一邊是原XX業(yè)務(wù)積分的db存儲模型。存儲層面更有甚至是http調(diào)用外部服務(wù)(當(dāng)時(shí)新零售戰(zhàn)略下,投資了些超市,它們有自己的會員儲值卡系統(tǒng),需要系統(tǒng)集成的方式對接使用)。
假設(shè)前期設(shè)計(jì)的好,只是在dao層有3種不同的實(shí)現(xiàn)適配,核心的領(lǐng)域代碼和模型是不用動的。
推而廣之,按照DDD六邊形法則,系統(tǒng)對外的觸點(diǎn),理論上都可以接口+實(shí)現(xiàn)的方式做環(huán)境的解耦,當(dāng)然實(shí)現(xiàn)上也要考慮ROI。最糟糕的是,例如上面講的xyz標(biāo)一樣,一個(gè)外部環(huán)境的語義在整個(gè)應(yīng)用中從頭貫到尾,當(dāng)對接的支付渠道作為外界環(huán)境發(fā)生變更時(shí),就只能做貫穿全身的大手術(shù)了。
5.配置態(tài)和運(yùn)行態(tài)解耦
主要講的是“配置”這個(gè)東西,讀用的地方和寫給的地方是兩件事情。如同上文《領(lǐng)域設(shè)計(jì)》里提到過的案例,客商里面費(fèi)用單配置,它在幫助費(fèi)用單表達(dá)其生成邏輯時(shí),這叫“運(yùn)行態(tài)”,此時(shí)單據(jù)是主配置是輔。
而在通過運(yùn)營平臺之類做配置管理時(shí),這個(gè)叫“配置態(tài)”,此時(shí)配置是主模型,它可以成為一個(gè)域,如果足夠復(fù)雜的話(譬如對配置的結(jié)構(gòu)管理、配置實(shí)例的增減、多角色的編輯審批等協(xié)同)。
三、多系統(tǒng)間架構(gòu)形態(tài)(反面)
多系統(tǒng)的組合空間巨大,沒法有個(gè)標(biāo)準(zhǔn)形態(tài)。這里以個(gè)人認(rèn)為的反面案例反向表達(dá)實(shí)踐的總結(jié)。
3.1?微服務(wù)切應(yīng)用
案例是15年共事過某XX域,當(dāng)時(shí)該BU的整體架構(gòu)原則就是微服務(wù)化,切的真是碎,一個(gè)挺小的沒幾個(gè)類的原子的功能就可以成為一個(gè)微服務(wù)應(yīng)用。
切的碎有它的設(shè)計(jì)考量,springboot讓應(yīng)用的建立成本大大降低,docker亦大大壓縮了運(yùn)維成本,從團(tuán)隊(duì)協(xié)同上講把事情分的更細(xì)能盡量規(guī)避巨大的溝通成本,好處多多。
但我個(gè)人是不贊同切的那么碎的。我認(rèn)同SOA,但不認(rèn)同微服務(wù),兩者的區(qū)別是顆粒度問題。我認(rèn)同十多年前的某項(xiàng)目把一個(gè)巨石系統(tǒng)的淘寶按域切分成交易、營銷、商品、結(jié)算等等,因?yàn)槊總€(gè)域背后是一支單獨(dú)的研發(fā)小組。
當(dāng)組織隨著業(yè)務(wù)龐大后,倘若繼續(xù)守著原來的一個(gè)系統(tǒng)一套svn(當(dāng)時(shí)行這個(gè))是協(xié)同上的一種巨大災(zāi)難。
但微服務(wù)顆粒度不同,已然是一個(gè)研發(fā)小組內(nèi)部的事,還要將應(yīng)用拆分到每人拿若干個(gè)應(yīng)用(聽說在原來AE的架構(gòu)里,這里的“若干”是比較大的一個(gè)數(shù)),我不太能理解這里的好處,可能是有。
但相較于應(yīng)用拆分導(dǎo)致的長鏈路性能、長鏈路下問題排查效率、每次基礎(chǔ)組件升級都要配合著落實(shí)N個(gè)應(yīng)用,潛藏的運(yùn)維成本、以及潛藏的應(yīng)用交接成本、潛藏的對N個(gè)應(yīng)用逐個(gè)盤摸得到全局的理解成本,遠(yuǎn)大于收益。
3.2?按層切應(yīng)用
案例,16年做的重構(gòu)設(shè)計(jì),設(shè)計(jì)了5個(gè)分層,網(wǎng)關(guān)層gateway,notify接收訂單完結(jié)消息,并DB里緩存之,之后異步計(jì)費(fèi)+結(jié)算;流程層business,沿用bpm編排底層能力,等同于標(biāo)準(zhǔn)分層架構(gòu)的biz層;ability層,提供底層的原子服務(wù)能力,供上層編排;domain層,基于某個(gè)模型的操作;本質(zhì)上ability層+domain層 = 標(biāo)準(zhǔn)分層架構(gòu)的domain層,而劃分成這兩層,其實(shí)是反面的案例,實(shí)操層面就會覺得邏輯都在ablity,domain幾乎弱化為dao,且ability用動詞劃分有不穩(wěn)定性問題,當(dāng)然這是上文“單系統(tǒng)內(nèi)分層架構(gòu)形態(tài)”范疇的一個(gè)反面案例了,此處不展開。
回到應(yīng)用劃分的問題,當(dāng)初受了層間腐化的苦(該沉的能力代碼沒沉,該浮的業(yè)務(wù)個(gè)性化在底下做判斷),心有戚戚,念著重構(gòu)最大的目標(biāo)就是要把業(yè)務(wù)和能力分層開。又擔(dān)憂現(xiàn)在的設(shè)計(jì)不被未來人所遵守,干錯(cuò)一不做二不休,直接把business, ability, domain,dao切開成單獨(dú)的應(yīng)用,且嚴(yán)格禁止跨層訪問,寄期望于應(yīng)用的隔離從架構(gòu)上把腐化問題給杜絕了。
但又礙于事務(wù)問題,ability到domain這層,一個(gè)原子ability服務(wù)會跨多個(gè)domain修改,domain的應(yīng)用獨(dú)立將引出跨應(yīng)用調(diào)用的分布式事務(wù)問題,實(shí)難解決。
為了堅(jiān)持應(yīng)用的獨(dú)立,曾經(jīng)考慮過ability到domain走二階段事務(wù)框架,參考螞蟻的xts分布式框架(當(dāng)年很行這個(gè),面試都會經(jīng)常問二階段問題,近幾年不知為何這類問題忽然淡出了),然又礙于二階段事務(wù)框架對domain的改造侵入之劇烈(需要domain每個(gè)方法都展開為prepare, commit, rollback三階段,且需根據(jù)服務(wù)場景用不同的寫法,寫錯(cuò)更容易引故障,對研發(fā)要求很高),最終放棄。
最終的最終,采取了折中方案,把a(bǔ)bility以下三層打包到一個(gè)應(yīng)用里,規(guī)避事務(wù)問題。雖然是折中方案,依然橫切出了3個(gè)應(yīng)用:hjgateway, hjbusiness, hjability。
直到2018左右支援某海外業(yè)務(wù)項(xiàng)目,因?yàn)楹M獠渴鸬某杀疽髽O高,故而只能合并到一個(gè)應(yīng)用做海外部署了,并借此機(jī)會把國內(nèi)的應(yīng)用也合并了。合并后,內(nèi)部還是以不同模塊做隔離,絲毫沒有因此增加層間污染的風(fēng)險(xiǎn)。
3.3 按領(lǐng)域切應(yīng)用
上圖是2022年接手的一個(gè)預(yù)算系統(tǒng)的架構(gòu)(考慮數(shù)據(jù)安全,畫的比較簡單),雖然其承接的業(yè)務(wù)效果很好,領(lǐng)域發(fā)展也不錯(cuò),但在系統(tǒng)設(shè)計(jì)和領(lǐng)域劃分上,是有些問題的。
我理解當(dāng)時(shí)團(tuán)隊(duì)是隨著預(yù)算發(fā)展對業(yè)務(wù)了解的深入,做了架構(gòu)演進(jìn)式的領(lǐng)域劃分,從對業(yè)務(wù)的認(rèn)知經(jīng)驗(yàn)和感覺出發(fā),劃成了動名結(jié)合體。分配、消耗、熔斷是動詞,賬戶、預(yù)算是名詞,按這些亦動亦名的關(guān)鍵概念劃了單個(gè)的域,并基于此單拎為系統(tǒng),db也是獨(dú)立的。這是第一點(diǎn),領(lǐng)域劃分方法論的問題。
全局設(shè)計(jì)上,出發(fā)點(diǎn)上希望對這些應(yīng)用有個(gè)分層,按照預(yù)算從生產(chǎn)到消耗的時(shí)序,從上到下劃為了分配域、消耗域、管控域。然而事實(shí)情況是這些動名詞組成的應(yīng)用間,呈現(xiàn)出上下左右互通的全網(wǎng)狀調(diào)用態(tài)。譬如,從時(shí)序上,分配在上、消耗在中、管控在下,然后管控通知業(yè)務(wù)系統(tǒng)熔斷活動前是需要查詢預(yù)算、賬戶的狀態(tài)、余額的,而這兩個(gè)名詞,卻是歸屬在上層的分配、消耗域系統(tǒng)中,導(dǎo)致了下層調(diào)用上層。這是第二點(diǎn),層次劃分角度的問題。
按域單拎應(yīng)用,帶來的巨大開支。譬如分配和賬戶,這在模型層面是兩個(gè)模型簇,但并不代表它們就必須是兩個(gè)應(yīng)用。
一次預(yù)算分配操作,要經(jīng)歷 1、建立新預(yù)算模型 2、將父預(yù)算下的賬戶余額轉(zhuǎn)出 3、新預(yù)算下新建賬戶,并接受轉(zhuǎn)入的余額,極簡流程大致如此。分割為兩個(gè)應(yīng)用,勢必涉及兩個(gè)應(yīng)用間的調(diào)用和數(shù)據(jù)一致性問題,而這本可是一個(gè)應(yīng)用里一個(gè)biz場景同一事務(wù)下對兩個(gè)domain服務(wù)的編排。這是第三點(diǎn),應(yīng)用切分粒度的問題。
當(dāng)前預(yù)算小組正在做重構(gòu)的設(shè)計(jì),目標(biāo)是解決幾個(gè)問題:
應(yīng)用太散,計(jì)劃會把圖中的預(yù)算運(yùn)營臺、分配、賬戶、熔斷四個(gè)應(yīng)用合一起,把兩個(gè)異步j(luò)ob合一起,至于消耗未合入主要是考慮這本質(zhì)上是一個(gè)對消息做準(zhǔn)實(shí)時(shí)異步聚合的事情,并非一個(gè)在線業(yè)務(wù)服務(wù),甚至未來是可以基于實(shí)時(shí)計(jì)算技術(shù)棧承載的; 在合并后的應(yīng)用內(nèi)部做分層、分域的劃分; 把微觀設(shè)計(jì)上過于復(fù)雜導(dǎo)致代碼晦澀難懂的點(diǎn),用更符合本質(zhì)簡潔易懂的設(shè)計(jì)方式再實(shí)現(xiàn)一遍。當(dāng)然,這是另外一個(gè)維度的話題,不展開了。
柚子快報(bào)激活碼778899分享:業(yè)務(wù)系統(tǒng)架構(gòu)實(shí)踐總結(jié)
推薦文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。