diff --git a/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
new file mode 100644
index 00000000..e26310d7
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
@@ -0,0 +1,51 @@
+using GFramework.Core.Abstractions.Architectures;
+
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 定义架构上下文使用的 CQRS runtime seam。
+/// 该抽象把请求分发、通知发布与流式处理从具体实现中解耦,
+/// 使 不再直接依赖某个固定的 runtime 类型。
+///
+public interface ICqrsRuntime
+{
+ ///
+ /// 发送请求并返回响应。
+ ///
+ /// 响应类型。
+ /// 当前架构上下文,用于上下文感知处理器注入与嵌套请求访问。
+ /// 要分发的请求。
+ /// 取消令牌。
+ /// 请求响应。
+ ValueTask SendAsync(
+ IArchitectureContext context,
+ IRequest request,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 发布通知到所有已注册处理器。
+ ///
+ /// 通知类型。
+ /// 当前架构上下文,用于上下文感知处理器注入。
+ /// 要发布的通知。
+ /// 取消令牌。
+ /// 表示通知分发完成的值任务。
+ ValueTask PublishAsync(
+ IArchitectureContext context,
+ TNotification notification,
+ CancellationToken cancellationToken = default)
+ where TNotification : INotification;
+
+ ///
+ /// 创建流式请求的异步响应序列。
+ ///
+ /// 流元素类型。
+ /// 当前架构上下文,用于上下文感知处理器注入。
+ /// 流式请求。
+ /// 取消令牌。
+ /// 按需生成的异步响应序列。
+ IAsyncEnumerable CreateStream(
+ IArchitectureContext context,
+ IStreamRequest request,
+ CancellationToken cancellationToken = default);
+}
diff --git a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
index 84d53f63..7a70b332 100644
--- a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
+++ b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
@@ -17,6 +17,9 @@
+
+
+
all
diff --git a/GFramework.Core.Tests/CqrsTestRuntime.cs b/GFramework.Core.Tests/CqrsTestRuntime.cs
index e9664925..f2f64f9b 100644
--- a/GFramework.Core.Tests/CqrsTestRuntime.cs
+++ b/GFramework.Core.Tests/CqrsTestRuntime.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
@@ -38,6 +39,65 @@ internal static class CqrsTestRuntime
?? throw new InvalidOperationException(
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
+ private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher type.");
+
+ private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher constructor.");
+
+ private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar type.");
+
+ private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
+ DefaultCqrsHandlerRegistrarType.GetConstructor(
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar constructor.");
+
+ ///
+ /// 为裸测试容器补齐默认 CQRS runtime seam。
+ /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
+ /// 而无需完整启动服务模块管理器。
+ ///
+ /// 目标测试容器。
+ internal static void RegisterInfrastructure(MicrosoftDiContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
+ var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
+ var registrar =
+ (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+
+ container.Register(runtime);
+ container.Register(registrar);
+ }
+
///
/// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
///
@@ -48,6 +108,8 @@ internal static class CqrsTestRuntime
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(assemblies);
+ RegisterInfrastructure(container);
+
var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
RegisterHandlersMethod.Invoke(
null,
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index b4e5af67..7126a3ad 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,6 +1,5 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
@@ -14,6 +13,8 @@ namespace GFramework.Core.Tests.Ioc;
[TestFixture]
public class MicrosoftDiContainerTests
{
+ private MicrosoftDiContainer _container = null!;
+
///
/// 在每个测试方法执行前进行设置
///
@@ -29,9 +30,9 @@ public class MicrosoftDiContainerTests
BindingFlags.NonPublic | BindingFlags.Instance);
loggerField?.SetValue(_container,
LoggerFactoryResolver.Provider.CreateLogger(nameof(MicrosoftDiContainer)));
- }
- private MicrosoftDiContainer _container = null!;
+ CqrsTestRuntime.RegisterInfrastructure(_container);
+ }
///
/// 测试注册单例实例的功能
@@ -328,6 +329,8 @@ public class MicrosoftDiContainerTests
descriptor.ServiceType == typeof(INotificationHandler)),
Is.False);
+ // Clear 会移除测试手工补齐的 CQRS seam,需要先恢复基础设施再验证程序集去重状态是否已重置。
+ CqrsTestRuntime.RegisterInfrastructure(_container);
_container.RegisterCqrsHandlersFromAssembly(assembly);
Assert.That(
diff --git a/GFramework.Core/Architectures/ArchitectureContext.cs b/GFramework.Core/Architectures/ArchitectureContext.cs
index 77c04fcf..63bb34e8 100644
--- a/GFramework.Core/Architectures/ArchitectureContext.cs
+++ b/GFramework.Core/Architectures/ArchitectureContext.cs
@@ -2,18 +2,13 @@ using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command;
using GFramework.Core.Abstractions.Cqrs;
-using GFramework.Core.Abstractions.Cqrs.Command;
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc;
-using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
-using GFramework.Core.Cqrs.Internal;
-using GFramework.Core.Logging;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Architectures;
@@ -25,15 +20,15 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
{
private readonly IIocContainer _container = container ?? throw new ArgumentNullException(nameof(container));
private readonly ConcurrentDictionary _serviceCache = new();
- private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(ArchitectureContext));
- private CqrsDispatcher? _cqrsDispatcher;
+ private ICqrsRuntime? _cqrsRuntime;
#region CQRS Integration
///
- /// 获取 CQRS 运行时分发器(延迟初始化)。
+ /// 获取 CQRS runtime seam(延迟初始化)。
///
- private CqrsDispatcher CqrsDispatcher => _cqrsDispatcher ??= new CqrsDispatcher(_container, this, _logger);
+ private ICqrsRuntime CqrsRuntime => _cqrsRuntime ??=
+ _container.Get() ?? throw new InvalidOperationException("ICqrsRuntime not registered");
///
/// 获取指定类型的服务实例,如果缓存中存在则直接返回,否则从容器中获取并缓存
@@ -73,7 +68,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
- return await CqrsDispatcher.SendAsync(request, cancellationToken);
+ return await CqrsRuntime.SendAsync(this, request, cancellationToken);
}
///
@@ -100,7 +95,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
where TNotification : INotification
{
ArgumentNullException.ThrowIfNull(notification);
- await CqrsDispatcher.PublishAsync(notification, cancellationToken);
+ await CqrsRuntime.PublishAsync(this, notification, cancellationToken);
}
///
@@ -115,7 +110,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
- return CqrsDispatcher.CreateStream(request, cancellationToken);
+ return CqrsRuntime.CreateStream(this, request, cancellationToken);
}
///
@@ -151,7 +146,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 查询结果类型
/// 要发送的查询
/// 查询结果
- public TResult SendQuery(Abstractions.Query.IQuery query)
+ public TResult SendQuery(IQuery query)
{
if (query == null) throw new ArgumentNullException(nameof(query));
var queryBus = GetOrCache();
@@ -165,7 +160,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 查询响应类型
/// 要发送的查询对象
/// 查询结果
- public TResponse SendQuery(GFramework.Core.Abstractions.Cqrs.Query.IQuery query)
+ public TResponse SendQuery(Abstractions.Cqrs.Query.IQuery query)
{
return SendQueryAsync(query).AsTask().GetAwaiter().GetResult();
}
@@ -191,7 +186,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的查询对象
/// 取消令牌,用于取消操作
/// 包含查询结果的ValueTask
- public async ValueTask SendQueryAsync(GFramework.Core.Abstractions.Cqrs.Query.IQuery query,
+ public async ValueTask SendQueryAsync(Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(query);
@@ -327,7 +322,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的命令对象
/// 取消令牌,用于取消操作
/// 包含命令执行结果的ValueTask
- public async ValueTask SendCommandAsync(GFramework.Core.Abstractions.Cqrs.Command.ICommand command,
+ public async ValueTask SendCommandAsync(Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(command);
@@ -366,7 +361,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 命令响应类型
/// 要发送的命令对象
/// 命令执行结果
- public TResponse SendCommand(GFramework.Core.Abstractions.Cqrs.Command.ICommand command)
+ public TResponse SendCommand(Abstractions.Cqrs.Command.ICommand command)
{
return SendCommandAsync(command).AsTask().GetAwaiter().GetResult();
}
@@ -388,7 +383,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 命令执行结果类型
/// 要发送的命令
/// 命令执行结果
- public TResult SendCommand(Abstractions.Command.ICommand command)
+ public TResult SendCommand(ICommand command)
{
ArgumentNullException.ThrowIfNull(command);
var commandBus = GetOrCache();
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
index 69f6794d..f1950cf7 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
@@ -14,34 +14,70 @@ namespace GFramework.Core.Cqrs.Internal;
///
internal sealed class CqrsDispatcher(
IIocContainer container,
- IArchitectureContext context,
- ILogger logger)
+ ILogger logger) : ICqrsRuntime
{
- private delegate ValueTask
/// 处理器实例。
- private void PrepareHandler(object handler)
+ /// 当前架构上下文。
+ private static void PrepareHandler(object handler, IArchitectureContext context)
{
if (handler is IContextAware contextAware)
contextAware.SetContext(context);
@@ -260,4 +266,18 @@ internal sealed class CqrsDispatcher(
var typedRequest = (TRequest)request;
return typedHandler.Handle(typedRequest, cancellationToken);
}
+
+ private delegate ValueTask RequestInvoker(object handler, object request,
+ CancellationToken cancellationToken);
+
+ private delegate ValueTask RequestPipelineInvoker(
+ object handler,
+ IReadOnlyList behaviors,
+ object request,
+ CancellationToken cancellationToken);
+
+ private delegate ValueTask NotificationInvoker(object handler, object notification,
+ CancellationToken cancellationToken);
+
+ private delegate object StreamInvoker(object handler, object request, CancellationToken cancellationToken);
}
diff --git a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
new file mode 100644
index 00000000..5d59f8a6
--- /dev/null
+++ b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
+
+namespace GFramework.Core.Cqrs.Internal;
+
+///
+/// 默认的 CQRS 处理器注册器实现。
+/// 该适配器把容器公开的 handler 接入入口转发到现有的注册流水线,
+/// 使容器主路径只依赖 抽象。
+///
+internal sealed class DefaultCqrsHandlerRegistrar(IIocContainer container, ILogger logger) : ICqrsHandlerRegistrar
+{
+ private readonly IIocContainer _container = container ?? throw new ArgumentNullException(nameof(container));
+ private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ ///
+ /// 按当前 runtime 约定扫描并注册处理器程序集。
+ ///
+ /// 要接入的程序集集合。
+ public void RegisterHandlers(IEnumerable assemblies)
+ {
+ ArgumentNullException.ThrowIfNull(assemblies);
+ CqrsHandlerRegistrar.RegisterHandlers(_container, assemblies, _logger);
+ }
+}
diff --git a/GFramework.Core/Ioc/MicrosoftDiContainer.cs b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
index 712a41c1..c094647d 100644
--- a/GFramework.Core/Ioc/MicrosoftDiContainer.cs
+++ b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
@@ -1,11 +1,9 @@
using System.ComponentModel;
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
-using GFramework.Core.Cqrs.Internal;
using GFramework.Core.Logging;
using GFramework.Core.Rule;
@@ -424,7 +422,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
continue;
}
- CqrsHandlerRegistrar.RegisterHandlers(this, [assembly], _logger);
+ ResolveCqrsHandlerRegistrar().RegisterHandlers([assembly]);
_registeredCqrsHandlerAssemblyKeys.Add(assemblyKey);
}
}
@@ -456,6 +454,27 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
#region Get
+ ///
+ /// 获取当前容器中已注册的 CQRS 处理器注册器。
+ /// 该方法仅供容器内部在注册阶段使用,因此直接读取服务描述符中的实例绑定,
+ /// 避免在容器未冻结前依赖完整的服务提供者构建流程。
+ ///
+ /// 已注册的 CQRS 处理器注册器实例。
+ /// 未找到可用的 CQRS 处理器注册器实例时抛出。
+ private ICqrsHandlerRegistrar ResolveCqrsHandlerRegistrar()
+ {
+ var descriptor = GetServicesUnsafe.LastOrDefault(static service =>
+ service.ServiceType == typeof(ICqrsHandlerRegistrar));
+
+ if (descriptor?.ImplementationInstance is ICqrsHandlerRegistrar registrar)
+ return registrar;
+
+ const string errorMessage =
+ "ICqrsHandlerRegistrar not registered. Ensure the CQRS runtime module has been installed before registering handlers.";
+ _logger.Error(errorMessage);
+ throw new InvalidOperationException(errorMessage);
+ }
+
///
/// 获取指定泛型类型的服务实例
/// 返回第一个匹配的注册实例,如果不存在则返回null
diff --git a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
new file mode 100644
index 00000000..915e07e2
--- /dev/null
+++ b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
@@ -0,0 +1,61 @@
+using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Cqrs.Internal;
+using GFramework.Core.Logging;
+
+namespace GFramework.Core.Services.Modules;
+
+///
+/// CQRS runtime 模块,用于把默认请求分发器与处理器注册器接入架构容器。
+/// 该模块在架构初始化早期完成注册,保证用户初始化阶段即可使用 CQRS 入口与 handler 自动接入能力。
+///
+public sealed class CqrsRuntimeModule : IServiceModule
+{
+ ///
+ /// 获取模块名称。
+ ///
+ public string ModuleName => nameof(CqrsRuntimeModule);
+
+ ///
+ /// 获取模块优先级。
+ /// CQRS runtime 需要先于架构默认 handler 扫描路径可用,因此放在基础总线模块之后、用户初始化之前注册。
+ ///
+ public int Priority => 15;
+
+ ///
+ /// 获取模块启用状态,默认启用。
+ ///
+ public bool IsEnabled => true;
+
+ ///
+ /// 注册默认 CQRS runtime seam 实现。
+ ///
+ /// 目标依赖注入容器。
+ public void Register(IIocContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsDispatcher));
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(DefaultCqrsHandlerRegistrar));
+
+ container.Register(new CqrsDispatcher(container, dispatcherLogger));
+ container.Register(new DefaultCqrsHandlerRegistrar(container, registrarLogger));
+ }
+
+ ///
+ /// 初始化模块。
+ ///
+ public void Initialize()
+ {
+ }
+
+ ///
+ /// 异步销毁模块。
+ ///
+ /// 已完成的值任务。
+ public ValueTask DestroyAsync()
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/GFramework.Core/Services/ServiceModuleManager.cs b/GFramework.Core/Services/ServiceModuleManager.cs
index d07c128b..a3965f4d 100644
--- a/GFramework.Core/Services/ServiceModuleManager.cs
+++ b/GFramework.Core/Services/ServiceModuleManager.cs
@@ -42,7 +42,7 @@ public sealed class ServiceModuleManager : IServiceModuleManager
///
/// 注册内置服务模块,并根据优先级排序后完成服务注册。
- /// 内置模块包括事件总线、命令执行器、查询执行器等核心模块。
+ /// 内置模块包括事件总线、命令执行器、CQRS runtime、查询执行器等核心模块。
/// 同时注册通过 ArchitectureModuleRegistry 自动注册的外部模块。
///
/// IoC容器实例,用于模块服务注册。
@@ -57,6 +57,7 @@ public sealed class ServiceModuleManager : IServiceModuleManager
// 注册内置模块
RegisterModule(new EventBusModule());
RegisterModule(new CommandExecutorModule());
+ RegisterModule(new CqrsRuntimeModule());
RegisterModule(new QueryExecutorModule());
RegisterModule(new AsyncQueryExecutorModule());
@@ -148,4 +149,4 @@ public sealed class ServiceModuleManager : IServiceModuleManager
_builtInModulesRegistered = false;
_logger.Info("All service modules destroyed");
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
similarity index 84%
rename from GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
index 5ec607e5..a00d67e6 100644
--- a/GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
@@ -4,4 +4,4 @@
/// 命令输入接口,定义命令模式中输入数据的契约
/// 该接口作为标记接口使用,不包含任何成员定义
///
-public interface ICommandInput : IInput;
\ No newline at end of file
+public interface ICommandInput : IInput;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
new file mode 100644
index 00000000..0257b3ec
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
@@ -0,0 +1,17 @@
+using System.Reflection;
+
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 定义 CQRS 处理器程序集接入的 runtime seam。
+/// 该抽象负责承接“生成注册器优先、反射扫描回退”的处理器注册流程,
+/// 让容器与架构启动链不再直接依赖固定的注册实现类型。
+///
+public interface ICqrsHandlerRegistrar
+{
+ ///
+ /// 扫描并注册指定程序集集合中的 CQRS 处理器。
+ ///
+ /// 要接入的程序集集合。
+ void RegisterHandlers(IEnumerable assemblies);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
similarity index 96%
rename from GFramework.Core.Abstractions/Cqrs/IInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
index dfed5012..ddf00622 100644
--- a/GFramework.Core.Abstractions/Cqrs/IInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs;
/// 表示输入数据的标记接口。
/// 该接口用于标识各类CQRS模式中的输入参数类型。
///
-public interface IInput;
\ No newline at end of file
+public interface IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/INotification.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/INotification.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs b/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IRequest.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs b/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
similarity index 94%
rename from GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
index 8b791839..236b30ff 100644
--- a/GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs.Notification;
/// 表示通知输入数据的标记接口。
/// 该接口继承自 IInput,用于标识CQRS模式中通知类型的输入参数。
///
-public interface INotificationInput : IInput;
\ No newline at end of file
+public interface INotificationInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
similarity index 79%
rename from GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
index c505c4ff..7e0a5b4f 100644
--- a/GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
@@ -3,4 +3,4 @@
///
/// 查询输入接口,定义了查询操作的输入规范
///
-public interface IQueryInput : IInput;
\ No newline at end of file
+public interface IQueryInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
similarity index 95%
rename from GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
index 0a0b1591..7b7ff83c 100644
--- a/GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs.Request;
/// 表示请求输入数据的标记接口。
/// 该接口继承自 IInput,用于标识CQRS模式中请求类型的输入参数。
///
-public interface IRequestInput : IInput;
\ No newline at end of file
+public interface IRequestInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Unit.cs b/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Unit.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
diff --git a/GFramework.Cqrs.Abstractions/Directory.Build.props b/GFramework.Cqrs.Abstractions/Directory.Build.props
new file mode 100644
index 00000000..8febdc42
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Directory.Build.props
@@ -0,0 +1,18 @@
+
+
+ netstandard2.1
+ true
+ true
+ preview
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
diff --git a/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj b/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj
new file mode 100644
index 00000000..8e07fd2c
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj
@@ -0,0 +1,11 @@
+
+
+
+ GeWuYou.$(AssemblyName)
+ true
+ T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute
+ enable
+ true
+
+
+
diff --git a/GFramework.Cqrs.Abstractions/GlobalUsings.cs b/GFramework.Cqrs.Abstractions/GlobalUsings.cs
new file mode 100644
index 00000000..5cd04a4e
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/GlobalUsings.cs
@@ -0,0 +1,3 @@
+global using System.Collections.Generic;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/GFramework.Cqrs/GFramework.Cqrs.csproj b/GFramework.Cqrs/GFramework.Cqrs.csproj
new file mode 100644
index 00000000..9f002283
--- /dev/null
+++ b/GFramework.Cqrs/GFramework.Cqrs.csproj
@@ -0,0 +1,16 @@
+
+
+
+ GeWuYou.$(AssemblyName)
+ net8.0;net9.0;net10.0
+ disable
+ enable
+ true
+ true
+
+
+
+
+
+
+
diff --git a/GFramework.sln b/GFramework.sln
index 6d4eb45f..67d53c69 100644
--- a/GFramework.sln
+++ b/GFramework.sln
@@ -38,6 +38,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGene
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.Tests", "GFramework.Godot.Tests\GFramework.Godot.Tests.csproj", "{576119E2-13D0-4ACF-A012-D01C320E8BF3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Abstractions", "GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj", "{69C06523-98AA-49DE-95D4-4BF203716DD2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs", "GFramework.Cqrs\GFramework.Cqrs.csproj", "{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -276,6 +280,30 @@ Global
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x64.Build.0 = Release|Any CPU
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.ActiveCfg = Release|Any CPU
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x64.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x86.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x64.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x64.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x86.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x86.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x64.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x86.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x64.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x64.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE