聊聊 ASP.NET 6 整潔架構開發模板

2023-06-21 18:00:22

大家好,我是Edison。

最近看了一些整潔架構(CleanArchitecture)的文章,自己和同事也簡單寫了一個基於整潔架構的ASP.NET 6開發模板在玩。這裡就僅僅拋個磚,案例主要以自己根據小組實際情況做了一些裁剪,可能不具有通用的應用性,大家看看就好。

整潔架構的產生背景

微服務架構讓DDD(領域驅動設計)煥發了第二春,在DDD的推動下,DDD的分層架構被逐漸推上了舞臺。DDD的分層架構就有好多種,例如整潔架構、CQRS和六邊形架構等等,每種架構模式雖然提出的時代和背景不同,但其核心理念都是為了設計出「高內聚低耦合」的架構,從而能夠實現架構的演進。

DDD分層架構

在歐創新老師的《DDD實戰課》中,給出了一個優化後的DDD四層架構,我們可以從下面這張圖中看到,從上到下分別是:使用者介面層、應用層、領域層和基礎層。

與傳統的三層架構不同,DDD四層架構的重點在於引入了一個領域層。
領域層的作用是實現企業核心業務邏輯,通過各種校驗手段保證業務的正確性。領域層主要體現領域模型的業務能力,它用來表達業務概念、業務狀態和業務規則。領域層包含:聚合根、實體、值物件、領域服務等領域模型中的領域物件。對於領域層,領域模型的業務邏輯主要由實體和領域服務來實現。對於實體,一般建議採用充血模型來實現所有與之相關的業務功能。對於領域服務,一般當單個實體不能實現某些功能時,領域服務才會出馬,組合聚合內的多個實體來實現複雜的業務邏輯。下圖中展示了傳統的三層架構與DDD四層架構的對應關係:

整潔架構簡單介紹

簡而言之,整潔架構是組織軟體體系結構的原則,可以輕鬆面對未來的不確定性,方便程式碼的重構。同時,它可以幫助我們為特定的領域模型構建服務,從而為將來可能的微服務體系結構做好準備。在Jason Taylor的這篇文章中《Clean Architecture with .NET Core: Gettting Started》中給出了一張經典的圖:

在整潔架構中,所有依賴關係都向內流動,而核心層不依賴於其他任何層。基礎設施層和展示層依賴於核心層,而不是彼此依賴。在Jason Taylor給出的圖中,只有三個圓圈,但在實際中,你可能需要更多,但是你可以以此作為起點,只需要記住讓所有依賴都指向內部即可。這裡的內部,我們可以將其稱為ApplicationCore,也就是Application + Domain。

整潔架構模板搭建

這裡我試著搭建了一個基於ASP.NET 6的開發模板,展示層有兩種可選:ASP.NET WebAPI / Blazor。需要說明是:該模板僅僅是結合我司實際情況的構想,沒有遵循DDD的一些原則思想(DDD是個好東西,但不是所有專案都適用,也不是所有團隊都可以用好),也不具有廣泛應用性,各位看官看看一笑而過就好。

在我司(一家制造業工廠的IT部),基於我們組的實際人員情況中(開發基礎能力較弱,以前的工作基本以運維為主,很少做開發工作)和開發專案的綜合複雜度(嚴格來說,複雜度並不高,以後臺管理資訊系統為主),我不想引入太多DDD的概念增加學習成本,因此ValueObject和Domain Service被我移除了,但是充血模型的Entity是我們所倡導的,因此,最終的結構如下圖所示:

對於展示層,分別使用WebAPI和Blazor實現API和UI的宿主;
對於核心層(ApplicationCore),包含 Application 和 Domain 兩個.NET 6.0類庫專案。

(1)Application定義了Services、Handlers(對於MQ的Consumers)、Validators(基於FluentValidation的Validators)以及 各種Models(DTO、VO,以及基於AutoMapper的MappingProfiles)。

(2)Domain則定義了實體、列舉、異常、常數等。這一層無需引入過多概念,只需要在原有實體的基礎上,使用充血模型,讓實體的行為豐富起來即可,這也可以讓開發人員很快適應和模仿。

對於基礎設施層,也是一個.NET 6.0類庫,主要包含了基於EF Core的上下文(DbContext)、實體對映關係(EntityConfiguration)、Repositories、Gateways(針對依賴的外部介面HttpClient實現,可以用HttpClientFactory來實現,也可以用WebApiClient之類的封裝專案)、Cache(比如RedisClient的註冊)、MessageQueue(比如KafkaClient的註冊,取決於你們組用了什麼MQ)等。

除了上面的四層之外,設計一個CrossCuting的Shared類庫,用於存放一些各個層都可以複用(參照)的幫助類、擴充套件方法、基礎類別等,用於減少重複。整個專案在Visual Studio中的解決方案目錄如下圖所示:

整個專案在Visual Studio中的解決方案目錄如下圖所示:

最終的依賴關係如下:
(1)Domain類庫只參照Shared類庫(即CrossCutting)。
(2)Application類庫參照:Domain、Infrastructure、Shared

這裡我們沒有讓Application不依賴於Infrastructure,是因為我們的DB技術棧已經固定,而且大家也比較習慣於在Infrastructure裡邊定義Repository、Gateway等的interface,而不是在Application裡面定義這些interface,Infrastructure通過應用Application來做實現。

(3)Infrastructure類庫參照:Domain、Shared

(4)Web專案參照:Application、Shared(其實這裡Application參照了Shared,Web專案無需再新增參照)

(5)WebUI專案參照:Application、Shared(其實這裡Application參照了Shared,Web專案無需再新增參照)通常情況下,WebAPI和WebUI專案二者只選選擇一個,因此新專案建立好之後,刪除其中一個。

(6)Application.UnitTests專案參照:Application
(7)Domain.UnitTests專案參照:Domain

(8)Web.IntegrationTests專案參照:Web

在實際模板中,針對ServiceCollection和ApplicationBuilder寫了很多擴充套件方法,用於一些常見元件的註冊,例如Swagger、CAP(Event Bus)、Redis Client、健康檢查EndPoints、KeyCloak鑑權啟用等等。開發者只需要根據需要在組態檔中新增或移除對應部分的config即可,這些擴充套件方法會根據組態檔中是否有這部分的config來判斷是否需要註冊。因此,大部分情況下,小組的開發者要做的僅僅是減法。比如,如果專案只用到了Cache沒有用到EventBus,那麼只需要在設定中移除EventBus的部分即可,不用改任何一句程式碼。又如,通常大部分專案裡我們只會保留一個UnitTest類庫,而不是讓三個都在專案中,因為我們的精力不足以寫三種型別的Test專案。但是某些大一點的專案,對質量有要求的,我們可能會寫兩中型別的Test專案。

模板上傳Nuget倉庫

這裡我們主要通過將其釋出為一個Nuget包上傳到企業內部的Nuget倉庫,然後使用者端可以通過安裝這個nuget包將其新增到Visual Studio中的專案模板中。

當然,也可以直接通過dotnet new命令直接通過模板建立新專案。

-- install
dotnet new install CleanArchitectureTemplate
-- uninstall
dotnet new uninstall CleanArchitectureTemplate

同理,當將開發模板釋出了新的nuget包,使用者端也可以通過更新nuget包的方式將模板進行更新,以便下次可以使用新的模板進行專案的開發。

-- check
dotnet new --update-check
-- apply
dotnet new --update-apply

關於如何通過Nuget上傳開發模板,可以參考Microsoft的這一篇檔案:https://learn.microsoft.com/zh-cn/dotnet/core/tools/custom-templates

小結

本文介紹了DDD分層架構的背景、整潔架構的概念,隨後分享了一個基於我所在小組的實際情況的一個整潔架構的模板案例。在實際情況中,ABP vNext也是一不錯的選擇,對DDD有興趣應用的建議仔細看看。

參考程式碼

GitHub:https://github.com/EdisonChou/CleanArchitectureTemplate

參考資料

Jason Taylor,《Clean Architecture with .NET Core: Gettting Started》

歐創新,極客時間《DDD實戰課》

Jacky Fei,《基於ASP.NET 6的整潔架構》

Alexander Zhao,《.NET Core整潔架構入門》

Denny Zhang,《領域驅動架構及其演變歷史》