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

一個最近由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模式的最高層實現,並異步更新讀取模型。這就是“最終一致性”。
每個實現層,都會增加解決方案的複雜性,因此,只有在真正需要時,我們才應去動更高的層。

原文鏈接:

Design and Implementation of a DDD-Based Modular Monolith

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章