docs(core): 添加 CQRS 和核心框架文档

- 新增 CQRS 详细文档,介绍命令查询职责分离模式
- 添加核心框架概述文档,包含架构图和快速开始指南
- 详细介绍五层架构设计和组件联动机制
- 提供完整的最佳实践和设计理念说明
- 添加架构生命周期管理和模块化设计说明
This commit is contained in:
GeWuYou 2026-04-14 22:30:59 +08:00
parent 156fd4df2f
commit 5c112f8545
16 changed files with 128 additions and 162 deletions

View File

@ -73,7 +73,8 @@ Architecture 负责统一生命周期编排,核心阶段包括:
### CQRS ### CQRS
命令与查询分离支持同步与异步执行。Mediator 模式通过源码生成器集成,以减少模板代码并保持调用路径清晰。 命令与查询分离,支持同步与异步执行。当前版本内建自有 CQRS runtime、行为管道和 handler 自动注册;公开 API 里仍保留少量历史
`Mediator` 命名以兼容旧调用点。
### EventBus ### EventBus

View File

@ -2,7 +2,6 @@ using GFramework.Core.Abstractions.Lifecycle;
using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility; using GFramework.Core.Abstractions.Utility;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;
@ -73,8 +72,8 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia
void RegisterUtility<T>(Action<T>? onCreated = null) where T : class, IUtility; void RegisterUtility<T>(Action<T>? onCreated = null) where T : class, IUtility;
/// <summary> /// <summary>
/// 注册中介行为管道 /// 注册 CQRS 请求管道行为。
/// 用于配置Mediator框架的行为拦截和处理逻辑。 /// 历史方法名保留了 Mediator 前缀,但当前用于配置框架内建 CQRS runtime 的行为拦截和处理逻辑。
/// 既支持实现 <c>IPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型, /// 既支持实现 <c>IPipelineBehavior&lt;,&gt;</c> 的开放泛型行为类型,
/// 也支持绑定到单一请求/响应对的封闭行为类型。 /// 也支持绑定到单一请求/响应对的封闭行为类型。
/// </summary> /// </summary>
@ -101,4 +100,4 @@ public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitia
/// </summary> /// </summary>
/// <returns>表示异步等待操作的任务</returns> /// <returns>表示异步等待操作的任务</returns>
Task WaitUntilReadyAsync(); Task WaitUntilReadyAsync();
} }

View File

@ -26,6 +26,6 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Mediator.Abstractions" Version="3.0.2"/> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.Ioc; namespace GFramework.Core.Abstractions.Ioc;
@ -90,8 +89,8 @@ public interface IIocContainer : IContextAware
void RegisterFactory<TService>(Func<IServiceProvider, TService> factory) where TService : class; void RegisterFactory<TService>(Func<IServiceProvider, TService> factory) where TService : class;
/// <summary> /// <summary>
/// 注册中介行为管道 /// 注册 CQRS 请求管道行为。
/// 用于配置Mediator框架的行为拦截和处理逻辑 /// 历史方法名保留了 Mediator 前缀,但当前用于配置框架内建 CQRS runtime 的行为拦截和处理逻辑。
/// </summary> /// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam> /// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
void RegisterMediatorBehavior<TBehavior>() void RegisterMediatorBehavior<TBehavior>()
@ -227,4 +226,4 @@ public interface IIocContainer : IContextAware
IServiceScope CreateScope(); IServiceScope CreateScope();
#endregion #endregion
} }

View File

@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command; using GFramework.Core.Abstractions.Command;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Environment; using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
@ -13,7 +14,6 @@ using GFramework.Core.Environment;
using GFramework.Core.Events; using GFramework.Core.Events;
using GFramework.Core.Ioc; using GFramework.Core.Ioc;
using GFramework.Core.Query; using GFramework.Core.Query;
using Mediator;
using ICommand = GFramework.Core.Abstractions.Command.ICommand; using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Tests.Architectures; namespace GFramework.Core.Tests.Architectures;
@ -34,6 +34,10 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture] [TestFixture]
public class ArchitectureServicesTests public class ArchitectureServicesTests
{ {
private TestArchitectureContextV3? _context;
private ArchitectureServices? _services;
[SetUp] [SetUp]
public void SetUp() public void SetUp()
{ {
@ -41,9 +45,6 @@ public class ArchitectureServicesTests
_context = new TestArchitectureContextV3(); _context = new TestArchitectureContextV3();
} }
private ArchitectureServices? _services;
private TestArchitectureContextV3? _context;
private void RegisterBuiltInServices() private void RegisterBuiltInServices()
{ {
_services!.ModuleManager.RegisterBuiltInModules(_services.Container); _services!.ModuleManager.RegisterBuiltInModules(_services.Container);
@ -347,61 +348,59 @@ public class TestArchitectureContextV3 : IArchitectureContext
{ {
} }
public ValueTask<TResponse> SendRequestAsync<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> request, public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendRequest<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> request) public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendCommandAsync<TResponse>( public ValueTask<TResponse> SendCommandAsync<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command,
global::GFramework.Core.Abstractions.Cqrs.Command.ICommand<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendCommand<TResponse>(global::GFramework.Core.Abstractions.Cqrs.Command.ICommand<TResponse> command) public TResponse SendCommand<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendQueryAsync<TResponse>( public ValueTask<TResponse> SendQueryAsync<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query,
global::GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse> query,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendQuery<TResponse>(global::GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse> query) public TResponse SendQuery<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask PublishAsync<TNotification>(TNotification notification, public ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) where TNotification : global::GFramework.Core.Abstractions.Cqrs.INotification CancellationToken cancellationToken = default) where TNotification : INotification
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IAsyncEnumerable<TResponse> CreateStream<TResponse>( public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
global::GFramework.Core.Abstractions.Cqrs.IStreamRequest<TResponse> request, IStreamRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default) public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : global::GFramework.Core.Abstractions.Cqrs.IRequest<global::GFramework.Core.Abstractions.Cqrs.Unit> where TCommand : IRequest<Unit>
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendAsync<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> command, public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command; using GFramework.Core.Abstractions.Command;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Environment; using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events; using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
@ -13,7 +14,6 @@ using GFramework.Core.Environment;
using GFramework.Core.Events; using GFramework.Core.Events;
using GFramework.Core.Ioc; using GFramework.Core.Ioc;
using GFramework.Core.Query; using GFramework.Core.Query;
using Mediator;
using ICommand = GFramework.Core.Abstractions.Command.ICommand; using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Tests.Architectures; namespace GFramework.Core.Tests.Architectures;
@ -394,61 +394,59 @@ public class TestArchitectureContext : IArchitectureContext
{ {
} }
public ValueTask<TResponse> SendRequestAsync<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> request, public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendRequest<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> request) public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendCommandAsync<TResponse>( public ValueTask<TResponse> SendCommandAsync<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command,
global::GFramework.Core.Abstractions.Cqrs.Command.ICommand<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendCommand<TResponse>(global::GFramework.Core.Abstractions.Cqrs.Command.ICommand<TResponse> command) public TResponse SendCommand<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendQueryAsync<TResponse>( public ValueTask<TResponse> SendQueryAsync<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query,
global::GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse> query,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public TResponse SendQuery<TResponse>(global::GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse> query) public TResponse SendQuery<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask PublishAsync<TNotification>(TNotification notification, public ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) where TNotification : global::GFramework.Core.Abstractions.Cqrs.INotification CancellationToken cancellationToken = default) where TNotification : INotification
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IAsyncEnumerable<TResponse> CreateStream<TResponse>( public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
global::GFramework.Core.Abstractions.Cqrs.IStreamRequest<TResponse> request, IStreamRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default) public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : global::GFramework.Core.Abstractions.Cqrs.IRequest<global::GFramework.Core.Abstractions.Cqrs.Unit> where TCommand : IRequest<Unit>
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public ValueTask<TResponse> SendAsync<TResponse>(global::GFramework.Core.Abstractions.Cqrs.IRequest<TResponse> command, public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -468,7 +466,7 @@ public class TestArchitectureContext : IArchitectureContext
/// <typeparam name="TResult">返回值类型</typeparam> /// <typeparam name="TResult">返回值类型</typeparam>
/// <param name="command">命令对象</param> /// <param name="command">命令对象</param>
/// <returns>命令执行结果</returns> /// <returns>命令执行结果</returns>
public TResult SendCommand<TResult>(Abstractions.Command.ICommand<TResult> command) public TResult SendCommand<TResult>(ICommand<TResult> command)
{ {
return default!; return default!;
} }
@ -489,7 +487,7 @@ public class TestArchitectureContext : IArchitectureContext
/// <typeparam name="TResult">查询结果类型</typeparam> /// <typeparam name="TResult">查询结果类型</typeparam>
/// <param name="query">查询对象</param> /// <param name="query">查询对象</param>
/// <returns>查询结果</returns> /// <returns>查询结果</returns>
public TResult SendQuery<TResult>(Abstractions.Query.IQuery<TResult> query) public TResult SendQuery<TResult>(IQuery<TResult> query)
{ {
return default!; return default!;
} }

View File

@ -13,68 +13,32 @@
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Coroutine; using GFramework.Core.Abstractions.Coroutine;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Coroutine.Extensions; using GFramework.Core.Coroutine.Extensions;
using Mediator;
using Moq;
namespace GFramework.Core.Tests.Coroutine; namespace GFramework.Core.Tests.Coroutine;
/// <summary> /// <summary>
/// MediatorCoroutineExtensions的单元测试类 /// <see cref="MediatorCoroutineExtensions" /> 的单元测试类
/// 测试Mediator模式与协程集成的扩展方法 /// 验证历史命名的协程扩展已切到框架内建 CQRS runtime
/// 注意:由于 Mediator 使用源生成器,本测试类主要验证接口和参数验证 /// 并确保协程对命令调度异常的传播行为保持稳定。
/// </summary> /// </summary>
[TestFixture] [TestFixture]
public class MediatorCoroutineExtensionsTests public class MediatorCoroutineExtensionsTests
{ {
/// <summary>
/// 测试用的简单命令类
/// </summary>
private class TestCommand : IRequest<Unit>
{
public string Data { get; set; } = string.Empty;
}
/// <summary>
/// 测试用的简单事件类
/// </summary>
private class TestEvent
{
public string Data { get; set; } = string.Empty;
}
/// <summary>
/// 上下文感知基类的模拟实现
/// </summary>
private class TestContextAware : IContextAware
{
public readonly Mock<IArchitectureContext> _mockContext = new();
public IArchitectureContext GetContext()
{
return _mockContext.Object;
}
public void SetContext(IArchitectureContext context)
{
}
}
/// <summary> /// <summary>
/// 验证SendCommandCoroutine应该返回IEnumerator<IYieldInstruction> /// 验证SendCommandCoroutine应该返回IEnumerator<IYieldInstruction>
/// </summary> /// </summary>
[Test] [Test]
public void SendCommandCoroutine_Should_Return_IEnumerator_Of_YieldInstruction() public void SendCommandCoroutine_Should_Return_IEnumerator_Of_YieldInstruction()
{ {
var command = new TestCommand { Data = "Test" }; var command = new TestCommand("Test");
var contextAware = new TestContextAware(); var contextAware = new TestContextAware();
// 创建 mediator 模拟 contextAware.MockContext
var mediatorMock = new Mock<IMediator>(); .Setup(ctx => ctx.SendAsync(command, It.IsAny<CancellationToken>()))
contextAware._mockContext .Returns(ValueTask.CompletedTask);
.Setup(ctx => ctx.GetService<IMediator>())
.Returns(mediatorMock.Object);
var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command); var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command);
@ -82,23 +46,45 @@ public class MediatorCoroutineExtensionsTests
} }
/// <summary> /// <summary>
/// 验证SendCommandCoroutine应该在mediator为null时抛出NullReferenceException /// 验证 SendCommandCoroutine 在底层命令调度失败时会重新抛出原始异常。
/// </summary> /// </summary>
[Test] [Test]
public void SendCommandCoroutine_Should_Throw_When_Mediator_Null() public void SendCommandCoroutine_Should_Rethrow_Inner_Exception_When_Command_Fails()
{ {
var command = new TestCommand { Data = "Test" }; var command = new TestCommand("Test");
var contextAware = new TestContextAware(); var contextAware = new TestContextAware();
var expectedException = new InvalidOperationException("Command failed.");
// 设置上下文服务以返回null mediator contextAware.MockContext
contextAware._mockContext .Setup(ctx => ctx.SendAsync(command, It.IsAny<CancellationToken>()))
.Setup(ctx => ctx.GetService<IMediator>()) .Returns(new ValueTask(Task.FromException(expectedException)));
.Returns((IMediator?)null);
// 创建协程
var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command); var coroutine = MediatorCoroutineExtensions.SendCommandCoroutine(contextAware, command);
// 调用 MoveNext 时应该抛出 NullReferenceException Assert.That(coroutine.MoveNext(), Is.True);
Assert.Throws<NullReferenceException>(() => coroutine.MoveNext()); var exception = Assert.Throws<InvalidOperationException>(() => coroutine.MoveNext());
Assert.That(exception, Is.SameAs(expectedException));
} }
}
/// <summary>
/// 测试用的简单命令类
/// </summary>
private sealed record TestCommand(string Data) : IRequest<Unit>;
/// <summary>
/// 上下文感知基类的模拟实现
/// </summary>
private sealed class TestContextAware : IContextAware
{
public Mock<IArchitectureContext> MockContext { get; } = new();
public IArchitectureContext GetContext()
{
return MockContext.Object;
}
public void SetContext(IArchitectureContext context)
{
}
}
}

View File

@ -10,11 +10,6 @@
<WarningLevel>0</WarningLevel> <WarningLevel>0</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Mediator.Abstractions" Version="3.0.2"/>
<PackageReference Include="Mediator.SourceGenerator" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.4.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.4.0"/>
<PackageReference Include="Moq" Version="4.20.72"/> <PackageReference Include="Moq" Version="4.20.72"/>
<PackageReference Include="NUnit" Version="4.5.1"/> <PackageReference Include="NUnit" Version="4.5.1"/>

View File

@ -7,7 +7,6 @@ using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility; using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Environment; using GFramework.Core.Environment;
using GFramework.Core.Logging; using GFramework.Core.Logging;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Architectures; namespace GFramework.Core.Architectures;
@ -146,8 +145,8 @@ public abstract class Architecture : IArchitecture
#region Module Management #region Module Management
/// <summary> /// <summary>
/// 注册中介行为管道 /// 注册 CQRS 请求管道行为。
/// 用于配置Mediator框架的行为拦截和处理逻辑。 /// 历史方法名保留了 Mediator 前缀,但当前用于配置框架内建 CQRS runtime 的行为拦截和处理逻辑。
/// 可以传入开放泛型行为类型,也可以传入绑定到特定请求的封闭行为类型。 /// 可以传入开放泛型行为类型,也可以传入绑定到特定请求的封闭行为类型。
/// </summary> /// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam> /// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
@ -328,4 +327,4 @@ public abstract class Architecture : IArchitecture
} }
#endregion #endregion
} }

View File

@ -2,7 +2,6 @@ using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Environment; using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Cqrs.Internal; using GFramework.Core.Cqrs.Internal;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Architectures; namespace GFramework.Core.Architectures;
@ -23,7 +22,7 @@ internal sealed class ArchitectureBootstrapper(
/// 因为用户初始化逻辑通常会立即访问事件总线、查询执行器或环境对象。 /// 因为用户初始化逻辑通常会立即访问事件总线、查询执行器或环境对象。
/// </summary> /// </summary>
/// <param name="existingContext">调用方已经提供的上下文;如果为空则创建默认上下文。</param> /// <param name="existingContext">调用方已经提供的上下文;如果为空则创建默认上下文。</param>
/// <param name="configurator">可选的容器配置委托,用于接入 Mediator 等扩展服务。</param> /// <param name="configurator">可选的容器配置委托,用于接入额外服务或覆盖默认依赖绑定。</param>
/// <param name="asyncMode">是否以异步模式初始化服务模块。</param> /// <param name="asyncMode">是否以异步模式初始化服务模块。</param>
/// <returns>已绑定到当前架构类型的架构上下文。</returns> /// <returns>已绑定到当前架构类型的架构上下文。</returns>
public async Task<IArchitectureContext> PrepareForInitializationAsync( public async Task<IArchitectureContext> PrepareForInitializationAsync(

View File

@ -5,7 +5,7 @@ namespace GFramework.Core.Architectures;
/// <summary> /// <summary>
/// 架构模块管理器 /// 架构模块管理器
/// 负责管理架构模块的安装和中介行为注册 /// 负责管理架构模块的安装和 CQRS 行为注册
/// </summary> /// </summary>
internal sealed class ArchitectureModules( internal sealed class ArchitectureModules(
IArchitecture architecture, IArchitecture architecture,
@ -13,14 +13,14 @@ internal sealed class ArchitectureModules(
ILogger logger) ILogger logger)
{ {
/// <summary> /// <summary>
/// 注册中介行为管道 /// 注册 CQRS 请求管道行为。
/// 用于配置Mediator框架的行为拦截和处理逻辑。 /// 历史方法名保留了 Mediator 前缀,但当前用于配置框架内建 CQRS runtime 的行为拦截和处理逻辑。
/// 支持开放泛型行为类型和针对单一请求的封闭行为类型。 /// 支持开放泛型行为类型和针对单一请求的封闭行为类型。
/// </summary> /// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam> /// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{ {
logger.Debug($"Registering mediator behavior: {typeof(TBehavior).Name}"); logger.Debug($"Registering CQRS pipeline behavior: {typeof(TBehavior).Name}");
services.Container.RegisterMediatorBehavior<TBehavior>(); services.Container.RegisterMediatorBehavior<TBehavior>();
} }
@ -37,4 +37,4 @@ internal sealed class ArchitectureModules(
logger.Info($"Module installed: {name}"); logger.Info($"Module installed: {name}");
return module; return module;
} }
} }

View File

@ -12,21 +12,22 @@
// limitations under the License. // limitations under the License.
using GFramework.Core.Abstractions.Coroutine; using GFramework.Core.Abstractions.Coroutine;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using Mediator;
namespace GFramework.Core.Coroutine.Extensions; namespace GFramework.Core.Coroutine.Extensions;
/// <summary> /// <summary>
/// 提供Mediator模式与协程集成的扩展方法。 /// 提供 CQRS 命令与协程集成的扩展方法。
/// 包含发送命令和等待事件的协程实现。 /// 历史命名保留了 Mediator 前缀,但当前实现直接走 <see cref="IContextAware.GetContext" /> 返回的
/// <see cref="GFramework.Core.Abstractions.Architectures.IArchitectureContext" /> CQRS 入口,不再依赖外部 Mediator 服务。
/// </summary> /// </summary>
public static class MediatorCoroutineExtensions public static class MediatorCoroutineExtensions
{ {
/// <summary> /// <summary>
/// 以协程方式发送命令并处理可能的异常。 /// 以协程方式发送无返回值 CQRS 命令并处理可能的异常。
/// </summary> /// </summary>
/// <typeparam name="TCommand">命令的类型</typeparam> /// <typeparam name="TCommand">命令的类型</typeparam>
/// <param name="contextAware">上下文感知对象,用于获取服务</param> /// <param name="contextAware">上下文感知对象,用于获取服务</param>
/// <param name="command">要发送的命令对象</param> /// <param name="command">要发送的命令对象</param>
/// <param name="onError">发生异常时的回调处理函数</param> /// <param name="onError">发生异常时的回调处理函数</param>
@ -35,13 +36,12 @@ public static class MediatorCoroutineExtensions
this IContextAware contextAware, this IContextAware contextAware,
TCommand command, TCommand command,
Action<Exception>? onError = null) Action<Exception>? onError = null)
where TCommand : notnull where TCommand : IRequest<Unit>
{ {
var mediator = contextAware ArgumentNullException.ThrowIfNull(contextAware);
.GetContext() ArgumentNullException.ThrowIfNull(command);
.GetService<IMediator>()!;
var task = mediator.Send(command).AsTask(); var task = contextAware.GetContext().SendAsync(command).AsTask();
yield return task.AsCoroutineInstruction(); yield return task.AsCoroutineInstruction();
@ -51,4 +51,4 @@ public static class MediatorCoroutineExtensions
else else
throw task.Exception!.InnerException ?? task.Exception; throw task.Exception!.InnerException ?? task.Exception;
} }
} }

View File

@ -7,7 +7,7 @@ namespace GFramework.Core.Cqrs.Internal;
/// <summary> /// <summary>
/// 在架构初始化期间扫描并注册 CQRS 处理器。 /// 在架构初始化期间扫描并注册 CQRS 处理器。
/// 首批实现采用运行时反射扫描,优先满足“无需 AddMediator 即可工作”的迁移目标。 /// 首批实现采用运行时反射扫描,优先满足“无需额外注册步骤即可工作”的迁移目标。
/// </summary> /// </summary>
internal static class CqrsHandlerRegistrar internal static class CqrsHandlerRegistrar
{ {

View File

@ -5,7 +5,6 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Logging; using GFramework.Core.Logging;
using GFramework.Core.Rule; using GFramework.Core.Rule;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Ioc; namespace GFramework.Core.Ioc;
@ -310,8 +309,8 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
/// <summary> /// <summary>
/// 注册中介行为管道 /// 注册 CQRS 请求管道行为。
/// 用于配置Mediator框架的行为拦截和处理逻辑。 /// 历史方法名保留了 Mediator 前缀,但当前用于配置框架内建 CQRS runtime 的行为拦截和处理逻辑。
/// 同时支持开放泛型行为类型和已闭合的具体行为类型, /// 同时支持开放泛型行为类型和已闭合的具体行为类型,
/// 以兼容通用行为和针对单一请求的专用行为两种注册方式。 /// 以兼容通用行为和针对单一请求的专用行为两种注册方式。
/// </summary> /// </summary>
@ -351,7 +350,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
} }
} }
_logger.Debug($"Mediator behavior registered: {behaviorType.Name}"); _logger.Debug($"CQRS pipeline behavior registered: {behaviorType.Name}");
} }
finally finally
{ {

View File

@ -1,21 +1,21 @@
--- ---
title: CQRS 与 Mediator title: CQRS
description: CQRS 模式通过 Mediator 实现命令查询职责分离,提供清晰的业务逻辑组织方式 description: GFramework 内建 CQRS runtime用统一请求分发、通知发布和流式处理组织业务逻辑
--- ---
# CQRS 与 Mediator # CQRS
## 概述 ## 概述
CQRSCommand Query Responsibility Segregation命令查询职责分离是一种架构模式将数据的读取Query和修改Command操作分离。GFramework CQRSCommand Query Responsibility Segregation命令查询职责分离是一种架构模式将数据的读取Query和修改Command操作分离。GFramework
通过集成 Mediator 库实现了 CQRS 模式,提供了类型安全、解耦的业务逻辑处理方式。 当前内建自有 CQRS runtime通过统一的请求分发器、通知发布和流式请求管道提供类型安全、解耦的业务逻辑处理方式。
通过 CQRS你可以将复杂的业务逻辑拆分为独立的命令和查询处理器每个处理器只负责单一职责使代码更易于测试和维护。 通过 CQRS你可以将复杂的业务逻辑拆分为独立的命令和查询处理器每个处理器只负责单一职责使代码更易于测试和维护。
**主要特性** **主要特性**
- 命令查询职责分离 - 命令查询职责分离
- 基于 Mediator 模式的解耦设计 - 内建请求分发与解耦设计
- 支持管道行为Behaviors - 支持管道行为Behaviors
- 异步处理支持 - 异步处理支持
- 与架构系统深度集成 - 与架构系统深度集成
@ -72,7 +72,6 @@ public class GetPlayerQuery : QueryBase<GetPlayerInput, PlayerData>
```csharp ```csharp
using GFramework.Core.CQRS.Command; using GFramework.Core.CQRS.Command;
using Mediator;
// 命令处理器 // 命令处理器
public class CreatePlayerCommandHandler : AbstractCommandHandler<CreatePlayerCommand, int> public class CreatePlayerCommandHandler : AbstractCommandHandler<CreatePlayerCommand, int>
@ -92,19 +91,19 @@ public class CreatePlayerCommandHandler : AbstractCommandHandler<CreatePlayerCom
} }
``` ```
### Mediator中介者 ### Dispatcher请求分发器
Mediator 负责将命令/查询路由到对应的处理器: 架构上下文会负责将命令、查询和通知路由到对应的处理器:
```csharp ```csharp
// 通过 Mediator 发送命令 // 通过架构上下文发送命令
var command = new CreatePlayerCommand(new CreatePlayerInput var command = new CreatePlayerCommand(new CreatePlayerInput
{ {
Name = "Player1", Name = "Player1",
Level = 1 Level = 1
}); });
var playerId = await mediator.Send(command); var playerId = await this.SendAsync(command);
``` ```
## 基本用法 ## 基本用法
@ -148,15 +147,13 @@ public class SaveGameCommandHandler : AbstractCommandHandler<SaveGameCommand>
// 4. 发送命令 // 4. 发送命令
public async Task SaveGame() public async Task SaveGame()
{ {
var mediator = this.GetService<IMediator>();
var command = new SaveGameCommand(new SaveGameInput var command = new SaveGameCommand(new SaveGameInput
{ {
SlotId = 1, SlotId = 1,
Data = currentGameData Data = currentGameData
}); });
await mediator.Send(command); await this.SendAsync(command);
} }
``` ```
@ -195,21 +192,19 @@ public class GetHighScoresQueryHandler : AbstractQueryHandler<GetHighScoresQuery
// 4. 发送查询 // 4. 发送查询
public async Task<List<ScoreData>> GetHighScores() public async Task<List<ScoreData>> GetHighScores()
{ {
var mediator = this.GetService<IMediator>();
var query = new GetHighScoresQuery(new GetHighScoresInput var query = new GetHighScoresQuery(new GetHighScoresInput
{ {
Count = 10 Count = 10
}); });
var scores = await mediator.Send(query); var scores = await this.SendQueryAsync(query);
return scores; return scores;
} }
``` ```
### 注册处理器 ### 注册处理器
在架构中注册 Mediator 和处理器 在架构中注册 CQRS 行为并让处理器自动扫描注册
```csharp ```csharp
public class GameArchitecture : Architecture public class GameArchitecture : Architecture
@ -225,7 +220,7 @@ public class GameArchitecture : Architecture
} }
``` ```
`RegisterMediatorBehavior<TBehavior>()` 同时支持两种形式: `RegisterMediatorBehavior<TBehavior>()` 是保留的兼容名称,当前用于注册框架内建 CQRS pipeline支持两种形式:
- 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求 - 开放泛型行为,例如 `LoggingBehavior<,>`,用于匹配所有请求
- 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior` - 封闭行为类型,例如某个只服务于单一请求的 `SpecialBehavior`
@ -326,7 +321,7 @@ var notification = new PlayerLevelUpNotification(new PlayerLevelUpInput
NewLevel = 10 NewLevel = 10
}); });
await mediator.Publish(notification); await this.PublishAsync(notification);
``` ```
### Pipeline Behaviors管道行为 ### Pipeline Behaviors管道行为
@ -334,11 +329,11 @@ await mediator.Publish(notification);
Behaviors 可以在处理器执行前后添加横切关注点: Behaviors 可以在处理器执行前后添加横切关注点:
```csharp ```csharp
using Mediator; using GFramework.Core.Abstractions.Cqrs;
// 日志行为 // 日志行为
public class LoggingBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse> public class LoggingBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse>
where TMessage : IMessage where TMessage : IRequest<TResponse>
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
@ -358,7 +353,7 @@ public class LoggingBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage,
// 性能监控行为 // 性能监控行为
public class PerformanceBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse> public class PerformanceBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse>
where TMessage : IMessage where TMessage : IRequest<TResponse>
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
@ -390,7 +385,7 @@ RegisterMediatorBehavior<PerformanceBehavior<,>>();
```csharp ```csharp
public class ValidationBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse> public class ValidationBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage, TResponse>
where TMessage : IMessage where TMessage : IRequest<TResponse>
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
@ -441,7 +436,7 @@ public class GetAllPlayersStreamQueryHandler : AbstractStreamQueryHandler<GetAll
// 使用流式查询 // 使用流式查询
var query = new GetAllPlayersStreamQuery(); var query = new GetAllPlayersStreamQuery();
var stream = await mediator.CreateStream(query); var stream = this.CreateStream(query);
await foreach (var player in stream) await foreach (var player in stream)
{ {
@ -530,12 +525,12 @@ CalculateDamageRequest
**解答** **解答**
- **Notification**:通过 Mediator 发送,处理器在同一请求上下文中执行 - **Notification**:通过内建 CQRS runtime 发送,处理器在同一请求上下文中执行
- **Event**:通过 EventBus 发送,监听器异步执行 - **Event**:通过 EventBus 发送,监听器异步执行
```csharp ```csharp
// Notification: 同步处理 // Notification: 同步处理
await mediator.Publish(notification); // 等待所有处理器完成 await this.PublishAsync(notification); // 等待所有处理器完成
// Event: 异步处理 // Event: 异步处理
this.SendEvent(event); // 立即返回,监听器异步执行 this.SendEvent(event); // 立即返回,监听器异步执行
@ -569,15 +564,13 @@ public override async ValueTask<Result> Handle(...)
### 问题:处理器可以调用其他处理器吗? ### 问题:处理器可以调用其他处理器吗?
**解答** **解答**
可以,通过 Mediator 发送新的命令或查询: 可以,通过架构上下文继续发送新的命令或查询:
```csharp ```csharp
public override async ValueTask<Unit> Handle(...) public override async ValueTask<Unit> Handle(...)
{ {
var mediator = this.GetService<IMediator>();
// 调用其他命令 // 调用其他命令
await mediator.Send(new AnotherCommand(...)); await this.SendAsync(new AnotherCommand(...));
return Unit.Value; return Unit.Value;
} }

View File

@ -391,17 +391,17 @@ public class PlayerController : IController
#### 5. ArchitectureModules (模块管理器) #### 5. ArchitectureModules (模块管理器)
**职责**: 管理架构模块和中介行为 **职责**: 管理架构模块和 CQRS 管道行为
**核心功能**: **核心功能**:
- 模块安装 (IArchitectureModule) - 模块安装 (IArchitectureModule)
- 中介行为注册 (Mediator Behaviors) - CQRS 管道行为注册(历史 API 名称仍为 `RegisterMediatorBehavior`
**关键方法**: **关键方法**:
- `InstallModule()` - 安装模块 - `InstallModule()` - 安装模块
- `RegisterMediatorBehavior<T>()` - 注册中介行为 - `RegisterMediatorBehavior<T>()` - 注册 CQRS 管道行为
#### 设计优势 #### 设计优势
@ -672,4 +672,3 @@ public interface IController :
- 添加 `PhaseChanged` 事件,支持阶段监听 - 添加 `PhaseChanged` 事件,支持阶段监听
**向后兼容**: 所有公共 API 保持不变,现有代码无需修改。 **向后兼容**: 所有公共 API 保持不变,现有代码无需修改。