mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
docs(cqrs): 刷新 Cqrs 文档入口
- 更新 Cqrs family landing、API 参考与 source-generators 导航 - 新增 CQRS handler registry 专题页并补充 XML inventory - 补充 runtime 与 generator 内部类型 XML 注释 - 记录 RP-004 验证结果与后续恢复点
This commit is contained in:
parent
da0ae700a3
commit
a1dbed3c8d
@ -1144,6 +1144,13 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
string HandlerInterfaceDisplayName,
|
||||
string HandlerInterfaceLogName);
|
||||
|
||||
/// <summary>
|
||||
/// 标记某条 handler 注册语句在生成阶段采用的表达策略。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该枚举只服务于输出排序与代码分支选择,用来保证生成注册器在“直接注册”
|
||||
/// “反射实现类型查找”和“精确运行时类型解析”之间保持稳定顺序。
|
||||
/// </remarks>
|
||||
private enum OrderedRegistrationKind
|
||||
{
|
||||
Direct,
|
||||
@ -1151,6 +1158,14 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
PreciseReflected
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 描述生成注册器中某个运行时类型引用的构造方式。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 某些 handler 服务类型可以直接以 <c>typeof(...)</c> 输出,某些则需要在运行时补充
|
||||
/// 反射查找、数组/指针封装或泛型实参重建。该记录把这些差异收敛为统一的递归结构,
|
||||
/// 供源码输出阶段生成稳定的类型解析语句。
|
||||
/// </remarks>
|
||||
private sealed record RuntimeTypeReferenceSpec(
|
||||
string? TypeDisplayName,
|
||||
string? ReflectionTypeMetadataName,
|
||||
@ -1161,18 +1176,27 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
RuntimeTypeReferenceSpec? GenericTypeDefinitionReference,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec> GenericTypeArguments)
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个可直接通过 <c>typeof(...)</c> 表达的类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromDirectReference(string typeDisplayName)
|
||||
{
|
||||
return new RuntimeTypeReferenceSpec(typeDisplayName, null, null, null, 0, null, null,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个需要从当前消费端程序集反射解析的类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromReflectionLookup(string reflectionTypeMetadataName)
|
||||
{
|
||||
return new RuntimeTypeReferenceSpec(null, reflectionTypeMetadataName, null, null, 0, null, null,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个需要从被引用程序集反射解析的类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromExternalReflectionLookup(
|
||||
string reflectionAssemblyName,
|
||||
string reflectionTypeMetadataName)
|
||||
@ -1182,18 +1206,27 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个数组类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromArray(RuntimeTypeReferenceSpec elementTypeReference, int arrayRank)
|
||||
{
|
||||
return new RuntimeTypeReferenceSpec(null, null, null, elementTypeReference, arrayRank, null, null,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个指针类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromPointer(RuntimeTypeReferenceSpec pointedAtTypeReference)
|
||||
{
|
||||
return new RuntimeTypeReferenceSpec(null, null, null, null, 0, pointedAtTypeReference, null,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec>.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个封闭泛型类型引用。
|
||||
/// </summary>
|
||||
public static RuntimeTypeReferenceSpec FromConstructedGeneric(
|
||||
RuntimeTypeReferenceSpec genericTypeDefinitionReference,
|
||||
ImmutableArray<RuntimeTypeReferenceSpec> genericTypeArguments)
|
||||
|
||||
@ -651,32 +651,70 @@ internal static class CqrsHandlerRegistrar
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 描述某个程序集在生成注册器之后仍需运行时补扫的 handler 元数据。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该对象把“是否存在精确 fallback 类型列表”与“是否只能回退到整程序集扫描”收敛为同一份内部状态,
|
||||
/// 供注册流水线后续阶段统一判断。
|
||||
/// </remarks>
|
||||
private sealed class ReflectionFallbackMetadata(IReadOnlyList<Type> types)
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取需要通过运行时反射补充注册的 handler 类型集合。
|
||||
/// </summary>
|
||||
public IReadOnlyList<Type> Types { get; } = types ?? throw new ArgumentNullException(nameof(types));
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否持有精确的 fallback 类型清单。
|
||||
/// </summary>
|
||||
public bool HasExplicitTypes => Types.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 描述单个程序集在注册阶段提取到的 generated registry 与 reflection fallback 元数据。
|
||||
/// </summary>
|
||||
private sealed class AssemblyRegistrationMetadata(
|
||||
IReadOnlyList<Type> registryTypes,
|
||||
ReflectionFallbackMetadata? reflectionFallbackMetadata)
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取程序集上声明的 generated registry 类型集合。
|
||||
/// </summary>
|
||||
public IReadOnlyList<Type> RegistryTypes { get; } =
|
||||
registryTypes ?? throw new ArgumentNullException(nameof(registryTypes));
|
||||
|
||||
/// <summary>
|
||||
/// 获取该程序集是否还要求运行时补充 reflection fallback。
|
||||
/// </summary>
|
||||
public ReflectionFallbackMetadata? ReflectionFallbackMetadata { get; } = reflectionFallbackMetadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存 generated registry 激活所需的类型判定结果与工厂委托。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该缓存把“是否实现契约”“是否为抽象类型”“是否已构建激活委托”封装为不可变快照,
|
||||
/// 避免对同一 registry 类型重复执行反射分析。
|
||||
/// </remarks>
|
||||
private sealed class RegistryActivationMetadata(
|
||||
bool implementsRegistryContract,
|
||||
bool isAbstract,
|
||||
Func<ICqrsHandlerRegistry>? factory)
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取目标类型是否实现了 <see cref="ICqrsHandlerRegistry" />。
|
||||
/// </summary>
|
||||
public bool ImplementsRegistryContract { get; } = implementsRegistryContract;
|
||||
|
||||
/// <summary>
|
||||
/// 获取目标类型是否为抽象类型。
|
||||
/// </summary>
|
||||
public bool IsAbstract { get; } = isAbstract;
|
||||
|
||||
/// <summary>
|
||||
/// 获取可用于实例化 registry 的工厂委托。
|
||||
/// </summary>
|
||||
public Func<ICqrsHandlerRegistry>? Factory { get; } = factory;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-003`
|
||||
- 当前阶段:`Phase 3 - Cqrs Docs Refresh Preparation`
|
||||
- 恢复点编号:`DOCUMENTATION-FULL-COVERAGE-GOV-RP-004`
|
||||
- 当前阶段:`Phase 3 - Cqrs Docs Refresh`
|
||||
- 当前焦点:
|
||||
- 准备进入 `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` 波次
|
||||
- 收口 `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` 的 landing / generator topic / API 入口
|
||||
- 延续 `README / landing / API reference / XML inventory` 的同一治理模板
|
||||
- 继续把模块族 inventory 从“入口存在”推进到“可审计的 XML / README / landing 对照表”
|
||||
- 为下一波 `Game` family 审计保留统一的恢复模板与验证口径
|
||||
|
||||
## 当前状态摘要
|
||||
|
||||
@ -43,13 +43,17 @@
|
||||
- 重写 `docs/zh-CN/ecs/arch.md`,明确 `UseArch(...)` 需早于 `Initialize()` 的真实接入时机
|
||||
- 刷新 `GFramework.Ecs.Arch/README.md`,使运行时 README 与源码 / 测试一致
|
||||
- 为 `GFramework.Ecs.Arch.Abstractions/README.md` 与 `docs/zh-CN/abstractions/ecs-arch-abstractions.md` 补齐类型族级 XML inventory
|
||||
- 重写 `docs/zh-CN/core/cqrs.md`,将其收敛为 `Cqrs` family landing,并补齐运行时 / 契约层 / 生成器的 XML inventory
|
||||
- 新建 `docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`,为 `Cqrs.SourceGenerators` 补齐站内专题入口
|
||||
- 更新 `docs/zh-CN/source-generators/index.md`、`docs/zh-CN/api-reference/index.md` 与 VitePress sidebar,使 `Cqrs` family 的 generator 入口可导航
|
||||
- 为 `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 与 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 中缺失的内部类型补齐 XML 注释,使本轮轻量 inventory 达到声明级闭环
|
||||
|
||||
## Inventory(第一版)
|
||||
|
||||
| 模块族 | 当前状态 | 当前证据 | 下一动作 |
|
||||
| --- | --- | --- | --- |
|
||||
| `Core` / `Core.Abstractions` | `README / landing / 类型族级 XML inventory 已收口,成员级审计待补齐` | 根 README、模块 README、`docs/zh-CN/core/**`、`docs/zh-CN/abstractions/core-abstractions.md` 已对齐当前目录与类型族基线 | 进入巡检;如有新 API 变更,再追加成员级 XML 审计 |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `待重写` | README 已存在;站内入口目前分散在 `docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/**` | 进入下一波次,补 dedicated landing / API map 审计 |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `README / landing / generator topic / 类型族级 XML inventory 已收口,成员级审计待补齐` | `GFramework.Cqrs/README.md`、`GFramework.Cqrs.Abstractions/README.md`、`GFramework.Cqrs.SourceGenerators/README.md`、`docs/zh-CN/core/cqrs.md`、`docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`、`docs/zh-CN/api-reference/index.md` 已对齐当前源码与测试 | 转入巡检;下一波切到 `Game` family 的 XML / 教程链路审计 |
|
||||
| `Game` / `Game.Abstractions` / `Game.SourceGenerators` | `已验证` | 根 README、模块 README、`docs/zh-CN/game/**` 和 abstractions 页已存在 | 后续波次补 XML / 教程链路审计 |
|
||||
| `Godot` / `Godot.SourceGenerators` | `已验证` | 上一轮归档 topic 已完成核心 landing / topic / tutorial 校验 | 进入巡检周期,重点看回漂 |
|
||||
| `Ecs.Arch` / `Ecs.Arch.Abstractions` | `README / landing / abstractions / 类型族级 XML inventory 已收口,成员级审计待补齐` | `GFramework.Ecs.Arch/README.md`、`GFramework.Ecs.Arch.Abstractions/README.md`、`docs/zh-CN/ecs/**`、`docs/zh-CN/abstractions/ecs-arch-abstractions.md` 已对齐当前源码与测试 | 转入巡检;后续仅在运行时公共 API 变动时补成员级 XML 细审 |
|
||||
@ -70,8 +74,6 @@
|
||||
|
||||
## 当前风险
|
||||
|
||||
- `Cqrs` family 目前仍缺 dedicated landing 与统一 API / XML 阅读链路,站内入口散落在 `core` 与 `source-generators` 栏目
|
||||
- 缓解措施:下一恢复点直接进入 `Cqrs` 波次,按 `Core` / `Ecs` 已验证模板重写入口
|
||||
- 当前 `Core` / `Core.Abstractions` 只完成了类型族级 XML 基线,不等于成员级契约全审计
|
||||
- 缓解措施:后续只在共享抽象或高风险生命周期接口发生改动时补成员级细审,不在本轮扩张范围
|
||||
- 其他模块族尚未全部建立同粒度的 XML inventory
|
||||
@ -80,6 +82,8 @@
|
||||
- 缓解措施:将本 topic 作为长期 active topic 保留,并在后续巡检中记录回漂来源
|
||||
- VitePress 页面不能直接链接到 `docs/` 目录之外的模块 `README.md`
|
||||
- 缓解措施:站内页面用模块路径文本或站内 API 入口表达,仓库级 README 仍保留仓库文件链接
|
||||
- `GFramework.Cqrs` 在当前 WSL / dotnet 环境下,本地 build 仍会读取失效的 fallback package folder 配置,导致无法完成该项目的标准编译验证
|
||||
- 缓解措施:本轮先以 `GFramework.Cqrs.SourceGenerators` 编译通过和 docs site build 通过作为有效验证,并在后续环境治理或构建脚本清理时单独处理 `RestoreFallbackFolders` / 资产文件问题
|
||||
|
||||
## 验证说明
|
||||
|
||||
@ -116,9 +120,27 @@
|
||||
- `cd docs && bun run build`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在 Ecs 波次重写后重新构建通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/core/cqrs.md`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在重写 `Cqrs` family landing 后重新验证
|
||||
- `bash .agents/skills/gframework-doc-refresh/scripts/validate-all.sh docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在新增 `Cqrs.SourceGenerators` 专题页后验证通过
|
||||
- `python3` 轻量 XML inventory 扫描
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 确认 `GFramework.Cqrs` 的 `Internal/` 为 `14/14`、`GFramework.Cqrs.SourceGenerators/Cqrs/` 为 `3/3`、`GFramework.Cqrs.Abstractions/Cqrs/` 为 `20/20`
|
||||
- `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release -p:RestoreFallbackFolders=`
|
||||
- 结果:通过
|
||||
- 备注:保留既有 `NU1900` 与 `MA0051` warnings;无新增编译错误
|
||||
- `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:失败
|
||||
- 备注:当前环境会命中失效的 Windows fallback package folder,并在多目标 inner build 阶段触发 `MSB4276` / `MSB4018`;失败原因已记录为环境阻塞,不属于本轮文档改动回归
|
||||
- `cd docs && bun run build`
|
||||
- 结果:通过
|
||||
- 备注:`2026-04-22` 在 `Cqrs` 波次文档刷新后重新构建通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 进入 `Cqrs` 波次,梳理 `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` 的模块边界与 docs 入口
|
||||
2. 判断是否需要为 `Cqrs` family 新建 dedicated landing,或把现有 `core/cqrs.md` 拆分成模块族入口页
|
||||
3. 继续为每个模块族补“README / landing / tutorials / API reference / XML”对照表,持续清零 `P0` / `P1`
|
||||
1. 切换到 `Game` / `Game.Abstractions` / `Game.SourceGenerators` 波次,按 `Cqrs` 模板核对 README / landing / tutorials / API reference / XML 链路
|
||||
2. 评估 `Game` family 当前是否已经具备类型族级 XML inventory,还是仍停留在“README / 页面存在但不可审计”
|
||||
3. 在后续环境治理任务中单独处理 `GFramework.Cqrs` 本地 build 的 fallback package folder 阻塞,避免影响后续代码类验证
|
||||
|
||||
@ -99,3 +99,45 @@
|
||||
|
||||
1. 在 `Cqrs` 波次核对模块 README、`docs/zh-CN/core/cqrs.md` 与 `docs/zh-CN/source-generators/**` 的真实 owner
|
||||
2. 决定 `Cqrs` family 是补 dedicated landing 还是拆分现有入口页
|
||||
|
||||
### 当前恢复点:RP-004
|
||||
|
||||
- 完成 `Cqrs` 波次的模块族入口刷新:
|
||||
- 重写 `docs/zh-CN/core/cqrs.md`
|
||||
- 新建 `docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`
|
||||
- 更新 `docs/zh-CN/source-generators/index.md`
|
||||
- 更新 `docs/zh-CN/api-reference/index.md`
|
||||
- 更新 `docs/.vitepress/config.mts`
|
||||
- 将 `Cqrs` family 从“README 已存在但 generator 入口分散”推进到“runtime / abstractions / source generator 都有明确站内入口”
|
||||
- 为 `GFramework.Cqrs/Internal/CqrsHandlerRegistrar.cs` 与
|
||||
`GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 中缺失的内部类型补齐 XML 注释
|
||||
- 基于轻量扫描确认:
|
||||
- `GFramework.Cqrs.Abstractions/Cqrs/` 当前类型声明级 XML 覆盖为 `20/20`
|
||||
- `GFramework.Cqrs` 根入口与 `Internal/` 已补到 `19/19`
|
||||
- `GFramework.Cqrs.SourceGenerators/Cqrs/` 当前类型声明级 XML 覆盖为 `3/3`
|
||||
|
||||
### 当前决策(RP-004)
|
||||
|
||||
- `docs/zh-CN/core/cqrs.md` 继续保留在 `Core` 栏目,但其角色调整为 `Cqrs` family landing,而不再只是 runtime 简介页
|
||||
- `Cqrs.SourceGenerators` 不单独新建一级导航栏目,而是在 `source-generators` 栏目内补一个专用专题页,保持站点 taxonomy 稳定
|
||||
- generator 入口以“专题页 + API reference 链接 + sidebar”三点联动,而不是只在 `source-generators/index.md` 留一个段落链接
|
||||
- XML inventory 仍维持“类型声明级基线”口径,不在本轮扩展成成员级 `param/returns/exception` 细审
|
||||
|
||||
### 当前验证(RP-004)
|
||||
|
||||
- 文档校验:
|
||||
- `validate-all.sh docs/zh-CN/core/cqrs.md`:通过
|
||||
- `validate-all.sh docs/zh-CN/source-generators/cqrs-handler-registry-generator.md`:通过
|
||||
- 轻量 XML inventory:
|
||||
- `GFramework.Cqrs/Internal/`:`14/14`
|
||||
- `GFramework.Cqrs.Abstractions/Cqrs/`:`20/20`
|
||||
- `GFramework.Cqrs.SourceGenerators/Cqrs/`:`3/3`
|
||||
- 构建校验:
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release -p:RestoreFallbackFolders=`:通过
|
||||
- `cd docs && bun run build`:通过;仅保留 VitePress 大 chunk warning,无构建失败
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`:失败;当前 WSL / dotnet 环境仍引用失效的 Windows fallback package folder,并在多目标 inner build 阶段触发 `MSB4276` / `MSB4018`
|
||||
|
||||
### 下一步
|
||||
|
||||
1. 切换到 `Game` family 波次,按 `Core` / `Ecs` / `Cqrs` 已验证模板继续补 XML inventory 与教程链路
|
||||
2. 把 `GFramework.Cqrs` 的本地构建阻塞留给后续环境治理或构建脚本清理,不在本 topic 内扩张为环境修复任务
|
||||
|
||||
@ -252,6 +252,7 @@ export default defineConfig({
|
||||
{ text: 'ContextAware 生成器', link: '/zh-CN/source-generators/context-aware-generator' },
|
||||
{ text: 'Priority 生成器', link: '/zh-CN/source-generators/priority-generator' },
|
||||
{ text: 'Context Get 注入', link: '/zh-CN/source-generators/context-get-generator' },
|
||||
{ text: 'CQRS Handler Registry', link: '/zh-CN/source-generators/cqrs-handler-registry-generator' },
|
||||
{ text: 'Godot 项目元数据', link: '/zh-CN/source-generators/godot-project-generator' },
|
||||
{ text: 'GetNode 生成器 (Godot)', link: '/zh-CN/source-generators/get-node-generator' },
|
||||
{ text: 'BindNodeSignal 生成器 (Godot)', link: '/zh-CN/source-generators/bind-node-signal-generator' }
|
||||
|
||||
@ -30,7 +30,7 @@ description: GFramework 的 API 阅读入口,按模块映射 README、专题
|
||||
| 模块族 | 模块 README | 站内入口 | XML 文档关注点 |
|
||||
| --- | --- | --- | --- |
|
||||
| `Core` / `Core.Abstractions` | `GFramework.Core/README.md`、`GFramework.Core.Abstractions/README.md` | [`../core/index.md`](../core/index.md)、[`../abstractions/core-abstractions.md`](../abstractions/core-abstractions.md) | 架构入口、生命周期、命令 / 查询 / 事件 / 状态 / 资源 / 日志 / 配置 / 并发契约 |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `GFramework.Cqrs/README.md`、`GFramework.Cqrs.Abstractions/README.md`、`GFramework.Cqrs.SourceGenerators/README.md` | [`../core/cqrs.md`](../core/cqrs.md)、[`../source-generators/index.md`](../source-generators/index.md) | request / notification / handler / pipeline / registry contract |
|
||||
| `Cqrs` / `Cqrs.Abstractions` / `Cqrs.SourceGenerators` | `GFramework.Cqrs/README.md`、`GFramework.Cqrs.Abstractions/README.md`、`GFramework.Cqrs.SourceGenerators/README.md` | [`../core/cqrs.md`](../core/cqrs.md)、[`../source-generators/cqrs-handler-registry-generator.md`](../source-generators/cqrs-handler-registry-generator.md) | request / notification / handler / pipeline / registry / fallback contract |
|
||||
| `Game` / `Game.Abstractions` / `Game.SourceGenerators` | `GFramework.Game/README.md`、`GFramework.Game.Abstractions/README.md`、`GFramework.Game.SourceGenerators/README.md` | [`../game/index.md`](../game/index.md)、[`../abstractions/game-abstractions.md`](../abstractions/game-abstractions.md) | 配置、数据、设置、场景、UI、存储、序列化契约 |
|
||||
| `Godot` / `Godot.SourceGenerators` | `GFramework.Godot/README.md`、`GFramework.Godot.SourceGenerators/README.md` | [`../godot/index.md`](../godot/index.md)、[`../source-generators/index.md`](../source-generators/index.md) | 节点扩展、场景 / UI 适配、资源 / 存储 / 日志接入 |
|
||||
| `Ecs.Arch` / `Ecs.Arch.Abstractions` | `GFramework.Ecs.Arch/README.md`、`GFramework.Ecs.Arch.Abstractions/README.md` | [`../ecs/index.md`](../ecs/index.md)、[`../ecs/arch.md`](../ecs/arch.md)、[`../abstractions/ecs-arch-abstractions.md`](../abstractions/ecs-arch-abstractions.md) | ECS 模块契约、系统适配、配置对象和运行时装配边界 |
|
||||
|
||||
@ -1,15 +1,29 @@
|
||||
---
|
||||
title: CQRS
|
||||
description: 当前推荐的新请求模型,统一覆盖 command、query、notification、stream request 和 pipeline behaviors。
|
||||
description: Cqrs 模块族的运行时、契约层、生成器入口,以及 XML / API 阅读链路。
|
||||
---
|
||||
|
||||
# CQRS
|
||||
|
||||
`GFramework.Cqrs` 是当前推荐的新请求模型 runtime。
|
||||
`Cqrs` 栏目对应三个直接相关的消费模块:
|
||||
|
||||
如果你在写新功能,优先使用这套模型,而不是继续扩展 `GFramework.Core.Command` / `Query` 的兼容层。
|
||||
- `GFramework.Cqrs`
|
||||
- `GFramework.Cqrs.Abstractions`
|
||||
- `GFramework.Cqrs.SourceGenerators`
|
||||
|
||||
## 安装方式
|
||||
如果你在写新功能,优先使用这套请求模型,而不是继续扩展 `GFramework.Core.Command` / `Query` 的兼容层。
|
||||
|
||||
## 模块族边界
|
||||
|
||||
| 模块 | 角色 | 何时安装 |
|
||||
| --- | --- | --- |
|
||||
| `GeWuYou.GFramework.Cqrs.Abstractions` | 纯契约层,定义 request、notification、stream、handler、pipeline、runtime seam | 需要把消息契约放到更稳定的共享层,或只依赖接口做解耦 |
|
||||
| `GeWuYou.GFramework.Cqrs` | 默认 runtime,提供 dispatcher、handler 基类、上下文扩展和程序集注册流程 | 大多数直接消费 CQRS 的业务模块 |
|
||||
| `GeWuYou.GFramework.Cqrs.SourceGenerators` | 编译期生成 `ICqrsHandlerRegistry`,缩小运行时反射扫描范围 | handler 较多,想把注册映射前移到编译期 |
|
||||
|
||||
## 最小接入路径
|
||||
|
||||
最小安装组合是:
|
||||
|
||||
```bash
|
||||
dotnet add package GeWuYou.GFramework.Cqrs
|
||||
@ -22,15 +36,6 @@ dotnet add package GeWuYou.GFramework.Cqrs.Abstractions
|
||||
dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
|
||||
```
|
||||
|
||||
## 先理解分层
|
||||
|
||||
- `GFramework.Cqrs.Abstractions`
|
||||
- 纯契约层,定义请求、处理器、行为等接口
|
||||
- `GFramework.Cqrs`
|
||||
- 默认 runtime、dispatcher、处理器基类和上下文扩展
|
||||
- `GFramework.Cqrs.SourceGenerators`
|
||||
- 可选生成器,为消费端程序集生成 `ICqrsHandlerRegistry`
|
||||
|
||||
## 最小示例
|
||||
|
||||
消息基类和处理器基类在不同命名空间:
|
||||
@ -38,12 +43,10 @@ dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
|
||||
- 消息基类:`GFramework.Cqrs.Command` / `Query` / `Notification`
|
||||
- 处理器基类:`GFramework.Cqrs.Cqrs.Command` / `Query` / `Notification`
|
||||
|
||||
示例:
|
||||
|
||||
```csharp
|
||||
using GFramework.Cqrs.Abstractions.Cqrs.Command;
|
||||
using GFramework.Cqrs.Command;
|
||||
using GFramework.Cqrs.Cqrs.Command;
|
||||
using GFramework.Cqrs.Abstractions.Cqrs.Command;
|
||||
|
||||
public sealed record CreatePlayerInput(string Name) : ICommandInput;
|
||||
|
||||
@ -66,9 +69,7 @@ public sealed class CreatePlayerCommandHandler
|
||||
}
|
||||
```
|
||||
|
||||
## 发送请求
|
||||
|
||||
如果你在 `IContextAware` 对象内部:
|
||||
如果你在 `IContextAware` 对象内部发送请求:
|
||||
|
||||
```csharp
|
||||
using GFramework.Cqrs.Extensions;
|
||||
@ -77,7 +78,7 @@ var playerId = await this.SendAsync(
|
||||
new CreatePlayerCommand(new CreatePlayerInput("Alice")));
|
||||
```
|
||||
|
||||
如果你在组合根或测试里:
|
||||
如果你在组合根或测试里发送请求:
|
||||
|
||||
```csharp
|
||||
var playerId = await architecture.Context.SendRequestAsync(
|
||||
@ -92,7 +93,7 @@ var playerId = await architecture.Context.SendRequestAsync(
|
||||
- `PublishAsync(...)`
|
||||
- `CreateStream(...)`
|
||||
|
||||
## 查询、通知和流
|
||||
## 统一请求模型
|
||||
|
||||
这套 runtime 不只处理 command,也统一处理:
|
||||
|
||||
@ -103,9 +104,9 @@ var playerId = await architecture.Context.SendRequestAsync(
|
||||
- Stream Request
|
||||
- 返回 `IAsyncEnumerable<T>`
|
||||
|
||||
也就是说,新代码通常不需要再分别设计“命令总线”“查询总线”和另一套通知分发语义。
|
||||
新代码通常不需要再分别设计“命令总线”“查询总线”和另一套通知分发语义。
|
||||
|
||||
## 注册处理器
|
||||
## 处理器注册与生成器协作
|
||||
|
||||
在标准 `Architecture` 启动路径中,CQRS runtime 会自动接入基础设施。你通常只需要在 `OnInitialize()` 里追加行为或额外程序集:
|
||||
|
||||
@ -123,11 +124,15 @@ protected override void OnInitialize()
|
||||
}
|
||||
```
|
||||
|
||||
默认逻辑会:
|
||||
默认注册流程当前遵循这些语义:
|
||||
|
||||
1. 优先使用消费端程序集上的生成注册器
|
||||
2. 生成注册器不可用时回退到反射扫描
|
||||
3. 对同一程序集去重,避免重复注册
|
||||
1. 优先读取消费端程序集上的 `CqrsHandlerRegistryAttribute`
|
||||
2. 存在生成注册器时优先使用 `ICqrsHandlerRegistry`
|
||||
3. 生成注册器不可用或元数据异常时记录告警并回退到反射路径
|
||||
4. 如果程序集带有 `CqrsReflectionFallbackAttribute`,只补扫剩余 handler
|
||||
5. 同一程序集按稳定键去重,避免重复注册
|
||||
|
||||
`Cqrs.SourceGenerators` 的专题入口见 [../source-generators/cqrs-handler-registry-generator.md](../source-generators/cqrs-handler-registry-generator.md)。
|
||||
|
||||
## Pipeline Behavior
|
||||
|
||||
@ -145,7 +150,7 @@ RegisterCqrsPipelineBehavior<LoggingBehavior<,>>();
|
||||
- 审计
|
||||
- 重试或统一异常封装
|
||||
|
||||
旧的 `Mediator` 兼容别名入口已经移除;当前公开入口只有 `RegisterCqrsPipelineBehavior<TBehavior>()`。
|
||||
当前公开入口只有 `RegisterCqrsPipelineBehavior<TBehavior>()`。
|
||||
|
||||
## 和旧 Command / Query 的关系
|
||||
|
||||
@ -157,15 +162,28 @@ RegisterCqrsPipelineBehavior<LoggingBehavior<,>>();
|
||||
- 新路径
|
||||
- `GFramework.Cqrs`
|
||||
|
||||
`IArchitectureContext` 仍然会兼容旧入口,但新代码应优先使用 CQRS runtime。
|
||||
`IArchitectureContext` 仍然兼容旧入口,但新代码应优先使用 CQRS runtime。
|
||||
|
||||
一个简单判断规则:
|
||||
|
||||
- 在维护历史代码:允许继续使用旧 Command / Query
|
||||
- 在写新功能或新模块:优先使用 CQRS
|
||||
|
||||
## XML 覆盖基线
|
||||
|
||||
下面这份 inventory 记录的是 `2026-04-22` 对 `Cqrs` 家族做的一轮轻量 XML 盘点结果:只统计当前运行时、契约层和生成器入口中的类型声明级 XML 覆盖,用来校对 README、landing page 与 API 入口,不把它表述成成员级契约全审计。
|
||||
|
||||
| 类型族 | 基线状态 | 代表类型 | 阅读重点 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GFramework.Cqrs.Abstractions/Cqrs/` | `20/20` 个类型声明已带 XML 注释 | `ICqrsRuntime`、`ICqrsHandlerRegistrar`、`IPipelineBehavior<,>`、`IRequestHandler<,>`、`Unit` | 先看请求、处理器和 runtime seam 的最小契约 |
|
||||
| `GFramework.Cqrs/Command` `Query` `Notification` `Request` `Extensions` | `7/7` 个类型声明已带 XML 注释 | `CommandBase<TInput, TResponse>`、`QueryBase<TInput, TResponse>`、`NotificationBase<TInput>`、`ContextAwareCqrsExtensions` | 看业务侧常用基类和上下文发送入口 |
|
||||
| `GFramework.Cqrs/Cqrs/` | `12/12` 个类型声明已带 XML 注释 | `AbstractCommandHandler<,>`、`AbstractQueryHandler<,>`、`AbstractNotificationHandler<>`、`LoggingBehavior<,>` | 看默认处理器基类、上下文注入与行为管道 |
|
||||
| `GFramework.Cqrs` 根入口与 `Internal/` | `19/19` 个类型声明已带 XML 注释 | `CqrsRuntimeFactory`、`ICqrsHandlerRegistry`、`CqrsHandlerRegistryAttribute`、`CqrsReflectionFallbackAttribute`、`DefaultCqrsRegistrationService` | 看 runtime 创建入口、registry 协议、fallback 语义和程序集去重规则 |
|
||||
| `GFramework.Cqrs.SourceGenerators/Cqrs/` | `3/3` 个类型声明已带 XML 注释 | `CqrsHandlerRegistryGenerator`、`RuntimeTypeReferenceSpec`、`OrderedRegistrationKind` | 看生成注册器、精确 type lookup 和 fallback 诊断边界 |
|
||||
|
||||
## 继续阅读
|
||||
|
||||
- 架构入口:[architecture](./architecture.md)
|
||||
- 上下文入口:[context](./context.md)
|
||||
- 生成器专题:[../source-generators/cqrs-handler-registry-generator.md](../source-generators/cqrs-handler-registry-generator.md)
|
||||
- 模块 README:`GFramework.Cqrs/README.md`
|
||||
|
||||
127
docs/zh-CN/source-generators/cqrs-handler-registry-generator.md
Normal file
127
docs/zh-CN/source-generators/cqrs-handler-registry-generator.md
Normal file
@ -0,0 +1,127 @@
|
||||
---
|
||||
title: CQRS Handler Registry 生成器
|
||||
description: 为消费端程序集生成 CQRS handler registry,并在需要时附带精确 reflection fallback 元数据。
|
||||
---
|
||||
|
||||
# CQRS Handler Registry 生成器
|
||||
|
||||
`GFramework.Cqrs.SourceGenerators` 会在编译期为当前业务程序集生成 `ICqrsHandlerRegistry`,让 `GFramework.Cqrs`
|
||||
runtime 在注册 handlers 时优先走静态注册表,而不是先扫描整个程序集。
|
||||
|
||||
它服务的是 `Cqrs` 家族,不是独立运行时:
|
||||
|
||||
- 契约层:`GeWuYou.GFramework.Cqrs.Abstractions`
|
||||
- 默认 runtime:`GeWuYou.GFramework.Cqrs`
|
||||
- 编译期生成器:`GeWuYou.GFramework.Cqrs.SourceGenerators`
|
||||
|
||||
## 生成什么
|
||||
|
||||
当前生成器会分析消费端程序集中的:
|
||||
|
||||
- `IRequestHandler<,>`
|
||||
- `INotificationHandler<>`
|
||||
- `IStreamRequestHandler<,>`
|
||||
|
||||
然后输出两类结果:
|
||||
|
||||
1. 一个实现 `ICqrsHandlerRegistry` 的内部注册器类型
|
||||
2. 程序集级 `CqrsHandlerRegistryAttribute`
|
||||
|
||||
当某些 handler 不能被生成代码安全地直接引用时,还会补发:
|
||||
|
||||
- 程序集级 `CqrsReflectionFallbackAttribute`
|
||||
|
||||
这意味着运行时会先使用生成注册器完成可静态表达的映射,再只对剩余类型做补扫,而不是退回整程序集盲扫。
|
||||
|
||||
## 最小接入路径
|
||||
|
||||
安装方式保持 runtime 包与生成器包版本一致,并把生成器作为编译期依赖引入:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<PackageReference Include="GeWuYou.GFramework.Cqrs" Version="x.y.z" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Cqrs.Abstractions" Version="x.y.z" />
|
||||
<PackageReference Include="GeWuYou.GFramework.Cqrs.SourceGenerators"
|
||||
Version="x.y.z"
|
||||
PrivateAssets="all"
|
||||
ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
运行时侧仍然按 `Core` 的标准入口注册程序集:
|
||||
|
||||
```csharp
|
||||
protected override void OnInitialize()
|
||||
{
|
||||
RegisterCqrsHandlersFromAssembly(typeof(GameArchitecture).Assembly);
|
||||
}
|
||||
```
|
||||
|
||||
如果你的 handlers 分布在多个业务程序集里,则改用:
|
||||
|
||||
```csharp
|
||||
RegisterCqrsHandlersFromAssemblies(
|
||||
[
|
||||
typeof(InventoryCqrsMarker).Assembly,
|
||||
typeof(BattleCqrsMarker).Assembly
|
||||
]);
|
||||
```
|
||||
|
||||
## 运行时如何消费生成结果
|
||||
|
||||
`Cqrs` runtime 当前的注册顺序是:
|
||||
|
||||
1. 先读取程序集上的 `CqrsHandlerRegistryAttribute`
|
||||
2. 优先激活生成的 `ICqrsHandlerRegistry`
|
||||
3. 若生成元数据损坏、registry 不可激活,记录告警并回退到反射路径
|
||||
4. 若存在 `CqrsReflectionFallbackAttribute`,只补扫剩余 handler
|
||||
5. 同一程序集按稳定键去重,避免重复注册
|
||||
|
||||
这个行为由 `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 和
|
||||
`GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 共同覆盖。
|
||||
|
||||
## 什么时候值得安装
|
||||
|
||||
推荐安装:
|
||||
|
||||
- 业务程序集内 handler 数量较多
|
||||
- 想把 handler 注册路径前移到编译期
|
||||
- 希望冷启动阶段减少整程序集反射扫描
|
||||
- 需要更明确地观察“哪些 handler 走静态注册,哪些只能走 fallback”
|
||||
|
||||
可以先不装:
|
||||
|
||||
- 项目体量很小,handler 很少
|
||||
- 当前只做原型,尚不关心注册成本
|
||||
- 你还没稳定到 `Cqrs` runtime 的最终接入边界
|
||||
|
||||
## fallback 边界
|
||||
|
||||
生成器并不会承诺“所有 handler 都能被静态表达”。
|
||||
|
||||
当前实现遵循一个保守原则:
|
||||
|
||||
- 能直接引用的 handler,生成直接注册语句
|
||||
- 实现类型不能直接引用、但服务接口还能精确表达时,生成反射实现类型查找
|
||||
- 服务接口本身也需要运行时解析时,生成精确 type lookup
|
||||
- 只有在 runtime 提供 `CqrsReflectionFallbackAttribute` 合同时,才允许发射依赖 fallback 的结果
|
||||
|
||||
如果当前编译环境缺少这个 fallback 合同,而某些 handler 又必须依赖它,生成器会报:
|
||||
|
||||
- `GF_Cqrs_001`
|
||||
|
||||
这条诊断的含义不是“某个 handler 写错了”,而是“当前 runtime 合同不足以安全承载这轮生成结果”。
|
||||
|
||||
## XML / API 阅读入口
|
||||
|
||||
如果你要核对生成器对外暴露的契约,优先看这些类型:
|
||||
|
||||
- `GFramework.Cqrs.ICqrsHandlerRegistry`
|
||||
- `GFramework.Cqrs.CqrsHandlerRegistryAttribute`
|
||||
- `GFramework.Cqrs.CqrsReflectionFallbackAttribute`
|
||||
- `GFramework.Cqrs.SourceGenerators.Cqrs.CqrsHandlerRegistryGenerator`
|
||||
|
||||
模块族入口见:
|
||||
|
||||
- [../core/cqrs.md](../core/cqrs.md)
|
||||
- [./index.md](./index.md)
|
||||
@ -65,7 +65,9 @@ GFramework 当前发布的生成器包是:
|
||||
|
||||
- 配置 schema 生成与运行时接法:
|
||||
- [../game/config-system.md](../game/config-system.md)
|
||||
- CQRS registry 生成入口:
|
||||
- CQRS handler registry 生成器:
|
||||
- [cqrs-handler-registry-generator](./cqrs-handler-registry-generator.md)
|
||||
- CQRS 模块族采用入口:
|
||||
- [../core/cqrs.md](../core/cqrs.md)
|
||||
|
||||
### Godot 专用生成器
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user