用实战项目经验告诉你什么是二方包!
前言
只有光頭才能變強(qiáng)。
文本已收錄至我的GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3y
最近在整合各種的系統(tǒng),在這個(gè)過程中遇到了各種的問題,三歪今天來分享一下關(guān)于「項(xiàng)目結(jié)構(gòu)」或者說「二方包」的事。
我們先不聊「二方包」,因?yàn)槌鯇W(xué)或者還沒工作的同學(xué)可能沒聽過這個(gè)詞。
初學(xué)的時(shí)候或者剛做項(xiàng)目的時(shí)候,我們的項(xiàng)目架構(gòu)是怎么樣的呢?我翻出了我在大學(xué)的時(shí)候?qū)懙男emo:
可以看到的是,我們的項(xiàng)目只有一個(gè)Module,里邊我們分各種的包:dao/service/controller/utils...等等。
這看起來好像沒啥問題吧?
我們?nèi)サ焦纠镞?#xff0c;可能看到的項(xiàng)目都分了多個(gè)Module,比如下圖:
有什么區(qū)別呢?我們用一個(gè)Module在里邊分各種的子包,看起來也還行。為什么我們要分多個(gè)Module呢?
我個(gè)人認(rèn)為原因是這樣的:Maven本身就支持多模塊(Module)的管理,將不同的層分出來,項(xiàng)目看起來更加清晰,在改動(dòng)的時(shí)候針對(duì)某個(gè)模塊去變更就好了。
- 比如,我把dao層分成一個(gè)模塊,當(dāng)我變更dao這個(gè)模塊的時(shí)候,我只需要關(guān)心這個(gè)模塊就好了,不需要關(guān)心service模塊或者web模塊
- 舉個(gè)例子:每層幾乎都會(huì)有自己的配置信息(配置文件),數(shù)據(jù)訪問層會(huì)有數(shù)據(jù)庫的配置文件、業(yè)務(wù)層也會(huì)有對(duì)應(yīng)的配置文件。我們抽出多個(gè)Module(不同的Module放屬于自己的配置文件),從代碼結(jié)構(gòu)層面上會(huì)顯得更加清晰。
我們寫完的代碼是需要維護(hù)的,可維護(hù)性很重要。
很多編程方式客觀上沒有對(duì)錯(cuò)之分,一致性很重要,可讀性很重要,團(tuán)隊(duì)溝通效率很重要。程序員天生需要團(tuán)隊(duì)協(xié)作,而協(xié)作的正能量要放在問題的有效溝通上。個(gè)性化應(yīng)盡量表現(xiàn)在系統(tǒng)架構(gòu)和算法效率的提升上,而不是在合作規(guī)范上進(jìn)行糾纏不休的討論、爭論,最后沒有結(jié)論。
二方包
當(dāng)年我看《阿里巴巴開發(fā)手冊(cè)》的時(shí)候也寫過一篇總結(jié),當(dāng)時(shí)的文章也提到了那時(shí)候不咋懂二方包,現(xiàn)在我回來填坑了。
首先來科普一下什么是二方庫?(二方庫也叫·)
- 一方庫指的是本項(xiàng)目中的依賴
- 二方庫指的是公司內(nèi)部其他項(xiàng)目提供的依賴
- 三方庫指的是其他組織、公司等來自第三方的依賴
如果看過我之前文章的同學(xué)都知道,三歪在公司目前維護(hù)的是消息管理平臺(tái),全公司發(fā)送的消息都會(huì)經(jīng)過我的系統(tǒng)。
我會(huì)打個(gè)「二方包」到公司的Maven倉庫,然后他們引入我的pom依賴,調(diào)用我的接口去下發(fā)消息。
假如我沒有分Module,所有的代碼都寫在同一個(gè)Module下,那我發(fā)到公司的Maven倉庫會(huì)發(fā)生什么?打包(deploy)這個(gè)過程是沒毛病的。
如果他們依賴了我這個(gè)沒有處理過的二方包,相當(dāng)于把我整個(gè)工程給依賴進(jìn)去了,這是非常可怕的。
如果有從零搭過系統(tǒng)或者整合過系統(tǒng)的同學(xué)會(huì)知道,這個(gè)過程有會(huì)有多「版本」的坑。只要版本不一致,就會(huì)出現(xiàn)一大堆奇奇怪怪的問題,并且這些問題都不太好解決。
所以,一般我們的二方包都應(yīng)該是很清爽的。比如我提供的二方包,它就只有接口和接口所需要的實(shí)體,沒有雜余的繁瑣依賴。
業(yè)務(wù)方是不關(guān)心接口的實(shí)現(xiàn)的,我們只需要暴露接口就好了。
三歪一次經(jīng)歷
最近三歪在整合系統(tǒng)嘛,分享一次經(jīng)歷。
我負(fù)責(zé)的消息管理平臺(tái)系統(tǒng)的架構(gòu)是這樣的:
可以看到,我所負(fù)責(zé)的系統(tǒng)是分得很細(xì)的(很符合分布式的理念)。從功能上看,這些系統(tǒng)變成一個(gè)大工程也不是什么問題,只是這個(gè)系統(tǒng)會(huì)非常非常大。
消息管理平臺(tái)除了上面的系統(tǒng),還有其他的子系統(tǒng),比如說「ID映射」。這里當(dāng)初設(shè)計(jì)的時(shí)候也把它當(dāng)做一個(gè)系統(tǒng)給抽出去了。
「ID映射」應(yīng)該不難理解:業(yè)務(wù)方傳入的是站內(nèi)的userId,但要發(fā)的是短信。我這邊要把userId轉(zhuǎn)換成手機(jī)號(hào),可以簡單理解這個(gè)系統(tǒng)就做的這么一個(gè)ID轉(zhuǎn)換的功能。
現(xiàn)在要規(guī)劃把這個(gè)「ID映射」給整合起來,原有的機(jī)器要下線了,要把這個(gè)服務(wù)的功能給整到消息管理平臺(tái)的系統(tǒng)中。(為啥要整到我這個(gè)架構(gòu)上?因?yàn)楸旧磉@個(gè)系統(tǒng)就只有我這邊在頻繁使用)
為啥要整合?現(xiàn)在的潮流可能是把單個(gè)系統(tǒng)拆出多個(gè)系統(tǒng)做”微服務(wù)“,但真正系統(tǒng)多起來未必是一件好事。
一些小的功能其實(shí)沒必要單獨(dú)分出來一個(gè)系統(tǒng),這是需要成本的,至少我們機(jī)器成本是有的(一個(gè)系統(tǒng)我們至少會(huì)有四臺(tái)機(jī)器)->兩臺(tái)線上,一臺(tái)預(yù)發(fā),一臺(tái)線下
OK,說完背景了以后,我們?cè)賮砜纯础窱D映射」這個(gè)系統(tǒng)的代碼架構(gòu):
說白了,就是這個(gè)工程下有這三個(gè)Module,如果你要問我為什么沒看到dao層的Module,而是直接放到core 層,問就是當(dāng)初設(shè)計(jì)不合理。依我的理解,應(yīng)該至少是要把數(shù)據(jù)訪問層抽出一個(gè)Module。
可以看到的是,這個(gè)「ID映射」系統(tǒng)其實(shí)也是一個(gè)完整的系統(tǒng),從后臺(tái)管理頁面到數(shù)據(jù)庫都是完整的。
現(xiàn)在要把這個(gè)系統(tǒng)給整到消息管理平臺(tái)的架構(gòu)下,如果是你,你會(huì)怎么弄呢?
其實(shí)就兩種方式:
最簡單的做法肯定是把這個(gè)系統(tǒng)的代碼搬到另一個(gè)系統(tǒng),然后就可以run起來了。但前人已經(jīng)把系統(tǒng)分得那么干凈了,如果我這樣干了,后面接手的人會(huì)不會(huì)錘我呢?
三歪認(rèn)為服務(wù)相關(guān)的代碼,應(yīng)該就整合到專門提供服務(wù)的系統(tǒng)。后臺(tái)相關(guān)的代碼,就應(yīng)該整合到后臺(tái)相關(guān)的系統(tǒng)。即便我后臺(tái)系統(tǒng)發(fā)布了,絲毫不影響對(duì)外提供的服務(wù)。
所以,我決定把「ID映射」的各個(gè)Module抽出來,分到不同的系統(tǒng)中。把a(bǔ)pi層和部分core層的代碼的Module分到service系統(tǒng)中,把web層的Module分到admin系統(tǒng)中。
看似是挺完美的,我當(dāng)初也是這樣執(zhí)行的,于是我順利把a(bǔ)pi和core的代碼整合到service系統(tǒng)之后,正打算把web的代碼整合到admin系統(tǒng)中,遇到了一個(gè)問題。
web的代碼是controller層,它顯然會(huì)依賴service層的代碼,service層的代碼也顯然會(huì)依賴dao的代碼。
現(xiàn)在我已經(jīng)把a(bǔ)pi/core層的代碼大多數(shù)已經(jīng)遷到了service系統(tǒng),而admin系統(tǒng)是沒有這些代碼和依賴的,我需要整個(gè)系統(tǒng)是能跑通的,我能怎么辦?
此時(shí)能想到的有兩種方案:
把service系統(tǒng)的代碼再到admin系統(tǒng)中實(shí)現(xiàn)。
現(xiàn)在service系統(tǒng)已經(jīng)實(shí)現(xiàn)好了,打個(gè)二方包,然后讓admin系統(tǒng)依賴。這里也有兩個(gè)方案:
如果是你,你會(huì)選擇哪種?我們來分析一下:
- 第一種方案:service系統(tǒng)的代碼再到admin系統(tǒng)實(shí)現(xiàn),這肯定會(huì)有代碼冗余的情況。畢竟service系統(tǒng)肯定會(huì)依賴dao層的,而admin系統(tǒng)最終也是需要依賴dao層。dao層沒有完全抽出,在前期就肯定會(huì)有代碼冗余的情況
- 第二種方案分支①:將service系統(tǒng)已實(shí)現(xiàn)的代碼直接打成二方包,admin系統(tǒng)直接引入就沒有任何代碼冗余的問題。但這會(huì)引發(fā)其他問題:
- 直接打成二方包意味著要把所有的實(shí)現(xiàn)依賴都打進(jìn)去,admin系統(tǒng)在引入的時(shí)候需要針對(duì)這個(gè)二方包做一系列的排包操作。(這個(gè)非常蛋疼)
- 其實(shí)最致命的是我們干不了。我們的系統(tǒng)在發(fā)布的時(shí)候是分環(huán)境的(線上、預(yù)發(fā)、線下),我們會(huì)在發(fā)布的時(shí)候根據(jù)不同的環(huán)境使用不同的配置。如果我們此時(shí)直接打個(gè)二方包,我們是需要指定環(huán)境的,這說明我們只能用一個(gè)環(huán)境的配置,這是行不通的。
- 第二種方案分支②:把a(bǔ)dmin系統(tǒng)所依賴的接口再出一個(gè)api層,實(shí)現(xiàn)遠(yuǎn)程調(diào)用。從字面上,這是最優(yōu)雅的方案,但如果admin系統(tǒng)依賴的接口眾多的時(shí)候,實(shí)際踐行的時(shí)候會(huì)發(fā)現(xiàn)這工作量巨大。
- admin系統(tǒng)所依賴的接口有40+個(gè),這些接口所依賴的實(shí)體有80+個(gè)。我搞了大半天,發(fā)現(xiàn)完全搞不動(dòng),工作量巨大!!這是單純的體力活
第二個(gè)方案的分支①三歪再來聊聊為什么說干不了,首先我們的實(shí)現(xiàn)是有配置的
我們?cè)赿eploy的時(shí)候就必須指定一個(gè)環(huán)境,比如我們默認(rèn)選擇線上環(huán)境的配置好了。打完包以后,這個(gè)包默認(rèn)的環(huán)境就是給線上使用的。但是admin系統(tǒng)他還需要在線下環(huán)境啟動(dòng),怎么辦?沒辦法吧?
假設(shè)環(huán)境配置的問題能解決,等著我們還有各種依賴的問題。admin系統(tǒng)和二方包的依賴沖突了怎么辦?
比如說:相同的配置項(xiàng),不同的配置信息。在service系統(tǒng)上能兼容(畢竟代碼都在同一個(gè)工程下),但直接打成二方包就沒法跟admin系統(tǒng)兼容了。
這只能刪除沖突的部分,然后再發(fā)包了吧?但沖突是admin系統(tǒng)沖突的,跟我service系統(tǒng)的有啥關(guān)系呢?這我要做成一個(gè)完美兼容admin和service系統(tǒng)的Module嗎?老實(shí)說,不太現(xiàn)實(shí)。
扯了一大堆,三歪最終選擇的是第一個(gè)方案。
在初期現(xiàn)在dao層的代碼肯定是在admin和service系統(tǒng)上冗余的。service層不好抽取,但dao層還是相對(duì)好抽取的啊。
為什么dao和service層的代碼不一樣?明明我在上面已經(jīng)講了怎么多直接抽取二方包的不可行性了。
dao層的代碼相較于service層的代碼要簡單多了,一個(gè)合格dao層應(yīng)該是只管訪問數(shù)據(jù)庫,不應(yīng)該有dao層的代碼去調(diào)service層的代碼的。
代碼的簡單意味著依賴會(huì)很少,比如我們的dao層就應(yīng)該只有Mybatis和Spring相關(guān)的依賴,其余的就不應(yīng)該有。而前面提到的環(huán)境配置的問題,我們可以交給業(yè)務(wù)方去實(shí)現(xiàn),不在dao層上做任何配置信息,開個(gè)hook給業(yè)務(wù)方去做。
像三歪這種”微服務(wù)“系統(tǒng),本應(yīng)該要把dao層給抽出來。再回看我的系統(tǒng)架構(gòu)圖,可以發(fā)現(xiàn)會(huì)有幾個(gè)系統(tǒng)都需要依賴dao,如果沒有抽出來,這必會(huì)冗余,冗余的代碼意味著不好維護(hù)。
規(guī)范
之前看不懂的阿里巴巴開發(fā)手冊(cè),現(xiàn)在能看懂了。我建議有空的時(shí)候還是可以看看的,都說得挺有道理的。
阿里大佬們踩了一堆坑,然后總結(jié)出來的規(guī)范。
【強(qiáng)制】二方庫的新增或升級(jí),保持除功能點(diǎn)之外的其它 jar 包仲裁結(jié)果不變。如果有改變,
必須明確評(píng)估和驗(yàn)證,建議進(jìn)行 dependency:resolve 前后信息比對(duì),如果仲裁結(jié)果完全不一
致,那么通過 dependency:tree 命令,找出差異點(diǎn),進(jìn)行<excludes>排除 jar 包。
【參考】為避免應(yīng)用二方庫的依賴沖突問題,二方庫發(fā)布者應(yīng)當(dāng)遵循以下原則:
1)精簡可控原則。移除一切不必要的 API 和依賴,只包含 Service API、必要的領(lǐng)域模型對(duì)
象、Utils 類、常量、枚舉等。如果依賴其它二方庫,盡量是 provided 引入,讓二方庫使用
者去依賴具體版本號(hào);無 log 具體實(shí)現(xiàn),只依賴日志框架。
2)穩(wěn)定可追溯原則。每個(gè)版本的變化應(yīng)該被記錄,二方庫由誰維護(hù),源碼在哪里,都需要能
方便查到。除非用戶主動(dòng)升級(jí)版本,否則公共二方庫的行為不應(yīng)該發(fā)生變化。
…
三歪瞎扯
為什么我們會(huì)用多模塊(Module)?其實(shí)就是讓我們的項(xiàng)目代碼變得更加清晰,像對(duì)外服務(wù)的api層就必須要抽出一個(gè)精簡的Module給別人使用。
不知道你們?nèi)绻芸赐赀@篇文章能不能有所啟發(fā),反正我已經(jīng)寫完了。如果你們感興趣的話,等我整合完這些系統(tǒng)我再來寫一篇關(guān)于這段時(shí)間的感受。
我是三歪,一個(gè)要成為光頭強(qiáng)的男人,下期見。給三歪點(diǎn)個(gè)贊,對(duì)三歪真的非常重要!**
總結(jié)
以上是生活随笔為你收集整理的用实战项目经验告诉你什么是二方包!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python离线安装第三方包
- 下一篇: node第三方包