架構(gòu)設(shè)計(jì)原則:SPI 與 API 該如何選擇?
背景
第一次聽說 SPI 是閱讀《軟件框架設(shè)計(jì)的藝術(shù)》,以后陸續(xù)在 JDBC 和 SpringBoot 中發(fā)現(xiàn)了以這種形式組織代碼的方式,本文給出為什么要區(qū)分 SPI 和 API 的一個(gè)思考過程。
從面向接口編程說起
圖片
我們在“調(diào)用方”和“實(shí)現(xiàn)方”之間引入了“接口”,上圖沒有給出“接口”應(yīng)該位于哪個(gè)“包”中,從純粹的可能性上考慮,我們有三種選擇:
- “接口”位于“調(diào)用方”所在的“包”中。
- “接口”位于“實(shí)現(xiàn)方”所在的“包”中。
- “接口”位于獨(dú)立的“包”中。
下面讓我們依次分析這三種可能性,如果現(xiàn)實(shí)中確實(shí)有這種可能性,不如我們就為其起個(gè)名字以方便交流。
“接口”位于“調(diào)用方”所在的“包”中
圖片
我們將“倉儲接口”放置于“領(lǐng)域?qū)印边@個(gè)“包”中,實(shí)現(xiàn)放在一個(gè)獨(dú)立的“包”中,我們看DDD大師的實(shí)現(xiàn)都是這樣子,現(xiàn)在來思考一下為什么這么做。
“領(lǐng)域?qū)印钡摹邦I(lǐng)域服務(wù)”會依賴“倉儲接口”,“倉儲接口”也會依賴“聚合根”,這兩者都是除了“實(shí)現(xiàn)依賴”之外的依賴關(guān)系,如果將“接口”放到“倉儲實(shí)現(xiàn)”中就喪失了面向接口編程的意義(編譯也不會通過),如果放到“獨(dú)立層”中呢?會編譯不通過,出現(xiàn)雙向依賴了。
對于類似這種情況下接口,我們將其稱為“SPI”,全稱為:service provider interface,“SPI”的規(guī)則如下:
- 概念上更依賴調(diào)用方。
- 組織上位于調(diào)用方所在的包中。
- 實(shí)現(xiàn)位于獨(dú)立的包中。
- 常見的例子是:插件模式的插件。
“接口”位于“實(shí)現(xiàn)方”所在的“包”中
對于類似這種情況下的接口,我們將其稱作為“API”,“API”的規(guī)則如下:
- 概念上更接近實(shí)現(xiàn)方。
- 組織上位于實(shí)現(xiàn)方所在的包中。
- 實(shí)現(xiàn)和接口在一個(gè)包中。
“接口”位于獨(dú)立的“包”中
需要注意的事項(xiàng)
SPI 和 API 也不一定是接口,我這里都是指狹義的具體的接口。
場景圖
圖片
每一次思考都伴隨著收獲,也離不開和朋友們的交流,天更藍(lán)了。
SPI 接口
- 定義:SPI 是一種服務(wù)提供者接口,它允許在運(yùn)行時(shí)加載不同的服務(wù)實(shí)現(xiàn)。
- 使用場景:
模塊化設(shè)計(jì):當(dāng)系統(tǒng)需要高度模塊化,且希望將核心功能與具體實(shí)現(xiàn)分離時(shí)。
可插拔架構(gòu):需要支持多種服務(wù)實(shí)現(xiàn),并且可以在不修改代碼的情況下替換或增加新的服務(wù)實(shí)現(xiàn)。
服務(wù)發(fā)現(xiàn):在運(yùn)行時(shí)根據(jù)配置或服務(wù)注冊表動態(tài)發(fā)現(xiàn)和加載服務(wù)。
微服務(wù)架構(gòu):在微服務(wù)架構(gòu)中,SPI 可用于服務(wù)間的動態(tài)交互和集成。
- 優(yōu)點(diǎn):
- 提供了一種機(jī)制來在運(yùn)行時(shí)選擇和加載服務(wù)實(shí)現(xiàn),增加了系統(tǒng)的靈活性和可擴(kuò)展性。
- 支持服務(wù)的熱插拔,無需重啟系統(tǒng)即可更換服務(wù)實(shí)現(xiàn)。
API 接口
- 定義:API 是一組預(yù)定義的函數(shù)、協(xié)議和工具,用于構(gòu)建軟件應(yīng)用,它定義了軟件組件之間交互的契約。
- 使用場景:
客戶端和服務(wù)器交互:當(dāng)需要設(shè)計(jì)客戶端和服務(wù)器之間的通信協(xié)議時(shí)。
庫和框架:提供給開發(fā)者使用的庫或框架的公共接口。
第三方集成:需要與第三方系統(tǒng)或服務(wù)進(jìn)行集成。
內(nèi)部組件通信:在大型系統(tǒng)中,不同組件或模塊之間的交互。
- 優(yōu)點(diǎn):
- 為開發(fā)者提供了清晰的接口文檔和規(guī)范,易于理解和使用。
- 有助于保持系統(tǒng)的穩(wěn)定性,因?yàn)?API 變更需要遵循版本控制和兼容性規(guī)則。
- 促進(jìn)了代碼的重用和模塊化。
如何選擇?
選擇使用 SPI 還是 API 的考慮因素:
- 擴(kuò)展性:如果需要在不修改代碼的情況下擴(kuò)展功能,SPI 更合適。
- 交互性:API 更適合定義系統(tǒng)內(nèi)部或系統(tǒng)之間的穩(wěn)定交互接口。
- 動態(tài)性:SPI 允許在運(yùn)行時(shí)動態(tài)發(fā)現(xiàn)和加載服務(wù),而 API 通常在編譯時(shí)就已經(jīng)確定。
- 安全性和穩(wěn)定性:API 由于其穩(wěn)定性和可預(yù)測性,通常更受青睞。SPI 雖然靈活,但可能引入運(yùn)行時(shí)錯(cuò)誤。
- 版本控制和兼容性:API 變更需要考慮版本控制和向后兼容性,而 SPI 可以通過服務(wù)版本協(xié)商來處理兼容性問題。
架構(gòu)是“取舍”,而非“銀彈”。






























