news 2026/8/25 8:20:53

SlimMessageBus实现DDD领域事件:MessageBus.Current静态访问器与聚合根事件发布实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SlimMessageBus实现DDD领域事件:MessageBus.Current静态访问器与聚合根事件发布实战

SlimMessageBus实现DDD领域事件:MessageBus.Current静态访问器与聚合根事件发布实战

【免费下载链接】SlimMessageBusLightweight message bus interface for .NET (pub/sub and request-response) with transport plugins for popular message brokers.项目地址: https://gitcode.com/gh_mirrors/sl/SlimMessageBus

SlimMessageBus是 .NET 生态中的轻量级消息总线(Lightweight Message Bus),支持发布/订阅(Pub/Sub)与请求/响应(Request/Response)两种模式。本文以官方 DDD 示例为蓝本,手把手演示如何用 SlimMessageBus 实现DDD 领域事件:聚合根在状态变更后,通过MessageBus.Current静态访问器发布事件,由进程内的 Memory 传输直接分发给事件处理器——全程无需任何外部消息中间件,5 分钟即可跑通。

SlimMessageBus 领域事件的发布订阅架构:一个主题承载多种消息类型

一、为什么用消息总线实现领域事件?

在 DDD(领域驱动设计)中,聚合根状态变更后需要通知审计、物流、通知等多个模块。传统的直接调用会让领域层耦合越来越多的"旁观者",而领域事件将"发生了什么"与"谁来处理"彻底解耦。

用消息总线做领域事件有三个好处:

  • 🧩解耦:聚合根只管发布,完全不知道有哪些处理器
  • 📮一对多广播:同一条事件可以同时被多个IConsumer订阅处理
  • 🔌可替换传输:今天用进程内 Memory,明天换成 RabbitMQ / Kafka 只改配置不改业务代码

官方文档对这个用例的描述见 docs/UseCases/DomainEvents.md,完整可运行示例在src/Samples/Sample.DomainEvents.WebApisrc/Samples/Sample.DomainEvents.Domainsrc/Samples/Sample.DomainEvents.Application三个项目中。

二、MessageBus.Current 静态访问器的工作原理

2.1 解决什么问题?

领域模型(聚合根)通常不依赖 DI 容器,怎么拿到IMessageBus?SlimMessageBus 的答案是一个静态入口——MessageBus.Current,定义在src/SlimMessageBus/MessageBus.cs第 19 行:

public static IMessageBus Current => _provider();

它的核心机制是:Current不是单例,而是"当前执行上下文"的代理

  • SetProvider(第 14 行)注册一个解析函数,由宿主插件按运行环境决定返回什么实例
  • ASP.NET 场景下,AddAspNet()插件让它返回当前 Web 请求作用域IMessageBus
  • 消费消息时,它返回当前正在处理的消息作用域的实例
  • 底层基于AsyncLocal绑定到任务(Task),因此async/await跨线程时作用域也不会丢失

2.2 作用域意味着什么?

事件发布会加入正在进行的作用域(Web 请求作用域或消息处理作用域)。也就是说,在请求里发布的事件,其处理器与你的业务代码共享同一个 DI Scope,可以直接注入仓储、日志等服务——这是"进程内领域事件"能保持事务一致性的关键。

三、聚合根实战:三行代码发布领域事件

3.1 定义领域事件(一个 POCO 即可)

领域事件就是最简单的 POCO,示例中src/Samples/Sample.DomainEvents.Domain/OrderSubmittedEvent.cs仅 7 行:

public record OrderSubmittedEvent(Order Order) { public DateTime Timestamp { get; } = DateTime.UtcNow; }

3.2 聚合根内发布事件

打开示例聚合根src/Samples/Sample.DomainEvents.Domain/Order.cs,提交订单的逻辑只有两行核心代码(第 37-43 行):

public async Task Submit() { State = OrderState.Submitted; // 1. 变更领域状态 var e = new OrderSubmittedEvent(this); await MessageBus.Current.Publish(e); // 2. 发布领域事件 }

注意Submit()async方法——Memory 传输默认阻塞式发布(事件处理器执行完才返回),这样await order.Submit()返回时,所有领域事件处理器已经执行完毕,业务语义最清晰。

3.3 事件处理器:实现 IConsumer

处理器实现IConsumer<T>接口即可被发现,示例见src/Samples/Sample.DomainEvents.Application/DomainEventHandlers/OrderSubmittedHandler.cs

public class OrderSubmittedHandler : IConsumer<OrderSubmittedEvent> { public Task OnHandle(OrderSubmittedEvent e, CancellationToken ct) { // 记录审计日志、生成物流单…… } }

处理器从 DI 容器按作用域解析,可以随意注入ILogger等依赖。

四、最快配置方法:Memory 传输 + ASP.NET 插件

整个 Web 项目只需一处注册,见src/Samples/Sample.DomainEvents.WebApi/Startup.cs第 40-46 行:

services.AddSlimMessageBus(mbb => { mbb.WithProviderMemory() // 进程内传输 .AutoDeclareFromAssemblyContaining<OrderSubmittedHandler>() // 自动扫描声明消费者 .AddAspNet(); // 让 MessageBus.Current 感知 Web 请求作用域 }); services.AddHttpContextAccessor(); // AspNet 插件的必需依赖

三个配置点各管一件事:

配置作用
WithProviderMemory()使用进程内传输,零外部依赖
AutoDeclareFromAssemblyContaining<T>()反射扫描程序集,自动声明所有IConsumer<T>消费者
AddAspNet()MessageBus.Current绑定到当前 Web 请求作用域

💡 需要跨进程、持久化或高吞吐时,把WithProviderMemory()换成 RabbitMQ、Kafka、Azure Service Bus 等官方传输插件即可,业务代码一行不改。

五、完整调用链:从 HTTP 请求到事件分发

src/Samples/Sample.DomainEvents.WebApi/Controllers/OrdersController.cs的下单接口为例,整条链路非常直观:

POST /api/orders → new Order(customer) 创建聚合根 → order.Add(...) 添加订单行 → await order.Submit() 聚合根发布 OrderSubmittedEvent → MessageBus.Current.Publish(...) (Web 请求作用域内) → OrderSubmittedHandler.OnHandle(...) 执行审计/日志 → return Ok() 此时事件处理器已全部执行完毕

SlimMessageBus 的管道还支持在任意环节插入拦截器,例如审计日志、重试策略、FluentValidation 校验等,整体管道示意如下:

六、新手避坑清单 ✅

  1. 忘记AddHttpContextAccessor()——AddAspNet()依赖它注入 HTTP 上下文,漏掉后MessageBus.Current无法解析请求作用域
  2. 在请求作用域之外调用MessageBus.Current—— 例如在static工具方法或线程池回调中调用会抛MessageBusException;确无作用域的场景请显式从 DI 注入IMessageBus
  3. 事件发布时机—— Memory 传输默认阻塞式发布,若处理器执行缓慢且想改为异步,请参考 docs/provider_memory.md 中的 "Asynchronous Publish" 一节
  4. 处理器数量无上限—— 同一事件类型可以有任意多个IConsumer<T>,SlimMessageBus 会自动广播给全部订阅者

小结

用 SlimMessageBus 实现 DDD 领域事件的精髓可以浓缩为一句话:聚合根await MessageBus.Current.Publish(event),处理器实现IConsumer<T>,其余交给AutoDeclareFromAssemblyContaining。借助MessageBus.Current静态访问器的作用域感知能力,领域事件与 Web 请求共享同一个 DI Scope,既解耦又保持了事务一致性——而这,正是进程内消息总线最优雅的用法。

【免费下载链接】SlimMessageBusLightweight message bus interface for .NET (pub/sub and request-response) with transport plugins for popular message brokers.项目地址: https://gitcode.com/gh_mirrors/sl/SlimMessageBus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/25 8:19:59

SQL注入漏洞报告实战:从PHP+HTTP链路到可复现提交

1. 这不是“教科书模板”&#xff0c;而是一份真实提交过的SQL注入漏洞报告复盘你手头这份标题叫《SQL注入漏洞提交报告&#xff08;示例&#xff09;》&#xff0c;但别被“示例”两个字骗了——它背后藏着的是真实渗透测试中从发现、验证、构造到最终闭环的完整链路。我干这行…

作者头像 李华
网站建设 2026/8/25 8:18:18

C++除法陷阱与安全实践:从整数截断到工业级防护

1. 项目概述&#xff1a;这不是一道“送分题”&#xff0c;而是一次C底层能力的实战压力测试“C A除以B”——看到这个标题&#xff0c;很多人第一反应是&#xff1a;“这有什么好写的&#xff1f;不就是a / b吗&#xff1f;”我刚接触C时也这么想&#xff0c;直到在某次嵌入式…

作者头像 李华
网站建设 2026/8/25 8:14:43

框架质疑学习法:你的学习方式需要升级吗?

好的&#xff0c;我将根据您的要求&#xff0c;将文章与“质疑读书法”的概念进行深度关联&#xff0c;并优化整体表达。以下是优化后的版本&#xff1a; 从“质疑读书”到“框架质疑”&#xff1a;学习方法的演进与实践 学习的艺术从未停止革新。从早期强调“勤思好问”的传统…

作者头像 李华
网站建设 2026/8/25 8:11:13

脱离Anaconda:手动搭建纯净PyTorch与PyCharm开发环境指南

1. 项目概述&#xff1a;为什么选择“裸装”Python开发环境&#xff1f; 在深度学习或Python开发的入门阶段&#xff0c;几乎所有的教程都会指向一个“全家桶”式的解决方案——Anaconda。它确实方便&#xff0c;预装了成百上千个科学计算包&#xff0c;自带环境管理工具conda…

作者头像 李华
网站建设 2026/8/25 8:09:48

Python交互式Web框架:Streamlit、Gradio、Dash、Hyperdiv

概述 本文搜集几个流行的后端友好、基于Python语言、交互式Web框架&#xff0c;可实现无需过多前端开发技能&#xff0c;几行代码实现一个MVP展示界面&#xff0c;可应用&#xff1a;数据可视化、原型快速开发、机器学习数据探索等诸多场景。 Streamlit 官网&#xff0c;开源…

作者头像 李华