基于事件驱动架构构建微服务第1部分:应用程序特定的业务规则
原文鏈接:https://logcorner.com/building-microservices-through-event-driven-architecture-part1-application-specific-business-rules/
如今,洋蔥或六邊形等架構為代碼的可測試性和維護、與外部框架的獨立性提供了重要幫助。
在本教程中,我將展示如何使用Clean架構,以及諸如領域驅動設計(DDD)、測試(行為)驅動開發(TDD)、CQRS、事件溯源、容器化、Oauth2和Oidc等方法和工具來構建微服務架構。
關于Clean架構的更多信息,我建議您閱讀Robert C. Martin (Uncle Bob)的這篇文章:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
依賴規則
同心圓代表軟件的不同區域。一般來說,越深入代表你的軟件層次越高。外圓是戰術實現機制,內圓是戰略核心策略。
使這個架構工作的最重要的規則是依賴規則。這個規則說明了源代碼依賴只能指向內部。內圈的任何東西都不可能知道外圈的任何東西。特別是,在外圈中聲明的部分不能被內圈中的代碼提及。包括,函數,類。變量或任何其他命名的軟件實體。
同樣,外圈中使用的數據格式不應該被內圈使用,特別是如果這些格式是由外圈中的框架生成的。我們不希望外圈的任何東西影響內圈。
命令查詢責任分離 (CQRS)
CQRS將命令與查詢分開。命令是更改應用程序狀態并且不返回數據的操作。查詢是返回數據但不更改應用程序狀態的操作。因此,在微服務領域,通過使用兩個數據庫創建應用程序,CQRS將是一個非常有用的概念:
1.一個關系型數據庫,它針對在命令端寫入進行了優化。
2.查詢端使用NoSQL數據庫,以便盡可能快地讀取數據。
由于大多數應用程序讀取數據的頻率遠高于寫入數據的頻率,因此在我們的容器化方法中,我們可以在2個pod上部署命令端,在10個pod上部署查詢端。
領域驅動設計
CQRS適合領域驅動設計。DDD專注于構建豐富的領域模型來處理復雜的業務邏輯。
更改數據會導致更多錯誤。因此,清楚地了解應用程序的哪個部分更改了數據以及應用程序的哪個部分不更改數據將有助于可維護性和調試。
事件溯源
事件溯源將對象的所有更改存儲為事件存儲中的一系列事件。https://eventstore.org/
我將使用這個概念來構建以下內容:
領域服務實現領域相關的概念(實體、值對象、聚合、領域事件),在關系數據庫中記錄一個命令,在事件存儲中記錄一個事件。全部作為一個單元,以進行數據更改
生產者從事件存儲中獲取事件并將其發送到服務總線(事件存儲是一個只附加表)
消費者,服務總線的訂閱者,從服務總線獲取事件并將其作為預先計算的數據寫入NoSQL數據庫
ReadModel服務查詢NOSQL數據庫
發生的一切都保存在事件存儲中?
我將建立一個系統,幫助演講者和與會者注冊和跟蹤事件(會議、談話、聚會等…)
我的項目結構如下:
EduSync.Speech.Domain
容納核心領域的最內層。它包含我們的領域對象和業務規則。定義了我們的外部接口。
數據庫、網絡連接、文件系統、用戶界面或特殊框架,都是不允許的。
核心領域不知道自己之外的任何東西。
這些依賴項及其實現是使用接口注入我們的核心域的。
EduSync.Speech.Application
指向核心領域并包含特定于應用程序的業務規則。
編排數據流并使用領域模型。
不依賴于數據庫、UI 或特殊框架。
EduSync.Speech.Presentation
該層包含Web、UI和展示邏輯。在我們的API上下文中,這意味著它通過網絡接受http請求形式的輸入(POST/PUT/PATCH/DELETE),并以JSON格式的內容返回其輸出。
EduSync.Speech.Infrastructure
該層包含數據庫和網關。在這里,我們定義數據訪問層、存儲庫等。
它包含在我們的Domain中定義的接口的物理實現。?
測試驅動開發
實現“語音注冊”用例
為了使我的測試變為綠色,我首先需要實現的是RegisterSpeechUseCase
使這個架構工作的最重要的規則是依賴規則。這個規則說源代碼依賴只能指向內部。內圈中的任何東西都不可能知道外圈中的任何東西。
因此,讓我們定義IRegisterSpeechUseCase接口及其實現RegisterSpeechUseCase。這些類型屬于EduSync.Speech.Application。
它將輸入對象作為命令。
然后是接口?
然后RegisterSpeechUseCase如下所示:
讓我們定義IUnitOfWork和ISpeechRepository等依賴項,這些接口屬于核心域,將在基礎設施上實現。
ISpeechRepository需要一個語音實體,所以讓我們在核心域上創建它
一切都編譯成功,但我的測試失敗了。
如您所見,測試失敗是因為我驗證需要調用CreateAsync和Commit方法,所以讓我們在RegisterSpeechUseCase類上調用SpeechRepository.CreateAsync和IUnitOfWork.Commit
然后在我的單元測試的arrange部分創建SpeechRepository.CreateAsync和IUnitOfWork.Commit的mock
所有測試都是綠色的了,但我的代碼覆蓋率還不夠:
例如,如果我注釋掉這個塊,我的測試將成功,但如果command為空,我的應用程序將在運行時崩潰?
讓我們添加一個新的測試來修復它?
最后,LogCorner.EduSync.Speech.Application的代碼覆蓋率為100%
但是如果我替換了賦值會發生什么?
var?title?=?command.Type; var?urlValue?=?command.Title; var?description?=?command.Url; var?type?=?command.Description;?所有測試都會成功,但我的應用程序將處于無效狀態,因為它將插入標題而不是 url,...。
我可以在測試斷言中使用moqSpeechRepository.Verify修復它,但我會保留它并在通過引入值對象實現我的領域時修復它
下一步,我將實現領域模型。
源代碼可在此處獲得:RegisterSpeechUseCase(https://github.com/logcorner/LogCorner.EduSync/tree/Feature/Task/RegisterSpeechUseCase)
歡迎關注我的個人公眾號”My IO“
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的基于事件驱动架构构建微服务第1部分:应用程序特定的业务规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF 实现任务栏角徽
- 下一篇: 如何排查 StackOverflow 异