news 2026/4/3 1:34:47

推荐一个 .NET 的 Async/await 优先的 CQRS+ES 的 DDD 框架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
推荐一个 .NET 的 Async/await 优先的 CQRS+ES 的 DDD 框架

EventFlow 是一个基于 .NET 的轻量级 CQRS + Event Sourcing 框架,提供了完善且合理的默认实现,帮助开发者以较低成本构建事件驱动的领域模型。

核心概念

Aggregates(聚合根)

聚合是领域模型的核心,它负责保证业务规则和状态变更的一致性。 在 EventFlow 中,所有对业务状态的修改都必须通过聚合完成,聚合内部通过产生 领域事件(Event) 来表达“发生了什么”,而不是直接改数据。

Command Bus & Commands(命令总线与命令)

命令是“我要做什么” 的表达,比如“创建订单”“支付订单”。 Command Bus 是所有命令的统一入口,负责把命令路由到对应的命令处理器,再由处理器调用聚合执行业务逻辑。

Event Store(事件存储)

Event Store 用来持久化聚合产生的事件流,而不是直接存状态。 聚合的当前状态,是通过“从头回放事件”得到的。

EventFlow 支持多种事件存储方式:

In-memory / Files(仅测试)

SQL Server / EF Core

SQLite / PostgreSQL

EventStore(专业事件存储)

Subscribers(事件订阅者)

订阅者用于监听已经提交成功的领域事件,并在事件发生后执行额外动作,例如:

发送通知

更新外部系统

触发异步流程

它不会影响聚合本身的一致性。

Read Models(读模型)

读模型是为“查询”而存在的,通常是事件的非规范化结果,结构更适合快速读取。 它和写模型(聚合)完全分离,是 CQRS 的关键组成部分。

支持的存储包括:

Elasticsearch

SQL Server / EF Core

SQLite / PostgreSQL

In-memory(测试)

Snapshots(快照)

如果一个聚合的事件非常多,每次都从头回放会变慢。 Snapshot 会定期保存聚合的“中间状态”,下次加载时只需从快照开始回放后续事件。

EventFlow 的快照是可选的,并支持快照升级。

Sagas(Saga / 进程管理器)

Saga 用来处理跨聚合、跨限界上下文的长事务流程。 它通过监听事件、发送命令,来协调多个聚合之间的协作。

Queries(查询)

Query 是对“我要查什么”的抽象描述,本身不关心怎么查。 具体执行逻辑由 Query Handler 决定,通常直接作用于读模型。

Jobs(作业 / 延时任务)

Jobs 用于执行延迟或定时任务,例如:

几分钟后发送命令

定时补偿操作

EventFlow 内置支持与 Hangfire 等调度器集成。

Event Upgrade(事件升级)

事件一旦写入 Event Store 就永远不会被修改。 当事件结构演进时,EventFlow 通过 Event Upgrader 在加载聚合时,把旧事件升级为新事件。

Event Publishing(事件发布)

有时领域事件不仅要在系统内部使用,还需要被其他系统或服务消费。 EventFlow 支持将事件发布到外部消息系统,如:

RabbitMQ

Metadata(元数据)

元数据是附加在事件上的上下文信息,例如:

用户 IP

操作人

请求来源

EventFlow 提供多种内置元数据提供器,开箱即用。

Value Objects(值对象)

值对象用于表达没有身份、只有值的领域概念,如:

用户名

邮箱

金额

它们通常是不可变的,并负责自身的合法性校验。

使用示例

[Test] public async Task Example() { // 将 EventFlow 与示例中用到的所有类型进行注册。 // 这里手动添加了事件、命令、命令处理器等, // 实际上也可以使用更简单的 AddDefaults(Assembly) 一次性注册。 var serviceCollection = new ServiceCollection() .AddLogging() .AddEventFlow(o => o .AddEvents(typeof(ExampleEvent)) .AddCommands(typeof(ExampleCommand)) .AddCommandHandlers(typeof(ExampleCommandHandler)) .UseInMemoryReadStoreFor<ExampleReadModel>()); using (var serviceProvider = serviceCollection.BuildServiceProvider()) { // 为聚合根创建一个新的唯一标识 var exampleId = ExampleId.New; // 从容器中解析命令总线,并通过它发布一个命令 var commandBus = serviceProvider.GetRequiredService<ICommandBus>(); await commandBus.PublishAsync( new ExampleCommand(exampleId, 42), CancellationToken.None); // 从容器中解析查询处理器, // 使用内置的“按 ID 查询读模型”的查询, // 获取表示当前聚合状态的读模型 var queryProcessor = serviceProvider.GetRequiredService<IQueryProcessor>(); var exampleReadModel = await queryProcessor.ProcessAsync( new ReadModelByIdQuery<ExampleReadModel>(exampleId), CancellationToken.None); // 验证读模型中的数据是否符合预期 exampleReadModel.MagicNumber.Should().Be(42); } }
// 聚合根 publicclassExampleAggregate : AggregateRoot<ExampleAggregate, ExampleId>, IEmit<ExampleEvent> { privateint? _magicNumber; public ExampleAggregate(ExampleId id) : base(id) { } // 由命令触发调用的业务方法 public void SetMagicNumber(int magicNumber) { if (_magicNumber.HasValue) throw DomainError.With("Magic number already set"); // 通过产生领域事件来表达状态变更 Emit(new ExampleEvent(magicNumber)); } // 事件溯源过程中用于应用事件的方法 // EventFlow 提供了多种方式来应用事件(例如使用状态对象), // Apply 方法是其中最简单、最直接的一种 public void Apply(ExampleEvent aggregateEvent) { _magicNumber = aggregateEvent.MagicNumber; } }

总结

EventFlow 核心全面接口化,具备良好的可配置性与可扩展性,同时不依赖后台线程或工作进程,行为清晰可控。采用 MIT 许可证,对企业级项目友好,既适合快速上手学习 CQRS/ES,也适用于对架构演 进有要求的实际业务系统。

项目地址:https://github.com/eventflow/EventFlow

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

amazingdotnet 2025

amazingdotnet 2025Intro回顾一下 2025 年比较受大家喜欢的几篇文章Articles你好 dotnet run file, 再见 csproj.NET 10 里更简单的 C#C# 14 Extensions 来了C# 14 Extension 实现更简单高效的扩展在 .NET 10 中使用 C# 实现 CI 脚本.NET 10 中的 dnxASP.NET Core 10 中的 Ser…

作者头像 李华
网站建设 2026/3/28 21:57:05

对比传统方法:FOFA如何十倍提升资产发现效率

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个网络资产发现效率对比工具&#xff0c;分别模拟传统扫描工具&#xff08;如Nmap&#xff09;和FOFA搜索在相同网络环境下的表现。要求展示查询时间、结果准确性和资源消耗…

作者头像 李华
网站建设 2026/3/27 12:46:25

1小时搭建你的专属网址推荐系统

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 使用快马平台快速开发一个网址推荐系统原型。用户输入兴趣标签&#xff0c;系统返回个性化网址推荐。要求支持用户注册和兴趣偏好设置&#xff0c;前端使用模板快速搭建&#xff0…

作者头像 李华
网站建设 2026/3/26 0:02:39

工业控制中UART波特率精确配置:快速理解关键参数

工业控制中UART波特率精确配置&#xff1a;从原理到实战的深度解析在某次PLC与远程传感器通信调试现场&#xff0c;工程师发现Modbus RTU数据包频繁CRC校验失败。排查数小时后&#xff0c;问题根源竟不是接线干扰或协议错误&#xff0c;而是——发送端和接收端的波特率相差了不…

作者头像 李华
网站建设 2026/3/31 5:08:40

如何从AI学生GitCode仓库下载GLM-4.6V-Flash-WEB完整镜像?

如何从AI学生GitCode仓库下载GLM-4.6V-Flash-WEB完整镜像&#xff1f; 在多模态大模型迅速落地的今天&#xff0c;一个真正“能跑起来”的开源项目&#xff0c;远比一纸论文更具说服力。尤其是在智能客服、电商图文理解、教育辅助等场景中&#xff0c;开发者不再满足于“模型性…

作者头像 李华
网站建设 2026/4/1 17:32:58

使用Docker部署docker-compose-ui工具并实现公网访问

目录 ⛳️推荐 1. 安装Docker 2. 检查本地docker环境 3. 安装cpolar内网穿透 4. 使用固定二级子域名地址远程访问 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Docker Co…

作者头像 李华