W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
本章節(jié)我們先講一講在軟件設(shè)計(jì)中,模塊化的一些設(shè)計(jì)和復(fù)用原則,然后再介紹?GoFrame
?框架的模塊化設(shè)計(jì),以便于大家更好地了解?GoFrame
?框架模塊化設(shè)計(jì)的思想。
模塊也稱作組件,是軟件系統(tǒng)中可復(fù)用的功能邏輯封裝單位。在不同的軟件架構(gòu)層次,模塊的概念會(huì)有些不太一樣。在開發(fā)框架層面,模塊是某一類功能邏輯的最小封裝單位。在Golang代碼層面中,我們也可以將package稱作模塊。
軟件進(jìn)行模塊化設(shè)計(jì)的目的,是為了使得軟件功能邏輯盡可能的解耦和復(fù)用,終極目標(biāo)也是為了保證軟件開發(fā)維護(hù)的效率和質(zhì)量。
REP
復(fù)用/發(fā)布等同原則復(fù)用/發(fā)布等同原則(?Release/Reuse Equivalency Principle
?):軟件復(fù)用的最小粒度應(yīng)等同于其發(fā)布的最小粒度。
直白地說,就是要復(fù)用一段代碼就把它抽成模塊。
CCP
共同閉包原則共同閉包原則(?Common Closure Principle
?):為了相同目的而同時(shí)修改的類,應(yīng)該放在同一個(gè)模塊中。
對大部分應(yīng)用程序而言,可維護(hù)性的重要性遠(yuǎn)遠(yuǎn)大于可復(fù)用性,由同一個(gè)原因引起的代碼修改,最好在同一個(gè)模塊中,如果分散在多個(gè)模塊中,那么開發(fā)、提交、部署的成本都會(huì)上升。
CRP
共同復(fù)用原則共同復(fù)用原則(?Common Reuse Principle
?):不要強(qiáng)迫一個(gè)模塊依賴它不需要的東西。
相信你一定有這種經(jīng)歷,集成了模塊A,但模塊A依賴了模塊B、C。即使模塊B、C 你完全用不到,也不得不集成進(jìn)來。這是因?yàn)槟阒挥玫搅四KA的部分能力,模塊A中額外的能力帶來了額外的依賴。如果遵循共同復(fù)用原則,你需要把A拆分,只保留你要用的部分。
?REP
?、?CCP
?、?CRP
三個(gè)原則之間存在彼此競爭的關(guān)系。?REP
和 ?CCP
是黏合性原則,它們會(huì)讓模塊變得更大,而 ?CRP
原則是排除性原則,它會(huì)讓模塊變小。遵守?REP
?、?CCP
而忽略 ?CRP
,就會(huì)依賴了太多沒有用到的模塊和類,而這些模塊或類的變動(dòng)會(huì)導(dǎo)致你自己的模塊進(jìn)行太多不必要的發(fā)布;遵守 ?REP
、?CRP
而忽略 ?CCP
?,因?yàn)槟K拆分的太細(xì)了,一個(gè)需求變更可能要改n個(gè)模塊,帶來的成本也是巨大的。
優(yōu)秀的架構(gòu)師應(yīng)該能在上述三角形張力區(qū)域中定位一個(gè)最適合目前研發(fā)團(tuán)隊(duì)狀態(tài)的位置,例如在項(xiàng)目早期,?CCP
?比?REP
?更重要,隨著項(xiàng)目的發(fā)展,這個(gè)最合適的位置也要不停調(diào)整。
經(jīng)過前面關(guān)于模塊設(shè)計(jì)原則和復(fù)用原則的介紹,我們應(yīng)該對模塊開發(fā)和管理這塊的原則有了大概的了解,那么我們接著介紹框架的模塊化設(shè)計(jì)就比較容易理解了。
根據(jù)?REP
?原則我們了解到,一個(gè)可復(fù)用的模塊是支持獨(dú)立版本管理的,單倉庫包設(shè)計(jì)也正是如此。Golang中很多這樣的單倉庫包,一個(gè)包就是一個(gè)獨(dú)立的模塊。單倉庫包根據(jù)?CRP
?原則可以再進(jìn)一步的細(xì)化解耦拆分。我們來舉個(gè)例子,在開發(fā)復(fù)雜的業(yè)務(wù)項(xiàng)目場景下,常見的包依賴情況,類似于這樣的:
module business
go 1.16
require (
business.com/golang/strings v1.0.0
business.com/golang/config v1.15.0
business.com/golang/container v1.1.0
business.com/golang/encoding v1.2.0
business.com/golang/files v1.2.1
business.com/golang/cache v1.7.3
business.com/framework/utils v1.30.1
github.com/pkg/errors v0.9.0
github.com/goorm/orm v1.2.1
github.com/goredis/redis v1.7.4
github.com/gokafka/kafka v0.1.0
github.com/gometrics/metrics v0.3.5
github.com/gotracing/tracing v0.8.2
github.com/gohttp/http v1.18.1
github.com/google/grpc v1.16.1
github.com/smith/env v1.0.2
github.com/htbj/command v1.1.1
github.com/kmlevel1/pool v1.1.4
github.com/anolog/logging v1.16.2
github.com/bgses123/session v1.5.1
github.com/gomytmp/template v1.3.4
github.com/govalidation/validate v1.19.2
github.com/yetme1/goi18n v0.10.0
github.com/convman/convert v1.20.0
github.com/google/uuid v1.1.2
// ...
)
示例中的模塊依賴,都是一些通用模塊,大部分業(yè)務(wù)項(xiàng)目都會(huì)涉及到。模塊地址是便于演示而寫的隨意地址,并不一定真實(shí)存在。
使用Golang開發(fā)過復(fù)雜一點(diǎn)的業(yè)務(wù)項(xiàng)目的小伙伴們,對于這樣的場景大家一定不會(huì)陌生。一個(gè)正常的軟件企業(yè),往往至少有數(shù)百個(gè)這樣的項(xiàng)目,真實(shí)的模塊依賴關(guān)系比這里的例子更加復(fù)雜。在Golang項(xiàng)目開發(fā)中,對于模塊依賴的維護(hù)性挑戰(zhàn)是比較大的,我們往往會(huì)遇到一些痛點(diǎn),主要的幾點(diǎn):
現(xiàn)身說法舉例。
本廠的自研模塊有數(shù)十個(gè),這些模塊已經(jīng)被頻繁使用遍布到數(shù)百個(gè)業(yè)務(wù)項(xiàng)目中。有一次,我們提交了對幾個(gè)模塊的?bug fix
?,其中有兩個(gè)還是比較重要的?bug
?,緊接著,我們要求所有業(yè)務(wù)項(xiàng)目全部升級(jí)一下對應(yīng)模塊的版本號(hào),并且這些版本號(hào)填寫得務(wù)必小心。當(dāng)然,這肯定也不是唯一的一次,隨后相同的場景各位同學(xué)可以自行腦補(bǔ)。
我們也可以選擇,不去主動(dòng)推進(jìn)所有業(yè)務(wù)項(xiàng)目升級(jí)模塊,只要項(xiàng)目還沒有觸發(fā)這些?bug
?,那么就等著業(yè)務(wù)項(xiàng)目踩到了坑再由項(xiàng)目組自行去升級(jí)。領(lǐng)導(dǎo)如果聽到這種解決方案......各位同學(xué)再自行腦補(bǔ)一下和諧的場景。
其實(shí)這種問題主要的原因,還是來源于模塊的不穩(wěn)定,模塊也是需要不停迭代改進(jìn)的。項(xiàng)目使用到這些模塊,那么就與這些模塊建立了耦合關(guān)系,耦合模塊的變化,必然會(huì)影響到依賴的相關(guān)項(xiàng)目。越底層的基礎(chǔ)模塊,頂層模塊則對其依賴的越多,影響面也就越大。那是不是只要模塊穩(wěn)定了,就不會(huì)存在這樣的問題了呢?風(fēng)險(xiǎn)依舊是存在的。Golang標(biāo)準(zhǔn)庫大家覺得算穩(wěn)定吧,但是它也是在不斷的迭代改進(jìn)過程中,也是不斷有bug出現(xiàn),只是大家幸運(yùn)沒踩上去而已,風(fēng)險(xiǎn)相對較低。
好的軟件設(shè)計(jì),并不是一成不變,而是能夠做到快速響應(yīng)變化,根據(jù)變化快速改進(jìn)完善。模塊的設(shè)計(jì)和管理,亦是如此。尋求能夠快速改進(jìn)模塊邏輯、有效維護(hù)模塊依賴的方案,比編寫更加穩(wěn)定的功能模塊,更加高效和務(wù)實(shí)。
?GoFrame
?的模塊化管理思想更偏重于?CCP
?原則,看重可維護(hù)性比可復(fù)用性更多。由于?GoFrame
?是基于開發(fā)框架層面的出發(fā)點(diǎn)考慮,因此整體框架的設(shè)計(jì)不是單點(diǎn)設(shè)計(jì)的,而是自頂向下設(shè)計(jì)的。前面有提到,越底層的基礎(chǔ)模塊,頂層模塊則對其依賴的越多,影響面也就越大。因此,框架將一些通用性的核心模塊進(jìn)行統(tǒng)一維護(hù),這樣做的目的是使得這些模塊共同形成閉包,保證基礎(chǔ)模塊的穩(wěn)定性,并通過統(tǒng)一的版本管理,提高開發(fā)效率和可維護(hù)性,降低接入和維護(hù)成本。
站在?GoFrame
?框架模塊化設(shè)計(jì)的角度,前面例子中的依賴情況應(yīng)當(dāng)變成以下的樣子:
module business
go 1.16
require (
github.com/gogf/gf v1.16.0
github.com/goorm/orm v1.15.1
github.com/goredis/redis v1.7.4
github.com/gokafka/kafka v0.1.0
github.com/google/grpc v1.16.1
// ...
)
?GoFrame
?只維護(hù)一些通用性的核心模塊,其他非通用核心模塊或者穩(wěn)定性較高的模塊,依舊建議使用單倉庫包的形式進(jìn)行依賴引入,正如?REP
?和?CRP
?模塊復(fù)用原則倡導(dǎo)的那樣。在這種設(shè)計(jì)模式下:
文件層面的源文件下載與模塊之間的邏輯耦合沒有直接關(guān)系。需要注意的是,編譯型語言和解釋型語言的模塊管理邏輯不太一樣。
main
?包為入口,編譯器會(huì)自動(dòng)分析源碼并將所有邏輯依賴模塊中對應(yīng)的資源進(jìn)行編譯處理,最終生成為靜態(tài)二進(jìn)制文件進(jìn)行發(fā)布,自身源文件以及依賴模塊(邏輯依賴)的源文件只在編譯階段使用,源碼文件并不會(huì)直接用于發(fā)布,如:C/C++、Golang、Rust等。最主要的一點(diǎn),框架的模塊設(shè)計(jì)也會(huì)充分考慮穩(wěn)定性因素,僅會(huì)將一些通用性的核心模塊按照?CCP
?進(jìn)行管理,并不會(huì)包含特定業(yè)務(wù)的邏輯封裝,因?yàn)樯婕暗教囟I(yè)務(wù)的功能邏輯實(shí)現(xiàn)將會(huì)為框架模塊帶來更多的不穩(wěn)定變化。
在保證一定的穩(wěn)定性前提下,模塊的版本發(fā)布按照框架統(tǒng)一的迭代開發(fā)計(jì)劃進(jìn)行,除了必要的?hot fix
?之外,版本發(fā)布設(shè)置有固定的時(shí)間窗口,以保證框架核心的穩(wěn)定性。因此,框架通過模塊聚合的方式進(jìn)行版本管理,不僅沒有增加框架的版本發(fā)布頻次,反而降低了框架的版本發(fā)布頻次,使得框架中的模塊版本更加穩(wěn)定。
首先,它們是基礎(chǔ)模塊,往往位于模塊依賴鏈的最底層,這部分的模塊變化對項(xiàng)目穩(wěn)定性影響最大。
其次,絕大部分項(xiàng)目(二八定律來講為80%以上)都會(huì)依賴的通用性基礎(chǔ)模塊,可以稱作核心模塊。
最后,這部分模塊不包含具體業(yè)務(wù)的封裝實(shí)現(xiàn)。例如:微信公眾號(hào)/小程序、CMS/CRM、區(qū)塊鏈等相關(guān)模塊都是具體業(yè)務(wù)實(shí)現(xiàn)封裝。
關(guān)于模塊通用性的評(píng)估無法完全準(zhǔn)確,框架為保證核心精簡會(huì)盡可能持保守態(tài)度,并且會(huì)根據(jù)實(shí)際情況在未來的迭代中逐步做調(diào)整。
以下為可供參考的模塊分層:
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: