去年某个周二,Jimmy Bogard在GitHub上推送了一条提交。第二天,全球数万个.NET项目的依赖管理器开始报警——MediatR转向了商业授权。这个用了十年的"基础设施",突然需要按开发者席位付费。

戏剧性的是,恐慌只持续了72小时。社区很快发现:那个被当成黑盒用了十年的库,拆开之后几乎全是空壳。命令分发、处理器注册、管道行为——这些被包装成"框架能力"的东西,纯手写不到50行。

打开网易新闻 查看精彩图片

被混淆的两个概念

MediatR和CQRS在.NET生态里纠缠太深,以至于很多人分不清边界。

「CQRS的核心就两句话:命令改状态,查询读数据。」原文作者用极简定义划清界限。命令有副作用,查询无副作用——除此之外全是实现细节,不需要任何库。

MediatR的流行源于一个历史巧合:它把"处理器自动发现+统一分发器+行为管道"打包成了三行配置代码。这三行代码解决了启动阶段的繁琐,却也制造了依赖幻觉——仿佛没有它,CQRS就无法运转。

授权变更撕开了一道口子。团队被迫审视:那些三行代码换来的便利,代价是放弃对抽象层的完全控制。

正方:手写实现的价值

剥离框架后的实现出奇地薄。五个接口定义全部关系:

两个标记接口ICommandIQuery作为类型系统的锚点,让编译器强制约束命令与处理器的配对关系。三个处理器接口分别处理无返回值命令、有返回值命令、查询——泛型参数确保「错误的使用会在编译期报错,而非运行时爆炸」。

作者特意为标记接口辩护:通常这确实是反模式,但此处它们承担了两个不可替代的职责。一是为程序集扫描提供明确目标——没有ICommand,无法写出AssignableTo(typeof(ICommand<>))这样的筛选条件;二是让分发器的泛型约束具备编译期检查能力。

DI容器接管剩余工作。处理器注册、生命周期管理、依赖注入——这些本就是容器的基础设施,无需额外封装。手写代码的优势在于透明:每个抽象层的职责、数据流向、扩展点都暴露在开发者眼前,而非藏在库的源码深处。

反方:框架的隐性收益

手写派忽略了一个成本:团队共识。

MediatR的价值不仅是代码,更是「社区共同语言」。新成员入职,看到IRequestHandler立刻知道该往哪写逻辑;代码审查时,管道行为的命名和顺序有既定惯例;遇到异常,Stack Overflow上有数千个已解答的问题。

手写实现意味着重建这套共识。五个接口的命名风格、分发器的错误处理策略、横切关注点的注入方式——每个决策都需要团队内部反复对齐。对于五人以下的团队这或许轻松,五十人团队则可能成为摩擦源。

另一个隐性成本是维护责任。MediatR的管道行为、异常处理、性能优化由社区持续迭代;手写代码的同等能力,需要团队自己长期投入。授权费用本质上是把这部分成本货币化,而非消除。

判断:什么时候该拆,什么时候该留

决策锚点在于「控制需求」与「团队规模」的交叉。

需要完全控制抽象层时,手写是理性选择。比如:处理器需要跨进程边界(从内存调用改为HTTP/RPC)、管道行为需要与特定遥测系统深度集成、或者团队对性能有极端敏感的要求。这些场景下,MediatR的封装反而成为障碍——它的扩展点设计服务于通用场景,特殊需求需要绕路或 fork。

团队规模小、迭代速度快、无特殊基础设施需求时,保留框架更经济。授权成本若低于「重建共识+维护基础设施」的人力折算,付费是合理交易。但需警惕「路径依赖」:因历史惯性继续付费,而非主动评估。

一个值得观察的信号是:手写实现的核心代码(接口定义+分发器+注册逻辑)在多数项目中高度相似。社区可能出现轻量级替代方案——不是第二个MediatR,而是「可复制的50行代码模板」。这种形态既保留透明性,又降低重复决策成本。

MediatR的授权变更,最终暴露了一个被长期遮蔽的事实:CQRS作为模式,其复杂度被框架人为放大了。当开发者发现「原来我自己也能写」,对基础设施的迷信便开始瓦解。这种认知迁移的影响,可能比任何具体的技术决策都更深远。