事件溯源与CQRS是应对高并发系统的两种互补架构模式。事件溯源以追加方式记录每一次领域事件,业务实体的当前状态由事件重放得出;CQRS将命令写模型和查询读模型解耦,写端负责处理指令并产出事件,读端订阅事件生成扁平化视图。在C#生态中,这种组合能显著降低数据库行锁冲突,提高请求吞吐量。
核心概念
事件溯源中的核心对象是聚合根与领域事件。聚合根接收命令后产生事件,事件一旦持久化便不可修改。CQRS中的命令端调用聚合根方法,查询端直接读取已构建的读模型,避免复杂联表。
聚合根示例
下面以库存扣减为例展示C#聚合根如何产生事件:
public class InventoryItem
{
public Guid Id { get; private set; }
public int Available { get; private set; }
private List<object> _changes = new List<object>();
public InventoryItem(Guid id) { Id = id; }
public void Deduct(int qty)
{
if (Available < qty)
throw new InvalidOperationException("库存不足");
var evt = new InventoryDeducted(Id, qty);
Apply(evt);
_changes.Add(evt);
}
private void Apply(InventoryDeducted e)
{
Available -= e.Qty;
}
public IEnumerable<object> GetChanges() => _changes;
}
public class InventoryDeducted
{
public Guid Id { get; }
public int Qty { get; }
public InventoryDeducted(Guid id, int qty) { Id = id; Qty = qty; }
}
高并发下的写模型处理
在高并发写入时,利用事件存储的追加特性,配合乐观并发控制,可以避免对业务表加锁。通常使用版本号校验,若聚合根版本与存储不一致则重试命令。
事件存储写入伪代码
public async Task SaveAsync(InventoryItem item, int expectedVersion)
{
var events = item.GetChanges().ToList();
// 事件表结构: AggregateId, Version, EventType, Payload
await _eventStore.AppendAsync(item.Id, expectedVersion, events);
}
读模型与最终一致性
查询端通过后台消费者订阅事件流,将事件投影到读库。用户查询时不经过聚合根,直接访问冗余好的视图,从而抗住高并发读。
| 模型 | 职责 | 并发策略 |
|---|---|---|
| 写模型 | 处理命令、产出事件 | 乐观锁、事件追加 |
| 读模型 | 提供查询视图 | 独立库、可横向扩展 |
注意事项
- 事件 schema 演进需兼容旧事件,可使用向上适配函数。
- 投影延迟会带来短暂不一致,业务需容忍最终一致。
- 消息队列如 RabbitMQ 或 Kafka 可解耦写与投影,但须处理重复投递。
在C#项目中引入MediatR处理命令与事件分发,可让应用层更清晰,也便于接入事件溯源组件。
小结
通过事件溯源记录不可变事实,配合CQRS隔离读写,C#开发者可以在订单、秒杀等场景中用较低成本支撑高并发。关键在于合理设计聚合边界与投影延时容忍度。
C#Event_SourcingCQRS高并发领域事件修改时间:2026-07-24 23:27:30