MASA Framework 命令查詢職責分離

概念

CQRS?(https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs)是一種與領域驅動設計和事件溯源相關的架構模式, 它的全稱是Command Query Responsibility Segregation, 又叫命令查詢職責分離, Greg Young在2010年創造了這個術語, 它是基于Bertrand Meyer 的 CQS (Command-Query Separation 命令查詢分離原則) 設計模式。

CQRS認為不論業務多復雜在最終實現的時候, 無非是讀寫操作, 因此建議將應用程序分為兩個方面, 即Command(命令)和Query(查詢)

  • 命令端:

    • 關注各種業務如何處理, 更新狀態進行持久化

    • 不返回任何結果 (void)

  • 查詢端:

    • 查詢, 并從不修改數據庫

CQRS的三種實現

單一數據庫的CQRS

aba55d0f2344abd2ed4e3248106f1748.png

命令與讀取操作的是同一個數據庫, 命令端通過ORM框架將實體保存到數據庫中, 查詢端通過數據訪問層獲取數據 (數據訪問層通過ORM框架或者存儲過程獲取數據)

雙數據庫的CQRS

a5c5cad987a39e2e42e72f570d490301.png

命令與讀取操作的是不同的數據庫, 命令端通過ORM框架將實體保存到?寫庫?(Write Db), 并將本地改動推送到?讀庫?(Read Db), 查詢端通過數據訪問層訪問?讀庫?(Read Db), 使用這種模式可以帶來以下好處:

  • 查詢更簡單

    • 讀操作不需要任何的完整性校驗, 也不需要外鍵約束, 可以減少鎖爭用, 我們可以針對查詢端單獨優化, 還可以使用剛好包含每個模板需要的數據的數據庫視圖,使得查詢變得更快更簡單

  • 提升查詢端的使用體驗

    • 由于這種架構將讀寫徹底分離,由于一般系統是讀操作遠遠大于寫操作, 這給我們的系統帶來了巨大的性能提升, 極大的提升了客戶的使用體驗

  • 關注點分離

    • 讀寫分離的模型可以使得關注點分離, 使得讀模型會變得相對簡單

事件溯源 (Event Sourcing) CQRS

a5481fcef92663b08a3a8f944f1efdd2.png

通過事件溯源實現的CQRS中會將應用程序的改變都以事件的方式存儲起來, 使用這種模式可以帶來以下好處:

  • 事件存儲中了完整的審計跟蹤, 后續出現問題時方便跟蹤

  • 可以在任何的時間點重建實體的狀態, 它將有助于排查問題并修復問題

  • 提升查詢端的使用體驗

    • 查詢端與命令端可以是完全不同的數據源, 查詢端可以針對查詢條件做針對應的優化, 或者使用像ESRedis等用來存儲數據, 提升查詢效率

  • 獨立縮放

    • 命令端與查詢端可以被獨立縮放, 減少鎖爭用

當然事情有利自然也有弊,?CQRS的使用固然會帶來很多好處, 但同樣它也會給項目帶來復雜度的提升, 并且雙數據庫模式、事件溯源模式?(https://microservices.io/patterns/data/event-sourcing.html)?的CQRS, 使用的是最終一致性, 這些都是我們在選擇技術方案時必須要考慮的

設計

上述文章中我們了解到了CQRS其本質上是一種讀寫分離的設計思想, 它并不是強制性的規定必須要怎樣去做, 這點與之前的IEvent?(進程內事件)、IIntegrationEvent?(跨進程事件)不同, 它并不是強制性的, 根據CQRS的設計模式我們將事件分成CommandQuery

由于Query?(查詢) 是需要有返回值的, 因此我們在繼承IEvent的同時, 還額外增加了一個Result屬性用以存儲結果, 我們希望將查詢的結果保存到Result中, 但它不是強制性的, 我們并沒有強制性要求必須要將結果保存到Result中。

由于Command?(命令) 是沒有返回值的, 因此我們并沒有額外新增Result屬性, 我們認為命令會更新數據, 那就需要用到工作單元, 因此Command除了繼承IEvent之外, 還繼承了ITransaction,這方便了我們在Handler中的可以通過@event.UnitOfWork來管理工作單元, 而不需要通過構造函數來獲取

MASA Framework?

(https://github.com/masastack/MASA.Framework)?并沒有要求必須使用?Event Sourcing 模式?

(https://microservices.io/patterns/data/event-sourcing.html)?或者?雙數據庫模式?的CQRS, 具體使用哪種實現, 它取決于業務的決策者

下面就就來看看MASA Framework提供的CQRS是如何使用的

入門

  • 安裝.NET 6.0?(https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0)

1.新建ASP.NET Core 空項目Assignment.CqrsDemo,并安裝Masa.Contrib.Dispatcher.EventsMasa.Contrib.Dispatcher.IntegrationEventsMasa.Contrib.Dispatcher.IntegrationEvents.DaprMasa.Contrib.ReadWriteSplitting.CqrsMasa.Contrib.Development.DaprStarter.AspNetCore

dotnet new web -o Assignment.CqrsDemo
cd Assignment.CqrsDemodotnet add package Masa.Contrib.Dispatcher.Events --version 0.7.0-preview.9 //使用進程內事件總線
dotnet add package Masa.Contrib.Dispatcher.IntegrationEvents --version 0.7.0-preview.9 //使用跨進程事件總線
dotnet add package Masa.Contrib.Dispatcher.IntegrationEvents.Dapr --version 0.7.0-preview.9 //使用Dapr提供pubsub能力
dotnet add package Masa.Contrib.ReadWriteSplitting.Cqrs --version 0.7.0-preview.9 //使用CQRSdotnet add package Masa.Contrib.Development.DaprStarter.AspNetCore --version 0.7.0-preview.9  //開發環境下協助 Dapr Sidecar, 用于通過Dapr發布集成事件

?2.注冊跨進程事件總線、進程內事件總線, 修改類Program.cs

示例中未真實使用DB, 不再使用發件箱模式, 只需要使用集成事件提供的PubSub?(https://docs.dapr.io/zh-hans/developing-applications/building-blocks/pubsub/pubsub-overview/)能力即可

builder.Services.AddIntegrationEventBus(dispatcherOptions =>
{dispatcherOptions.UseDapr();//使用 Dapr 提供的PubSub能力dispatcherOptions.UseEventBus();//使用進程內事件總線
});

3.注冊Dapr Starter協助管理Dapr Sidecar?(開發環境使用)

if (builder.Environment.IsDevelopment())builder.Services.AddDaprStarter();

4.新增加添加商品方法, 修改類Program.cs

app.MapPost("/goods/add", async (AddGoodsCommand command, IEventBus eventBus) =>
{await eventBus.PublishAsync(command);
});/// <summary>
/// 添加商品參數, 用于接受商品參數
/// </summary>
public record AddGoodsCommand : Command
{public string Name { get; set; }public string Cover { get; set; }public decimal Price { get; set; }public int Count { get; set; }
}

5.新增加查詢商品的方法, 修改類Program.cs

app.MapGet("/goods/{id}", async (Guid id, IEventBus eventBus) =>
{var query = new GoodsItemQuery(id);await eventBus.PublishAsync(query);return query.Result;
});/// <summary>
/// 用于接收查詢商品信息參數
/// </summary>
public record GoodsItemQuery : Query<GoodsItemDto>
{public Guid Id { get; set; } = default!;public override GoodsItemDto Result { get; set; }public GoodsItemQuery(Guid id){Id = id;}
}/// <summary>
/// 用于返回商品信息
/// </summary>
public class GoodsItemDto
{public Guid Id { get; set; }public string Name { get; set; }public string Cover { get; set; }public decimal Price { get; set; }public int Count { get; set; }public DateTime DateTime { get; set; }
}

6.新增Command處理程序, 添加類CommandHandler.cs

public class CommandHandler
{/// <summary>/// 將商品添加到Db,并發送跨進程事件/// </summary>/// <param name="command"></param>/// <param name="integrationEventBus"></param>[EventHandler]public async Task AddGoods(AddGoodsCommand command, IIntegrationEventBus integrationEventBus){//todo: 模擬添加商品到db并發送添加商品集成事件var goodsId = Guid.NewGuid(); //模擬添加到db后并獲取商品idawait integrationEventBus.PublishAsync(new AddGoodsIntegrationEvent(goodsId, command.Name, command.Cover, command.Price,command.Count));}
}/// <summary>
/// 跨進程事件, 發送添加商品事件
/// </summary>
/// <param name="Id"></param>
/// <param name="Name"></param>
/// <param name="Cover"></param>
/// <param name="Price"></param>
/// <param name="Count"></param>
public record AddGoodsIntegrationEvent(Guid Id, string Name, string Cover, decimal Price, int Count) : IntegrationEvent
{public Guid Id { get; set; } = Id;public string Name { get; set; } = Name;public string Cover { get; set; } = Cover;public decimal Price { get; set; } = Price;public int Count { get; set; } = Count;public override string Topic { get; set; } = nameof(AddGoodsIntegrationEvent);
}

7.新增Query處理程序, 添加類QueryHandler.cs

public class QueryHandler
{/// <summary>/// 從緩存查詢商品信息/// </summary>/// <param name="query"></param>/// <returns></returns>[EventHandler]public Task GetGoods(GoodsItemQuery query){//todo: 模擬從cache獲取商品var goods = new GoodsItemDto();query.Result = goods;return Task.CompletedTask;}
}

8.新增添加商品的跨進程事件的處理服務, 修改Program.cs

app.MapPost("/integration/goods/add",[Topic("pubsub", nameof(AddGoodsIntegrationEvent))](AddGoodsIntegrationEvent @event, ILogger<Program> logger) =>{//todo: 模擬添加商品到緩存logger.LogInformation("添加商品到緩存, {Event}", @event);});// 使用 dapr 來訂閱跨進程事件
app.UseRouting();
app.UseCloudEvents();
app.UseEndpoints(endpoint =>
{endpoint.MapSubscribeHandler();
});

流水賬式的服務會使得Program.cs變得十分臃腫, 可以通過MASA?Framework提供的MinimalAPIs來簡化Program.cs?點擊查看詳情(https://blogs.masastack.com/2022/07/12/masa/framework/practice/14.%20%E6%9C%80%E5%B0%8F%20API%20-%20MinimalAPIs)

我們上面的例子是通過事件總線來完成解耦以及數據模型的同步, 使用的雙數據庫模式, 但讀庫使用的是?緩存數據庫, 在Command端做商品的添加操作, 在Query端只做查詢, 且兩端分別使用各自的數據源, 兩者業務互不影響, 并且由于緩存數據庫性能更強, 它將最大限度的提升性能, 使得我們有更好的使用體驗。

在Masa Framework中僅僅是通過ICommandIQuery將讀寫分開, 但這并沒有硬性要求, 事實上你使用IEvent也是可以的,?CQRS只是一種設計模式, 這點我們要清楚, 它只是告訴我們要按照一個什么樣的標準去做, 但具體怎么來做, 取決于業務的決策者, 除此之外, 后續Masa Framework還會增加對Event Sourcing(事件溯源?(https://docs.microsoft.com/zh-cn/azure/architecture/patterns/event-sourcing))的支持, 通過事件重放, 允許我們隨時重建到對象的任何狀態

本章源碼

Assignment15

https://github.com/zhenlei520/MasaFramework.Practice

CQRS架構項目:https://github.com/masalabs/MASA.EShop/tree/main/src/Services/Masa.EShop.Services.Catalog

參考

  • CQRS 模式?

    (https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs)

  • 在微服務中應用簡化后的 CQRS 和 DDD 模式

    ?(https://learn.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/apply-simplified-microservice-cqrs-ddd-patterns)

  • .NET現代化應用開發 - CQRS&類目管理代碼剖析?

    (https://www.bilibili.com/video/BV1D24y1R7jE)

開源地址

MASA.Framework:https://github.com/masastack/MASA.Framework


如果你對我們的 MASA Framework 感興趣, 無論是代碼貢獻、使用、提 Issue, 歡迎聯系我們

3cdd22fb67bb2b45ee75cb4ac853468c.png

1eb59f06e09a7e11b04e7130c00e0ba7.gif

《MASA?Framework實戰課程》已開課

點擊“閱讀原文”查看課程安排

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/280339.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/280339.shtml
英文地址,請注明出處:http://en.pswp.cn/news/280339.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Google的Project Stream準備在Chrome中播放AAA控制臺游戲

Streaming full 3D games over a high-speed web connection is a fast growing trend. And with ridiculous amounts of infrastructure and remote computing power, Google is well equipped to join it. 通過高速網絡連接流式傳輸完整的3D游戲是一種快速增長的趨勢。 憑借可…

私有云之迷思:未來是什么?

本文講的是私有云之迷思&#xff1a;未來是什么&#xff1f;&#xff0c;【編者的話】非常好的一篇文章&#xff0c;作者從OpenStack目前的困境講起&#xff0c;聊到了私有云的產生背景&#xff0c;進而介紹了云計算的發展史。從云計算誕生的初衷以及現在流行的分布式應用又延伸…

如何在vue中使用sass

安裝sass 安裝教程鏈接&#xff1a; https://www.sass.hk/install/ 在vue中使用sass 參考鏈接&#xff1a; https://www.jianshu.com/p/8e60048baeb7 打開控制臺&#xff1a;輸入命令行 如果是沒有淘寶鏡像的&#xff0c;先下載淘寶鏡像&#xff0c;之后的下載速度比較快 npm i…

maven項目的目錄結構

1、maven項目采用“約定優于配置”的原則&#xff1a; src/main/java&#xff1a;約定用于存放源代碼&#xff0c;src/test/java&#xff1a;用于存放單元測試代碼&#xff0c;&#xff08;測試代碼的包應該和被測試代碼包結構保持一致&#xff0c;方便測試查找&#xff09;src…

AWS大力支持.NET 開源項目,和Azure搶.NET 客戶

出品 | OSC開源社區&#xff08;ID&#xff1a;oschina2013)在 2022 re:Invent 會議上&#xff0c; AWS 軟件開發經理 Saikat Banerjee 銳評道&#xff1a;” 我們發現 .NET 開源項目資金嚴重不足&#xff0c;仍可稱之為第三方開源”。隨即表示 AWS 過去非常重視 .net 生態&…

攻防 logmein_如何使用LogMeIn Hamachi在任何地方訪問文件

攻防 logmeinWhether you’re at work and forgot some file on your home computer, want to play some music on a train, or just want to move some files between your computers, accessing your files from anywhere is a life saver. 無論您是在工作時忘記了家用計算機…

Docker-machine創建虛機時停在虛機啟動的提示上,并且創建的虛機顯示Ip Not found...

Docker-machine創建虛機時停在虛機啟動的提示上&#xff0c;并且創建的虛機用docker-machine ls 列出來的時候顯示Ip Not found&#xff0c; 是什么原因那&#xff1f; 【答案】 看這個帖子&#xff1a; https://github.com/docker/machine/issues/3832 拷貝如下&#xff1a; I…

【年度總結】2016年年度總結

早晨醒來&#xff0c;在被窩里面刷著簡書&#xff0c;看到一篇文章叫《深漂一年&#xff0c;一個資深程序員的2016年終告白》&#xff0c;寫的很好&#xff0c;很有感觸。在2016年的農歷的最后一天&#xff0c;總是有很多感觸要寫下來。所以下午掃墓之后&#xff0c;我也按照劇…

在FC中如何獲取fcdot文件

在FlexiCapture中一些客戶在問如何獲取.fcdot文件(在測試序列號下或者沒有測試模板的情況下) 第一步&#xff1a; 1、查看License Manager查看是否找到序列號 首先我們在開始菜單里面打開ABByyFlexiCapTure11——》選擇"工具"下的License Manager 第二步 1、選擇管理…

Blazor學習之旅 (9) 用MudBlazor重構Todo

【Blazor】| 總結/Edison Zhou大家好&#xff0c;我是Edison。在之前的學習之旅&#xff08;3&#xff09;開發一個Todo應用中&#xff0c;我們開發了一個簡單版的Todo&#xff0c;這次我們基于MudBlazor來重構這個Todo應用。Todo V1回顧在Blazor入門學習&#xff08;3&#xf…

50多種在Photoshop中刪除圖像背景的工具和技術,第3頁

We’re completing the 50 Tools and Techniques today with this final installment. Read about advanced selection and masking tools, as well as some stupid graphics geek tricks, and ways to fake removing a background in seconds. 我們今天最后一部分將完成50多種…

socket跟TCP/IP 的關系,單臺服務器上的并發TCP連接數可以有多少

常識一&#xff1a;文件句柄限制 在linux下編寫網絡服務器程序的朋友肯定都知道每一個tcp連接都要占一個文件描述符&#xff0c;一旦這個文件描述符使用完了&#xff0c;新的連接到來返回給我們的錯誤是“Socket/File:Cantopen so many files”。 這時你需要明白操作系統對可以…

SSPL的MongoDB再被拋棄,GUN Health也合流PostgreSQL

2019 年 2 月 12 日&#xff0c;紅帽官方發博稱&#xff0c;Red Hat Satellite 將擁抱PostgreSQL&#xff0c;并且不會支持 SSPL 許可的 MongoDB 新版本。無獨有偶&#xff0c;同一天GNU Health也發博稱GNU Health Federation Information System 將從MongoDB遷移到PostgreSQL&…

開源的 .NET 數據庫遷移框架

你好&#xff0c;這里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;實用的工具和組件&#xff0c;希望對您有用&#xff01;簡介FluentMigrator 是一個開源的數據庫遷移框架&#xff0c;可以幫助用戶在開發過程中保持數據庫的一致性。它提供了一個簡潔的 Flue…

在deepin上安裝YouCompleteMe

詳細安裝步驟在github上有&#xff0c;https://github.com/Valloric/YouCompleteMe&#xff0c;我這里是自己總結的簡化版安裝步驟。 步驟1.安裝Vundle 首先&#xff0c;clone到本地 git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim把以下內容…

2015年IT領域里Docker和其它顛覆性的趨勢

本文講的是2015年IT領域里Docker和其它顛覆性的趨勢&#xff0c;【編者的話】文中作者介紹了2015年IT領域的一些顛覆性的趨勢&#xff0c;比如Docker將如何革新PaaS、IaaS等&#xff0c;Docker將如何顛覆虛擬化、私有云、配置管理。 2014年真是令人興奮的一年&#xff0c;這一年…

進化:從孤膽極客到高效團隊_極客狂:為什么這么多的網站無法使用打印樣式表?...

進化:從孤膽極客到高效團隊It never ceases to amaze me that people have to look for a link or a button that says “Print” on a web page, especially considering there’s a miracle technology that makes that step unnecessary. Sadly almost nobody uses it, even…

Iterator 和 for...of 循環

Iterator 和 for...of 循環 Iterator&#xff08;遍歷器&#xff09;意義 為Array、Object、Map、Set四種數據集合&#xff0c;提供統一的接口機制來處理所有不同的數據結構 。 任何數據結構&#xff0c;只要部署 Iterator 接口&#xff0c;就可以完成遍歷操作&#xff08;即依…

python簡單開發接口

1、首先需要安裝flask這個模塊&#xff1a;pip install flask。flask是個輕量級的接口開發框架2、開發接口有什么作用  1、mock接口&#xff0c;模擬一些接口&#xff0c;在別的接口沒有開發好的時候&#xff0c;需要用mock去模擬一些接口。  2、知道接口是怎么開發的&…

九哥聊Kestrel網絡編程第二章:開發一個Fiddler

推薦序之前在.NET 性能優化群內交流時&#xff0c;我們發現很多朋友對于高性能網絡框架有需求&#xff0c;需要創建自己的消息服務器、游戲服務器或者物聯網網關。但是大多數小伙伴只知道 DotNetty&#xff0c;雖然 DotNetty 是一個非常優秀的網絡框架&#xff0c;廣泛應用于各…