恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
为什么eShopOnWeb用IRepository与IReadRepository读写分离?CQRS完整解析
首页
资讯中心
/
为什么eShopOnWeb用IRepository与IReadRepository读写分离?CQRS完整解析
为什么eShopOnWeb用IRepository与IReadRepository读写分离?CQRS完整解析
发布时间:2026/9/19 16:53:57
为什么eShopOnWeb用IRepository与IReadRepository读写分离CQRS完整解析【免费下载链接】eShopOnWebSample ASP.NET Core 8.0 reference application, now community supported: https://github.com/NimblePros/eShopOnWeb项目地址: https://gitcode.com/gh_mirrors/es/eShopOnWeb在 ASP.NET Core 8.0 参考应用 eShopOnWeb 中IRepository与IReadRepository两个接口的分工正是 CQRS命令查询职责分离思想最简洁的落地方式写操作走命令仓储读操作走查询仓储。本文带你用 10 分钟读懂这套读写分离设计背后的原理与收益。 什么是 CQRS一句话讲清楚CQRSCommand Query Responsibility Segregation命令查询职责分离的核心思想只有一句话负责改数据的代码和负责读数据的代码应该用不同的接口分开。想象一家餐厅点菜写和取餐读由两个窗口完成各自有独立的流程互不干扰。CQRS 就是把软件里的点菜窗口和取餐窗口拆成两个接口而不是让一个窗口又接单又出餐。在 eShopOnWeb 里这个两个窗口就是IRepository—— 命令窗口负责增、删、改IReadRepository—— 查询窗口负责只读查询 在 eShopOnWeb 中找到这两个接口两个接口定义都非常短小位于应用核心层的 Interfaces 目录命令仓储IRepository.cs查询仓储IReadRepository.cs它们都基于 Ardalis.Specification 规范模式的基类区别在于接口继承的基类能力IRepositoryTIRepositoryBaseT增、删、改、查完整读写IReadRepositoryTIReadRepositoryBaseT仅查询只读注意两者都有相同的约束条件泛型参数T必须实现IAggregateRoot聚合根接口见 IAggregateRoot.cs。这正是 DDD领域驱动设计的味道——仓储只管理聚合根如Basket购物篮、Order订单、CatalogItem商品。⚙️ 一个实现类两种身份真正实现这两个接口的只有一个类EfRepository EfRepository.cspublic class EfRepositoryT : RepositoryBaseT, IReadRepositoryT, IRepositoryT where T : class, IAggregateRoot它同时实现了读写两个接口底层都走同一个 Entity Framework Core 数据库上下文见 CatalogContext.cs。这是否意味着读写分离没意义恰恰相反。接口隔离的价值不在于物理上分两台数据库而在于逻辑上约束代码行为依赖IReadRepositoryOrder的类从编译器层面就无法执行任何写操作依赖IRepositoryOrder的类明确表达这里会发生数据变更未来若要升级为真正的读写分离比如读走从库、走缓存甚至搜索索引只需替换查询仓储的实现业务代码一行都不用改。 谁在写谁在读一张表看懂在 Web 项目中依赖注入是这样注册的见 ConfigureCoreServices.cs 与 PublicApi/Program.csservices.AddScoped(typeof(IRepository), typeof(EfRepository)); services.AddScoped(typeof(IReadRepository), typeof(EfRepository));项目里各处对两个接口的使用情况一目了然使用方依赖的接口职责BasketService.csIRepositoryBasket加购、改数量、转移/删除购物篮写OrderService.csIRepositoryOrder等下单时创建新订单写GetMyOrdersHandler.csIReadRepositoryOrder查询我的订单列表读GetOrderDetailsHandler.csIReadRepositoryOrder查询订单详情读BasketQueryService.cs直接用 DbContext在数据库端统计购物篮商品总数读规律非常清晰Service 层负责业务写Handler 层MediatR 查询管道负责业务读。 为什么这样设计三大实际收益1. 意图显式化代码自带文档看到IRepositoryBasket就知道这段代码会改数据看到IReadRepositoryOrder就知道这段代码只读、可放心并行执行。接口本身就是最好的注释。2. 查询可以无后顾之忧地优化只读路径不受业务规则约束因此可以大胆优化。例如 BasketQueryService.cs 中的CountTotalBasketItems把SUM聚合直接下推到数据库执行而不是把数据拉回内存再算——这种优化只适合放在读路径里做。3. 为架构演进留好后门今天IRepository和IReadRepository指向同一个实现但接口已经分离意味着✅ 读查询未来可替换为缓存、Redis、Elasticsearch✅ 写操作可替换为消息队列真正的 CQRS 事件溯源✅ 读写连接可分流到主从数据库这正是 eShopOnWeb 作为微软官方参考应用的示范意义不为了上架构而上架构而是用最小的接口改动换取最大的演进自由度。 新手上手路径3 步读懂这条链路如果你想在自己的项目里借鉴这套设计按下面顺序读源码即可看接口IRepository.cs → IReadRepository.cs理解接口契约看实现EfRepository.cs看一个类如何同时服务两种职责看用法对比 BasketService.cs写路径与 GetOrderDetailsHandler.cs读路径体会命令与查询的不同气质配套的查询条件对象Specification也值得一读例如 BasketWithItemsSpecification.cs 展示了如何声明式地表达查谁的购物篮、带出哪些明细。✅ 总结CQRS 不是复杂而是分得清常见误区eShopOnWeb 的做法CQRS 必须上消息队列和事件溯源两个接口 一个实现类即可落地读写分离一定要两台数据库逻辑分离先行物理分离随时可升级参考架构一定很复杂接口各只有 3 行代码简洁得惊人eShopOnWeb 用最小的代价告诉我们CQRS 的本质不是技术栈的堆砌而是写归写、读归读的清晰边界。理解了IRepository与IReadRepository这对接口你就掌握了在 ASP.NET Core 项目中引入 CQRS 的第一块基石 。【免费下载链接】eShopOnWebSample ASP.NET Core 8.0 reference application, now community supported: https://github.com/NimblePros/eShopOnWeb项目地址: https://gitcode.com/gh_mirrors/es/eShopOnWeb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考