每日最新頭條.有趣資訊

基於 DDD 設計並實現模塊化單體應用

作者丨Jan Stenberg

譯者丨蓋磊

一個最近由 Kamil Grzybek 在 Github 發布的項目,給出了使用領域驅動設計(DDD,Domain-Driven Design)方法設計並實現一個單體(monolith)應用的詳細介紹。該項目的目標,就是展示如何以模塊化方式設計並實現一個單體應用。此外,Grzybek 還基於他在應用開發實踐的收獲,給出了一些有用的架構建議和設計模式。

Grzybek 是華沙 ITSG Global 的一位系統架構師,並擔任團隊負責人。他指出,該項目的目標並非是要創建一個極簡應用,或是一個驗證原型(PoC,proof of concept),而是給出一種適用於生產環境的完整實現。該項目的動機,來自於 Grzybek 對一些類似的但並未取得成功的項目的審視。在他看來,大多數示例應用過於簡單,或是不夠完整。他一直認為,這些應用至少在某些部分上存在著設計和實現上的問題,或是存在著相關文檔缺失的問題。

Grzybek 強調指出,他的實現只是解決類似業務問題的許多方法之一。系統的軟體架構需考慮多種因素,例如功能要求、質量屬性和技術約束等,另一方面也會受開發人員的經驗、技術約束、時間和預算等因素的影響,所有這些因素都會影響解決方案。

該應用針對的是為大多數開發人員所認可的會議組(meeting group)領域。應用實現中考慮了額外的複雜性,因此相比基於 CRUD 的常規應用更具意義。Grzybek 將主領域進一步劃分為四個子域,即會議、支付、管理,以及用戶訪問。

為給出領域所需的功能,Grzybek 採用了一種稱為“事件風暴”( Event Storming )的方法。該方法由 Alberto Brandolini 建立,用於探索複雜業務領域。Grzybek 使用該方法在主領域的各子域中發現行為和事件。

從更高層次上看,該架構中定義了一個 API 層、包含存儲的四個模塊(分別對應於所發現的四個子域),以及一個用於通信的公共事件總線(EventBus),如下圖所示。

模塊也相應地劃分為四個子模塊,並分別實現為獨立的二進製文件,分別為:處理所有請求的 Application、實現領域邏輯的 Domain、實現基礎架構代碼的 Infrastructure,以及在 EventBus 上發布事件並且是模塊間唯一共享組件的 IntegrationEvents。Grzybek 使用了 Decorator 模式,實現添加工作單元( Unit of Work )和日誌等交叉關注點(cross-cutting)。

為分離應用內部的命令和查詢,Grzybek 使用並實現了 CQRS 的一種變體。該 CQRS 變體針對命令所涉及的同一數據庫表,在查詢中使用了原始 SQL 和視圖。雖然 CQRS 還具體其他變體,但 Grzybek 力圖避免使應用過於複雜化。

模塊間的集成是基於異步事件傳輸的。事件傳輸使用了“發件箱模式”/“收件箱模式”( outbox and inbox pattern ),以及基於記憶體中的 EventBus 代理。為存儲要發布的事件,發件箱模式在數據存儲中添加了獨立的表。事件的添加,實現中通過執行任務的命令,以及等同於命令的事務。此後,這些事件通過單獨的流程,轉發到另一個模塊的收件箱中。在該項目中,事件傳輸是通過各模塊中的後台 Worker 實現的。該實現提供了多次交付和處理。

最後,Grzybek 強調該項目仍在開發中,歡迎貢獻者的加入。項目是使用 C# 編寫的.NET Core 應用,使用了像 Autofac (用於 IoC )、 Dapper (一種用於讀取模型的 MicroORM )之類的類庫。項目中還包括基於 Arrang-Act-Assert 模式的測試,使用 NUnit 實現。

在與 InfoQ 的訪談中,Grzybek 進一步詳細介紹了他的設計理念。

InfoQ:相比基於微服務的設計,您如何評價單體應用的模塊化設計?

Kamil Grzybek:與微服務體系架構相比,模塊化單體應用的主要差別之處在於部署方法和模塊間通信。

在微服務架構中,每個模塊都以獨立的進程運行。模塊間的通信必須使用網絡實現,並且通常通過同步服務 API 調用(即 RPC,遠程過程調用),或是使用代理(即消息傳遞)實現。微服務架構是一種分布式系統,具有分布式的所有優點和缺點。對於模塊化單體應用,則無需考慮分布式系統。所有模塊均以同一進程運行,無需使用網絡即可相互通信。各模塊可以通過方法調用直接同步引用記憶體中對象,或是異步地使用運行於同一進程中的某個中介者(Mediator)。

InfoQ:相比其他解決方案,例如一些消息傳遞平台,使用 EventBus 和發件箱 / 收件箱模式具有哪些優缺點?

Grzybek:模塊化單體應用的主要優點,是開發人員無需使用任何消息平台,因為大多數功能都可以使用現有的設計模式在記憶體中實現。模塊化單體應用本身可擔當此類平台。當然,對於更高級的系統,使用獨立的平台可能是更好的解決方案。但做出此決定時,必須謹慎。

InfoQ:您在 CQRS 模式的實現中,使用了視圖和原始 SQL,而非單獨的表。對此您能詳細介紹一下嗎?

Grzybek:我的方法雖然是一種最簡單的 CQRS 實現,但其功能強大,通常完全夠用。使用視圖是應用和數據庫之間的一種抽象和契約形式。開發人員可隨時進入 CQRS 實現的下一層,讀取應用邏輯可以保持不變。

下一層是物化視圖。物化視圖加快了讀取的速度,但會導致寫入性能略有下降。一些最先進的系統對於擴展性要求極高,它們引入了 CQRS 模式的最高層實現,並異步更新讀取模型。這就是“最終一致性”。

每個實現層,都會增加解決方案的複雜性,因此,只有在真正需要時,我們才應去動更高的層。

https://www.infoq.com/news/2019/09/design-ddd-modular-monolith/

點個在看少個 bug

獲得更多的PTT最新消息
按讚加入粉絲團