支持多方法处理请求的C#/OOP解耦架构方案咨询
C# 场景下请求多处理者解耦架构实现方案
核心需求的本质是让请求发起方和处理方完全无直接依赖,处理逻辑按构建版本自动路由,最适配的实现是抽象契约层+轻量中介分发+依赖注入装配的组合,完全符合OOP设计原则,落地代码如下:
核心实现步骤
- 第一步:抽离最薄公共契约层
把请求、处理者、分发器的抽象单独放在无业务依赖的契约项目里,所有业务模块都只依赖这层抽象,不依赖其他业务模块的具体实现:// 所有业务请求的标记接口,无任何方法约束 public interface IRequest { } // 请求处理者泛型抽象,约束请求类型必须实现IRequest public interface IRequestHandler<in TRequest> where TRequest : IRequest { Task HandleAsync(TRequest request, CancellationToken ct = default); } // 请求分发器抽象,发起方只依赖这个接口发送请求 public interface IRequestDispatcher { Task SendAsync<TRequest>(TRequest request, CancellationToken ct = default) where TRequest : IRequest; } - 第二步:各模块独立实现业务逻辑
请求发起方(类A)、不同版本的处理方(类B、类C)分别在各自的模块里实现逻辑,互相之间没有任何项目引用、代码依赖:// 具体业务请求,只携带操作需要的参数,不耦合任何处理逻辑 public record OperationRequest(int BizId, string BizContent) : IRequest; // 发起请求的类A,只依赖分发器抽象,完全不知道类B、类C的存在 public class ClassA { private readonly IRequestDispatcher _dispatcher; // 构造函数注入分发器实例,不接触任何具体处理类 public ClassA(IRequestDispatcher dispatcher) => _dispatcher = dispatcher; public async Task RunOperation() { var req = new OperationRequest(1001, "业务参数"); // 直接发送请求即可,不需要关心谁来处理、怎么处理 await _dispatcher.SendAsync(req); } } // V1版本处理逻辑类B,只依赖契约层和自己的业务依赖,和类A无直接关联 public class ClassB : IRequestHandler<OperationRequest> { public async Task HandleAsync(OperationRequest request, CancellationToken ct) { // V1版本的具体处理逻辑 Console.WriteLine($"V1逻辑处理请求,业务ID:{request.BizId}"); await Task.CompletedTask; } } // V2版本处理逻辑类C,和类A、类B都无直接依赖 public class ClassC : IRequestHandler<OperationRequest> { public async Task HandleAsync(OperationRequest request, CancellationToken ct) { // V2版本的具体处理逻辑 Console.WriteLine($"V2逻辑处理请求,业务ID:{request.BizId}"); await Task.CompletedTask; } } - 第三步:实现版本感知的分发逻辑
分发器是唯一持有路由规则的组件,负责根据当前构建版本匹配对应的处理者执行,路由逻辑完全集中在这里,业务层无感知:public class VersionBasedDispatcher : IRequestDispatcher { private readonly IServiceProvider _sp; // 当前程序的构建版本,可从编译常量、程序集版本、环境配置读取 private readonly int _currentVersion = ResolveCurrentVersion(); public VersionBasedDispatcher(IServiceProvider sp) => _sp = sp; public async Task SendAsync<TRequest>(TRequest request, CancellationToken ct) where TRequest : IRequest { // 按版本路由到对应处理者:V1走类B,V2及以上走类C IRequestHandler<TRequest> handler = _currentVersion switch { 1 => (IRequestHandler<TRequest>)_sp.GetRequiredService(typeof(ClassB)), >=2 => (IRequestHandler<TRequest>)_sp.GetRequiredService(typeof(ClassC)), _ => throw new NotSupportedException($"版本{_currentVersion}无对应处理实现") }; await handler.HandleAsync(request, ct); } private static int ResolveCurrentVersion() { // 示例用编译宏判断版本,可根据自己的构建流程替换读取逻辑 #if BUILD_V1 return 1; #elif BUILD_V2 return 2; #else return 1; #endif } } - 第四步:启动层统一装配依赖
在程序启动入口的DI注册阶段完成所有依赖绑定,这是唯一同时引用契约层、类A、类B、类C所在模块的地方:// .NET 控制台/ASP.NET Core 服务注册示例 var services = new ServiceCollection(); services.AddScoped<IRequestDispatcher, VersionBasedDispatcher>(); services.AddScoped<ClassB>(); services.AddScoped<ClassC>(); services.AddScoped<ClassA>(); var serviceProvider = services.BuildServiceProvider(); // 调用示例,实际业务中不需要手动获取ClassA,由DI自动注入 var aInstance = serviceProvider.GetRequiredService<ClassA>(); await aInstance.RunOperation();
方案特性
- 解耦彻底:类A所在的项目不需要引用类B、类C的程序集,B、C的逻辑修改、新增/删除处理类都不会导致A重新编译,完全符合要求
- 扩展灵活:后续要加AB测试分流、多租户差异化处理、灰度发布规则,只需要修改分发器的路由逻辑,业务代码零改动
- 符合开闭原则:新增处理逻辑只需要新增对应的处理类,不需要修改已有业务代码
不想自己实现分发逻辑的话,直接用MediatR即可,它已经封装了完整的请求分发能力,只需要在DI注册阶段按当前构建版本注册对应请求的处理实现即可,核心原理和上述实现完全一致。
内容的提问来源于stack exchange,提问作者mhdnt
相关产品推荐
相关产品推荐

