在軟件架構(gòu)領(lǐng)域,網(wǎng)上討論最廣泛的架構(gòu)之一是整潔架構(gòu)(Clean Architecture)。它通過將項(xiàng)目劃分為多個(gè)層級,實(shí)現(xiàn)關(guān)注點(diǎn)分離,從而提升代碼的可維護(hù)性和可擴(kuò)展性。
圖片
每一層都遵循單一職責(zé)原則,確保每個(gè)類只負(fù)責(zé)一部分邏輯,不僅使系統(tǒng)結(jié)構(gòu)更清晰,也極大地方便了單元測試的編寫與執(zhí)行。
整潔架構(gòu)的核心理念可以概括為:
依賴關(guān)系向內(nèi)指向“業(yè)務(wù)核心”,外層可以依賴內(nèi)層,但內(nèi)層絕不可以反向依賴外層。
換句話說,沒有哪一層可以看到比它更高層的細(xì)節(jié)。它們可以引用自己的子層,但從不允許跨層依賴或反向耦合。
這種設(shè)計(jì)思想不僅適用于整體系統(tǒng)架構(gòu),在具體場景如 API 開發(fā)中同樣具有重要價(jià)值。那么,如何將 Clean Architecture 的理念應(yīng)用到 API 端點(diǎn)的設(shè)計(jì)中?這就引出了“Clean API 架構(gòu)”這一實(shí)踐模式:
圖片
它將接口層、應(yīng)用服務(wù)、領(lǐng)域邏輯和數(shù)據(jù)訪問等模塊清晰隔離,使 API 結(jié)構(gòu)更加清晰、易于測試和長期演進(jìn)。
1. 框架:從請求入口到架構(gòu)分層
在現(xiàn)代 Web 系統(tǒng)中,任何一個(gè) API 請求通常都需要經(jīng)過多個(gè)層級的處理——從負(fù)載均衡器、Web 服務(wù)器,到應(yīng)用服務(wù)器,最終由具體的 API 或 Web 框架將請求路由到正確的代碼路徑。目前主流的開發(fā)框架如 Rails、Django 和 Spring Boot 等都提供了豐富的文檔支持和成熟的生態(tài)體系,是大多數(shù)開發(fā)者首選的技術(shù)棧。
然而,當(dāng)請求真正進(jìn)入框架并開始業(yè)務(wù)邏輯處理后,不同系統(tǒng)的設(shè)計(jì)路徑往往開始顯著分化。
以 Rails 為例,其采用經(jīng)典的 MVC(Model-View-Controller)架構(gòu),在小型項(xiàng)目中表現(xiàn)優(yōu)異,結(jié)構(gòu)清晰且上手成本低。但對于大型、高可用性的 API 系統(tǒng)而言,這種模式逐漸顯露出局限性——控制器和模型容易膨脹為臃腫的“上帝類”,違背了單一職責(zé)原則,導(dǎo)致維護(hù)困難、測試復(fù)雜。
正因如此,在框架層級之下,我們所構(gòu)建的整個(gè)系統(tǒng)設(shè)計(jì)更加注重解耦與可擴(kuò)展性,并深受 Clean Architecture 的啟發(fā)。這套設(shè)計(jì)理念強(qiáng)調(diào)業(yè)務(wù)邏輯應(yīng)獨(dú)立于外部依賴(如數(shù)據(jù)庫、UI、框架等),從而提升系統(tǒng)的可測試性和長期可維護(hù)性。
在本系列后續(xù)內(nèi)容中,我們將深入剖析我們是如何按照這一思想來組織各層級代碼的。
在每一層中,我們會(huì)定義一個(gè)或多個(gè)單一職責(zé)的支持類,它們只服務(wù)于當(dāng)前層級,不引用上下層的具體實(shí)現(xiàn)。這種嚴(yán)格隔離不僅有助于代碼復(fù)用,也有效避免了層級混亂和過度耦合的問題。
此外,雖然我們在架構(gòu)中使用了諸如 AWS EC2、SQS、RDS 和 ElastiCache 等云服務(wù),但這些服務(wù)在我們的設(shè)計(jì)中被作為框架層的輔助工具類存在,而非核心業(yè)務(wù)邏輯的一部分。正如 Clean Architecture 所強(qiáng)調(diào)的那樣:業(yè)務(wù)規(guī)則不應(yīng)依賴于基礎(chǔ)設(shè)施或數(shù)據(jù)存儲(chǔ)方式,而應(yīng)保持完全的獨(dú)立。這也確保了我們的系統(tǒng)具備更強(qiáng)的可移植性與靈活性。
2. 接口適配器層:連接外部世界與核心邏輯的橋梁
當(dāng)一個(gè)請求穿越框架層,進(jìn)入系統(tǒng)內(nèi)部時(shí),接口適配器層便開始發(fā)揮作用。這一層的核心職責(zé)是將外部輸入轉(zhuǎn)化為內(nèi)部可理解的數(shù)據(jù)結(jié)構(gòu),并將應(yīng)用邏輯的執(zhí)行結(jié)果以合適的格式返回給調(diào)用者。
在這一過程中,控制器(Controller)扮演著協(xié)調(diào)者的角色。它首先通過 Request 對象提取請求參數(shù),驗(yàn)證其語法格式,并完成用戶身份認(rèn)證等前置操作。隨后,控制器實(shí)例化相應(yīng)的業(yè)務(wù)類,驅(qū)動(dòng)數(shù)據(jù)在不同層級之間的流轉(zhuǎn),從而啟動(dòng)真正的應(yīng)用邏輯處理流程。
值得注意的是,控制器并不是接口適配器層中唯一負(fù)責(zé)業(yè)務(wù)流程的對象。我們還引入了 Jobs(任務(wù)),用于處理異步隊(duì)列相關(guān)的操作——這部分內(nèi)容將在后續(xù)章節(jié)中詳細(xì)展開。
為了確保系統(tǒng)的清晰分層與職責(zé)分離,控制器依賴于多個(gè)輔助類:
- Validators(驗(yàn)證器)
負(fù)責(zé)檢查輸入數(shù)據(jù)的合法性,確保進(jìn)入系統(tǒng)的信息符合預(yù)期格式; - Presenters(展示器)
專注于輸出數(shù)據(jù)的格式化處理,為上層邏輯提供統(tǒng)一的數(shù)據(jù)視圖; - Response(響應(yīng))對象
則承擔(dān)最終輸出的封裝工作,能夠?qū)?shù)據(jù)轉(zhuǎn)換為 JSON、HAML 或其他客戶端可識別的格式返回。
此外,系統(tǒng)中還包含一種特殊的適配器——套接字中繼類(Socket Relay),它通過 WebSocket 等通信通道,實(shí)時(shí)將狀態(tài)變更推送給客戶端,實(shí)現(xiàn)雙向通信能力。
Request 類則是一個(gè)類型化的數(shù)據(jù)結(jié)構(gòu),聚合了當(dāng)前請求所需的所有信息。與傳統(tǒng) HTTP 請求(通常是以鍵值對形式存在的 CGI 風(fēng)格請求)不同,這種設(shè)計(jì)提供了更強(qiáng)的類型安全性和結(jié)構(gòu)清晰性。
Response 類的功能類似于 Rails 中的渲染器,但它更加靈活,支持多種輸出格式,如 HAML、JSON 或自定義類型,便于構(gòu)建多端兼容的 API 響應(yīng)。
最后,參數(shù)提取器(Parameter Extractor) 從原始的 params 散列中提取數(shù)據(jù),并將其轉(zhuǎn)換為正確的類型,如整數(shù)、浮點(diǎn)數(shù)或字符串,為后續(xù)邏輯提供強(qiáng)類型的輸入保障。
整體而言,接口適配器層作為系統(tǒng)的“翻譯官”,在外部請求與內(nèi)部邏輯之間建立起高效、清晰的橋梁,是實(shí)現(xiàn) Clean Architecture 分層思想的重要一環(huán)。
3. 應(yīng)用邏輯層:業(yè)務(wù)流轉(zhuǎn)的核心引擎
在 Clean API 架構(gòu)中,應(yīng)用邏輯層是整個(gè)系統(tǒng)真正開始處理業(yè)務(wù)需求的地方。它承接來自接口適配器層的請求,并協(xié)調(diào)數(shù)據(jù)驗(yàn)證、權(quán)限控制、外部調(diào)用以及最終的業(yè)務(wù)執(zhí)行。
對于 GET 請求這類讀取型端點(diǎn),請求一旦進(jìn)入該層,首先由服務(wù)類(Service)進(jìn)行處理。服務(wù)對象負(fù)責(zé)確保輸入?yún)?shù)的有效性,驗(yàn)證用戶是否有權(quán)限訪問目標(biāo)資源,并通過 Repo(用于數(shù)據(jù)庫操作) 或 Adapter(用于外部 API 調(diào)用) 從實(shí)體邏輯層獲取所需數(shù)據(jù)。
在數(shù)據(jù)獲取完成后,服務(wù)對象將結(jié)果封裝為一個(gè)由 Result 對象返回。這種設(shè)計(jì)不僅統(tǒng)一了成功與失敗的返回結(jié)構(gòu),也便于上層(如控制器)根據(jù)結(jié)果類型做出相應(yīng)的響應(yīng)決策。
而對于 POST、PUT 和 DELETE 等寫入型請求,應(yīng)用邏輯的處理流程類似,但引入了異步機(jī)制以提升性能和可靠性。服務(wù)對象仍然負(fù)責(zé)驗(yàn)證輸入、授權(quán)用戶,并準(zhǔn)備寫入所需的數(shù)據(jù)。不同之處在于,這些變更操作會(huì)被包裝并提交到我們的任務(wù)隊(duì)列(基于 Amazon SQS)中排隊(duì),交由后臺(tái)的作業(yè)(Job)或異步服務(wù)來執(zhí)行真正的數(shù)據(jù)寫入操作。這種方式既減輕了主流程的壓力,也增強(qiáng)了系統(tǒng)的容錯(cuò)能力和可擴(kuò)展性。
此外,作業(yè)還承擔(dān)著觸發(fā)副作用的職責(zé)。例如,在數(shù)據(jù)持久化完成之后,作業(yè)可以通過 Relay 模塊向客戶端發(fā)送 WebSocket 消息,實(shí)時(shí)通知狀態(tài)變更,實(shí)現(xiàn)前后端之間的即時(shí)反饋。
值得一提的是,在本架構(gòu)中,Service 類還會(huì)組合一組專門的 Validator 類,對請求內(nèi)容進(jìn)行語義級別的驗(yàn)證。這意味著我們在系統(tǒng)中構(gòu)建了雙層驗(yàn)證機(jī)制:
- 語法驗(yàn)證發(fā)生在請求層,確保傳入的數(shù)據(jù)格式正確;
- 語義驗(yàn)證則在應(yīng)用邏輯層進(jìn)行,確保數(shù)據(jù)在業(yè)務(wù)規(guī)則下是合理且合法的。
這種分層驗(yàn)證策略顯著提升了系統(tǒng)的健壯性,避免了無效或非法數(shù)據(jù)對核心業(yè)務(wù)邏輯造成干擾,同時(shí)也使代碼更具可測試性和可維護(hù)性。
4. 實(shí)體邏輯層:業(yè)務(wù)規(guī)則與數(shù)據(jù)交互的核心
實(shí)體邏輯層(Entity Logic Layer) 是系統(tǒng)中最具通用性和復(fù)用價(jià)值的部分。它不僅服務(wù)于當(dāng)前 API 端點(diǎn),也為其他多個(gè)接口和業(yè)務(wù)流程提供基礎(chǔ)能力支撐。這一層承載了系統(tǒng)的核心業(yè)務(wù)規(guī)則以及與外部存儲(chǔ)系統(tǒng)的交互邏輯。
在這一層級中,我們實(shí)現(xiàn)對持久化數(shù)據(jù)庫(如 MySQL 或 PostgreSQL)的訪問,封裝了數(shù)據(jù)的讀取、寫入和轉(zhuǎn)換邏輯;同時(shí),Adapter 類 則負(fù)責(zé)對接各類外部服務(wù) API,例如 AWS 提供的 S3(對象存儲(chǔ))、ElastiCache(緩存服務(wù))等,使得系統(tǒng)能夠靈活集成多種基礎(chǔ)設(shè)施資源。
與上層(如應(yīng)用邏輯層)中為特定端點(diǎn)定制的服務(wù)類不同,實(shí)體邏輯層中的類設(shè)計(jì)強(qiáng)調(diào)高內(nèi)聚、低耦合與廣泛復(fù)用性。它們通常不依賴于具體的請求或業(yè)務(wù)場景,而是圍繞領(lǐng)域模型構(gòu)建穩(wěn)定的數(shù)據(jù)訪問和業(yè)務(wù)處理能力。
簡而言之,實(shí)體邏輯層是整個(gè) Clean API 架構(gòu)中最接近“不變”的部分——它屏蔽了外部變化的影響,確保系統(tǒng)核心邏輯穩(wěn)定可靠,同時(shí)也為上層模塊提供了統(tǒng)一、可測試、可替換的數(shù)據(jù)交互接口。
5. 數(shù)據(jù)層:存儲(chǔ)抽象與適配的關(guān)鍵一環(huán)
數(shù)據(jù)層(Data Layer) 是整個(gè) Clean API 架構(gòu)中最底層的一環(huán),其核心職責(zé)是為上層模塊提供統(tǒng)一的數(shù)據(jù)訪問接口,并屏蔽具體存儲(chǔ)實(shí)現(xiàn)的細(xì)節(jié)。理想情況下,這一層應(yīng)保持高度簡潔和可替換,專注于連接數(shù)據(jù)庫、緩存、文件系統(tǒng)或其他持久化機(jī)制。
為了實(shí)現(xiàn)跨平臺(tái)一致性,我們在不同技術(shù)棧中(例如 Android 或 iOS 開發(fā))也為數(shù)據(jù)存儲(chǔ)層建立了統(tǒng)一接口。通過依賴注入(Dependency Injection) 技術(shù),我們可以在測試時(shí)輕松替換真實(shí)的數(shù)據(jù)源為內(nèi)存中的模擬實(shí)現(xiàn)(Mock)。例如,在本地運(yùn)行單元測試時(shí),可以使用基于 SQLite 的內(nèi)存數(shù)據(jù)庫代替實(shí)際的文件系統(tǒng)或遠(yuǎn)程服務(wù),從而提高測試效率并減少外部依賴的影響。
在 Web 服務(wù)器環(huán)境中,我們通常將云服務(wù)(如 Redis、Memcached、MySQL 等)抽象為單例對象,并根據(jù)部署環(huán)境動(dòng)態(tài)指向不同的實(shí)際資源。例如,在開發(fā)階段,這些服務(wù)可以指向本地運(yùn)行的 Docker 容器;而在生產(chǎn)環(huán)境中,則連接真實(shí)的云服務(wù)實(shí)例。
支撐數(shù)據(jù)層的各類存儲(chǔ)系統(tǒng)——如 MySQL 和 Postgres——通常以進(jìn)程級別的單例形式存在,并通過依賴注入或配置管理進(jìn)行初始化和替換。像 ActiveRecord 這樣的 ORM 會(huì)維護(hù)自己的連接池,而 Redis 和 Memcached 等服務(wù)也需要類似的全局訪問控制機(jī)制來管理連接資源。
對于基于 HTTP 的無狀態(tài)服務(wù)(如 S3、DynamoDB 等),我們通常采用模擬雙(Instance Doubles)或覆蓋連接參數(shù)的方式來隔離外部環(huán)境。這使得測試過程更加可控,同時(shí)也能保證代碼邏輯在不同環(huán)境下的一致性。
總之,數(shù)據(jù)層不僅是系統(tǒng)與外部世界交互的橋梁,更是實(shí)現(xiàn)可測試性、可維護(hù)性和可擴(kuò)展性的關(guān)鍵所在。通過良好的抽象設(shè)計(jì)與靈活的注入策略,它確保了我們的業(yè)務(wù)邏輯不受底層存儲(chǔ)細(xì)節(jié)的牽制,真正做到“一次編寫,多環(huán)境運(yùn)行”。
6. 這是否過度設(shè)計(jì)?讓我們通過一個(gè)簡單的示例來探討
為了更好地理解各層架構(gòu)的實(shí)際應(yīng)用,我們來看一個(gè)最基礎(chǔ)的 API 示例:將文件添加到收藏夾。即使在這樣一個(gè)看似簡單的操作中,每一層的設(shè)計(jì)理念依然得到了體現(xiàn)。
假設(shè)我們要?jiǎng)?chuàng)建一個(gè)允許用戶將某個(gè)文件添加到其收藏夾的功能。在這個(gè)過程中,盡管表面上看只需要一個(gè)簡單的數(shù)據(jù)庫操作,但實(shí)際上,這個(gè)功能隱式地依賴于我們之前定義的每一層架構(gòu)。
請求(Request)
請求參數(shù)直接從 HTTP 請求中提取,包含 target_id(目標(biāo)文件的ID)和 creator_id(執(zhí)行該操作的用戶ID)。這些未經(jīng)處理的原始參數(shù)構(gòu)成了一個(gè)隱式的請求對象。
Params: { "target_id": 123, "creator_id": 456 }控制器(Controller)
由于此場景下沒有復(fù)雜的驗(yàn)證或表示邏輯需求,因此無需專門編寫控制器代碼。這意味著我們可以跳過這一步驟,直接進(jìn)入服務(wù)層處理業(yè)務(wù)邏輯。
服務(wù)(Service)
服務(wù)層在此處承擔(dān)了主要職責(zé),它接收來自請求的 target_id和 creator_id,并查找或創(chuàng)建相應(yīng)的領(lǐng)域?qū)ο?nbsp;Favorite。這一過程確保了輸入的有效性和用戶的授權(quán)狀態(tài),并協(xié)調(diào)后續(xù)的數(shù)據(jù)處理步驟。
實(shí)體邏輯(Entity Logic)
實(shí)體邏輯層負(fù)責(zé)與持久化存儲(chǔ)交互,這里使用了一個(gè)特殊的 ActiveRecord 方法 first_or_create 來檢查是否存在符合條件的記錄,若不存在則創(chuàng)建新記錄。這種方法不僅簡化了數(shù)據(jù)訪問邏輯,還保證了數(shù)據(jù)的一致性。
數(shù)據(jù)(Data)
在數(shù)據(jù)層,Favorite 模型充當(dāng)了與底層數(shù)據(jù)庫交互的角色,提供了對特定對象的操作接口。它封裝了所有與數(shù)據(jù)庫相關(guān)的細(xì)節(jié),如連接管理、查詢構(gòu)建等,使得上層代碼可以專注于業(yè)務(wù)邏輯而非技術(shù)實(shí)現(xiàn)。
通過這個(gè)簡單的例子可以看出,即使是看似微不足道的功能,Clean API 的分層設(shè)計(jì)也能夠提供清晰的結(jié)構(gòu)劃分,確保每個(gè)部分專注于自己的職責(zé)。這樣的設(shè)計(jì)雖然初看起來可能顯得有些復(fù)雜,但它極大地提高了代碼的可維護(hù)性、測試性和擴(kuò)展性。隨著系統(tǒng)規(guī)模的增長,這種架構(gòu)的優(yōu)勢將會(huì)更加明顯。
























