using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.StateManagement; using GFramework.Core.Events; using GFramework.Core.StateManagement; namespace GFramework.Core.Extensions; /// /// 为 Store 提供到 EventBus 的兼容桥接扩展。 /// 该扩展面向旧模块渐进迁移场景,使现有事件消费者可以继续观察 Store 的 action 分发和状态变化。 /// public static class StoreEventBusExtensions { /// /// 将 Store 的 dispatch 和状态变化同时桥接到 EventBus。 /// dispatch 事件会逐次发布;状态变化事件会复用 Store 自身的通知折叠语义,因此批处理中只发布最终状态。 /// /// 状态树的根状态类型。 /// 源 Store。 /// 目标事件总线。 /// 是否发布每次 action 分发事件。 /// 是否发布状态变化事件。 /// 用于拆除桥接的句柄。 public static IUnRegister BridgeToEventBus( this Store store, IEventBus eventBus, bool publishDispatches = true, bool publishStateChanges = true) { ArgumentNullException.ThrowIfNull(store); ArgumentNullException.ThrowIfNull(eventBus); IUnRegister? dispatchBridge = null; IUnRegister? stateBridge = null; if (publishDispatches) { dispatchBridge = store.BridgeDispatchesToEventBus(eventBus); } if (publishStateChanges) { stateBridge = store.BridgeStateChangesToEventBus(eventBus); } return new DefaultUnRegister(() => { dispatchBridge?.UnRegister(); stateBridge?.UnRegister(); }); } /// /// 将 Store 的每次 dispatch 结果桥接到 EventBus。 /// 该桥接通过中间件实现,因此即使某次分发未改变状态,也会发布对应的 dispatch 事件。 /// /// 状态树的根状态类型。 /// 源 Store。 /// 目标事件总线。 /// 用于移除 dispatch 桥接中间件的句柄。 public static IUnRegister BridgeDispatchesToEventBus(this Store store, IEventBus eventBus) { ArgumentNullException.ThrowIfNull(store); ArgumentNullException.ThrowIfNull(eventBus); return store.RegisterMiddleware(new DispatchEventBusMiddleware(eventBus)); } /// /// 将 Store 的状态变化桥接到 EventBus。 /// 该桥接复用 Store 的订阅通知语义,因此只会在状态真正变化时发布事件。 /// /// 状态树的根状态类型。 /// 源 Store。 /// 目标事件总线。 /// 用于移除状态变化桥接的句柄。 public static IUnRegister BridgeStateChangesToEventBus(this IReadonlyStore store, IEventBus eventBus) { ArgumentNullException.ThrowIfNull(store); ArgumentNullException.ThrowIfNull(eventBus); return store.Subscribe(state => eventBus.Send(new StoreStateChangedEvent(state, DateTimeOffset.UtcNow))); } /// /// 用于把 dispatch 结果桥接到 EventBus 的内部中间件。 /// 选择中间件而不是改写 Store 核心提交流程,是为了把兼容层成本保持在可选扩展中。 /// /// 状态树的根状态类型。 private sealed class DispatchEventBusMiddleware(IEventBus eventBus) : IStoreMiddleware { /// /// 目标事件总线。 /// private readonly IEventBus _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); /// /// 执行后续 dispatch 管线,并在结束后把分发结果发送到 EventBus。 /// /// 当前分发上下文。 /// 后续管线。 public void Invoke(StoreDispatchContext context, Action next) { next(); var dispatchRecord = new StoreDispatchRecord( context.Action, context.PreviousState, context.NextState, context.HasStateChanged, context.DispatchedAt); _eventBus.Send(new StoreDispatchedEvent(dispatchRecord)); } } }