Mediator
把"谁处理这个请求"从调用方剥离出去——调用方只管
send(),框架负责路由到正确的处理器。
适用场景
- 控制器里塞了过多注入——
IOrderService、IInventoryService、IEmailService七八个依赖排成一排 - 想加日志、校验、事务这些横切逻辑,但不想在每个方法里重复
try/catch/commit - 希望命令和查询走不同路径,写操作与读操作职责分离
- 需要一对多的通知——比如订单创建后同时通知物流系统和积分系统
核心问题
没有中介器时,控制器直接依赖多个服务:
Controller → IOrderService + IInventoryService + IEmailService + ILogger + ...每加一个横切关注点(日志、事务、校验),所有控制器都要改一轮。
中介器把这条链路拆成两步:调用方发请求 → 框架路由到处理器。处理器之间互不知道对方存在,横切逻辑收敛到管道里。
Controller → IMediator.send(request)
│
├─ Pipeline(日志 → 事务 → 校验)
│
└─ Handler(业务逻辑)快速开始
定义请求 + 处理器,一行 send() 发起:
import soulsoft_mediator.*
import soulsoft_extensions_injection.*
// 1. 定义请求
public class PingRequest <: IRequest<String> {}
// 2. 实现处理器
public class PingHandler <: IRequestHandler<PingRequest, String> {
public func handle(_: PingRequest): String {
"pong"
}
}
// 3. 注册
let services = ServiceCollection()
services.addMediator()
.registerServices(PackageInfo.get("myapp"))
let provider = services.build()
// 4. 调用
try (scope = provider.getOrThrow<IServiceScopeFactory>().createScope()) {
let mediator = scope.services.getOrThrow<IMediator>()
let result = mediator.send<PingRequest, String>(PingRequest())
// result == "pong"
}核心概念
两种消息模式
| 模式 | 接口 | 返回值 | 处理器数量 | 典型用途 |
|---|---|---|---|---|
| 请求/响应 | IRequest<TResponse> | 有返回值 | 严格一对一 | 命令、查询 |
| 通知/事件 | INotification | 无返回值 | 一对多 | 领域事件广播 |
请求走 send(),框架保证只存在一个处理器,找不到就抛异常:
public class CreateOrderCommand <: IRequest<Int64> {
public var productId: Int64 = 0
public var count: Int64 = 0
}
public class CreateOrderHandler <: IRequestHandler<CreateOrderCommand, Int64> {
public func handle(cmd: CreateOrderCommand): Int64 {
// 业务逻辑
return newOrderId
}
}
let orderId = mediator.send<CreateOrderCommand, Int64>(cmd)通知走 publish(),所有订阅者都会收到:
public class OrderCreatedEvent <: INotification {
public var orderId: Int64 = 0
}
public class LogisticsHandler <: INotificationHandler<OrderCreatedEvent> {
public func handle(e: OrderCreatedEvent): Unit { /* 通知物流 */ }
}
public class PointsHandler <: INotificationHandler<OrderCreatedEvent> {
public func handle(e: OrderCreatedEvent): Unit { /* 发放积分 */ }
}
// 两个处理器都会执行
mediator.publish(OrderCreatedEvent(orderId: 123))管道:横切关注点的统一入口
IPipelineHandler 是中介器最关键的扩展点——它让你在"请求到达处理器之前"和"处理器返回之后"插入逻辑,不修改处理器本身。
public interface IPipelineHandler {
func handle(request: Any, next: RequestDelegate): Any
}next() 调用下一个管道或最终的处理器。你可以:
- 在
next()之前做前置处理(校验、开启事务) - 在
next()之后做后置处理(提交事务、记录日志) - 不调用
next()直接返回(短路,如权限不足直接拒绝) - 捕获
next()的异常(统一异常处理) - 修改
next()的返回值(脱敏、包装)
注册顺序决定嵌套顺序——先注册的在最外层:
services.addMediator()
.addPipelineHandler<LoggingBehavior>() // ① 最外层
.addPipelineHandler<TransactionBehavior>() // ② 内层
.registerServices(PackageInfo.get("myapp"))
// 执行顺序:Logging.before → Transaction.before → Handler → Transaction.after → Logging.after常见用法
管道:日志行为
在请求前后各打一条日志,不碰处理器代码:
public class LoggingBehavior <: IPipelineHandler {
public func handle(request: Any, next: RequestDelegate): Any {
let name = request.getType().name
println("[开始] ${name}")
let result = next()
println("[完成] ${name}")
return result
}
}管道:事务行为
把 DbContext 管理和事务包裹收敛到一个管道里,服务层不再重复 try(ctx) { try(tx) { ... tx.commit() } } 模板:
public class TransactionBehavior <: IPipelineHandler {
private let _sp: IServiceProvider
public init(sp: IServiceProvider) {
_sp = sp
}
public func handle(request: Any, next: RequestDelegate): Any {
try (ctx = _sp.getService<AppDbContext>()) {
try (tx = ctx.beginTransaction()) {
let result = next()
tx.commit()
return result
} // 异常时 close() 自动 rollback
}
}
}这与 事务 中"从重复模板到管道/AOP"一节相呼应——管道一把事务模板从所有服务方法中提走,业务处理器只管核心逻辑。
管道:异常处理
统一捕获处理器异常,避免每个方法写 try/catch:
public class ExceptionBehavior <: IPipelineHandler {
public func handle(request: Any, next: RequestDelegate): Any {
try {
return next()
} catch (e: Exception) {
println("[异常] ${request.getType().name}: ${e.message}")
throw e
}
}
}短路:权限校验
不满足条件时直接返回,不进入处理器:
public class AuthBehavior <: IPipelineHandler {
public func handle(request: Any, next: RequestDelegate): Any {
if (request is IAuthRequired) {
return "无权限" // 短路,不调用 next()
}
return next()
}
}返回值转换
在 next() 返回后对结果做统一包装:
public class ResultWrapBehavior <: IPipelineHandler {
public func handle(request: Any, next: RequestDelegate): Any {
let result = next()
return ApiResult(code: 0, data: result) // 统一包装返回值
}
}通知:一对多广播
// 处理器中发布通知
public class CreateOrderHandler <: IRequestHandler<CreateOrderCommand, Int64> {
private let _publisher: IPublisher
public init(publisher: IPublisher) {
_publisher = publisher
}
public func handle(cmd: CreateOrderCommand): Int64 {
let orderId = newOrderId
_publisher.publish(OrderCreatedEvent(orderId: orderId))
return orderId
}
}与其他模块组合
- 管道中的
DbContext/ 事务管理来自 事务 - 所有处理器和管道行为通过 依赖注入 注册和解析
ISender/IPublisher可从 DI 容器独立解析,IMediator是二者的合并接口- 通知不适合走管道——
publish()直接分发到所有处理器 - 管道不支持异步(当前实现为同步),需要异步时在处理器内部自行处理
生产建议
- 管道里放横切关注点,处理器里放业务逻辑——不要反过来
- 通知处理器各自独立——某个通知处理器抛异常不会影响其他处理器
- 命令用
IRequest<Unit>——不需要返回值时,用Unit表示"只是副作用" registerServices()只传当前程序集——避免扫描不相关的包,拖慢启动
常见问题
请求处理器能返回 Unit 吗?
能。命令不需要返回值时用 IRequest<Unit>,处理器 handle() 返回 Unit。
通知能走管道吗?
不能。publish() 直接遍历处理器列表调用,不经过 IPipelineHandler。如果需要拦截通知,在通知处理器内部处理。
管道执行顺序是怎样的?
先注册的管道在最外层。注册顺序 A → B → C 的执行流程:A.before → B.before → C.before → Handler → C.after → B.after → A.after。
一个请求能对应多个处理器吗?
不能。IRequest<TResponse> 严格一对一,多个处理器同类型会抛异常。需要一对多用 INotification。