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));
}
}
}