Files
FlowScope/docs/guides/qframework-core-analysis.md
2026-06-18 15:11:42 +08:00

502 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QFramework Core 细读与 FlowScope 借鉴边界
更新时间2026-06-12
参考源码:<https://github.com/liangxiegame/QFramework/blob/master/QFramework.cs>
本文分析 QFramework 单文件 Core 的架构设计、适用范围、优缺点,以及它对 FlowScope Kernel / CoreModules 的可借鉴部分。本文不是选型结论,也不是实现计划;它用于后续讨论 FlowScope 业务架构层、事件层、状态层时作为参考。
## 1. 总结
QFramework Core 的价值不在于提供完整商业游戏运行时,而在于用很少的类型固定 Unity 业务代码的依赖方向。
它的核心模型可以概括为:
```text
Architecture<T>
-> IOCContainer
-> Model / System / Utility
-> Command / Query
-> Event / BindableProperty
Controller / MonoBehaviour
-> SendCommand / SendQuery
-> GetModel / GetSystem
-> RegisterEvent
```
QFramework Core 解决的是 Unity 项目里常见的“表现层乱改状态、MonoBehaviour 互相引用、业务逻辑没有入口、通知链路散落”的问题。它不是资源框架、UI 框架、热更框架、构建框架,也不负责 Feature、场景、流程或模块生命周期编排。
对 FlowScope 来说QFramework Core 最值得借鉴的是:
- 状态变更必须有入口。
- 上层可以调用下层,下层不直接引用上层。
- 下层通过事件或可观察状态通知上层。
- Command / Query 是短生命周期行为对象,不承载长期状态。
- 用能力接口限制对象可访问的架构能力。
不适合直接照搬的是:
- 静态全局 `Architecture<T>.Interface` 入口。
- 强制 `Model / System / Utility / Command` 成为 Kernel 业务分层。
- 没有 RootScope / FeatureScope 的单容器模型。
- 没有 Feature 级生命周期和释放边界。
- 把 Event / BindableProperty 作为所有项目默认基础设施。
## 2. Core 范围
QFramework.cs 里的核心组成大致分为以下几组。
| 组 | 类型 | 职责 |
| --- | --- | --- |
| 架构根 | `IArchitecture`, `Architecture<T>` | 注册和获取 Model / System / Utility执行 Command / Query发送和注册事件 |
| 表现层协议 | `IController` | 通常由 MonoBehaviour 实现,负责接入架构,不直接持有业务状态 |
| 业务逻辑层 | `ISystem`, `AbstractSystem` | 放跨表现层共享的业务逻辑 |
| 数据层 | `IModel`, `AbstractModel` | 放状态和数据操作 |
| 工具层 | `IUtility` | 放外部能力适配例如存储、SDK、序列化 |
| 行为对象 | `ICommand`, `ICommand<TResult>`, `IQuery<TResult>` | 封装写操作和读操作 |
| 能力接口 | `ICanGetModel`, `ICanSendCommand` 等 | 用接口组合限制对象能做什么 |
| 事件系统 | `TypeEventSystem`, `EasyEvent` | 按类型注册、发送、反注册事件 |
| 可绑定状态 | `BindableProperty<T>` | 值变化时通知订阅者 |
| 容器 | `IOCContainer` | 按类型注册和解析实例 |
这个范围说明 QFramework Core 更接近“业务代码架构层”,而不是“应用 Kernel”。它把代码写法约束住但不管理完整应用运行期。
## 3. Architecture<T>
`Architecture<T>` 是 QFramework 的架构根。它使用静态字段保存当前架构实例,`Interface` 首次访问时触发初始化。
典型启动过程:
1. 创建 `T : Architecture<T>` 实例。
2. 调用子类实现的 `Init()`
3.`Init()` 中注册 Model、System、Utility。
4. 执行 `OnRegisterPatch`
5. 初始化所有未初始化的 Model。
6. 初始化所有未初始化的 System。
7. 将架构标记为已初始化。
反初始化过程:
1. 调用 `OnDeinit()`
2. 对已初始化的 System 调用 `Deinit()`
3. 对已初始化的 Model 调用 `Deinit()`
4. 清空容器。
5. 清空静态架构实例。
这个设计很适合中小项目:入口少、初始化简单、读源码很快。它的问题也同样明确:全局静态入口会自然演化成 Service Locator业务对象很容易绕开组合根直接拿依赖。
FlowScope 不应该照搬这个入口。FlowScope 的 Kernel 需要显式 `AppKernel``RootScope``StartupPipeline``FeatureHost`,让启动顺序、依赖边界和关闭顺序都可测试、可替换、可释放。
## 4. Model / System / Utility
QFramework 的分层规则可以简化理解为:
```text
Controller -> Command / Query -> System / Model -> Utility
Model / System -> Event / BindableProperty -> Controller
```
### Model
`IModel` 表示数据层。它可以:
- 持有状态。
- 调用 Utility。
- 发送事件。
- 初始化和反初始化。
它不应该直接引用 Controller也不应该直接操作 UI。
### System
`ISystem` 表示共享业务逻辑层。它可以:
- 获取 Model。
- 获取 Utility。
- 获取其他 System。
- 注册事件。
- 发送事件。
- 初始化和反初始化。
System 适合放跨 UI、跨场景、跨表现层复用的业务逻辑。它比 Model 更偏行为,比 Controller 更偏领域。
### Utility
`IUtility` 是最薄的一层,只是一个标记接口。它通常承载外部能力,例如:
- 本地存储。
- 网络 SDK。
- 平台 SDK。
- JSON 序列化。
- 时间服务。
Utility 本身不被强制注入 Architecture这也意味着它更像纯工具或外部适配器。
### 对 FlowScope 的启发
FlowScope Kernel 不应该规定 `Model / System / Utility`。这是项目业务架构选择,不是 Kernel 机制。
但 FlowScope 可以在 CoreModules 或推荐模板里提供类似规则:
- Feature 内部状态归 Feature 自己或 Feature-scoped state service。
- 跨 Feature 长期状态归 Root-scoped service。
- 外部能力通过接口注册到 RootScope。
- UI 和 MonoBehaviour 不直接写长期状态,而是调用 UseCase、Command 或 Service 方法。
## 5. Command / Query
QFramework 用 Command 作为状态变更入口,用 Query 作为读操作入口。
Command 的特征:
- 短生命周期。
- 执行前被注入 Architecture。
- 可获取 Model / System / Utility。
- 可发送事件。
- 可继续发送其他 Command 或 Query。
- 不保存长期状态。
Query 的特征:
- 短生命周期。
- 执行前被注入 Architecture。
- 主要读取 Model / System。
- 返回查询结果。
这个设计的好处是把“写操作”从 Controller 中拿出来。Controller 不再到处写:
```csharp
model.Coins.Value += 10;
```
而是写:
```csharp
this.SendCommand(new AddCoinCommand(10));
```
这样状态变化可以被搜索、测试和复用。
对 FlowScope 来说,这个思想很值得吸收,但名称和形态不应进 Kernel。Kernel 不应该强制所有状态变更都叫 Command。项目可以选择
- `Command`
- `UseCase`
- `Action`
- `Service Method`
- `Application Service`
Kernel 只需要保证依赖边界和生命周期,不需要规定业务行为对象的命名体系。
## 6. 能力接口组合
QFramework 一个很好的设计点是能力接口组合。
例如:
- `ICanGetModel`
- `ICanGetSystem`
- `ICanGetUtility`
- `ICanSendCommand`
- `ICanSendEvent`
- `ICanRegisterEvent`
- `ICanSendQuery`
这些接口本身几乎没有实现,实际能力通过扩展方法提供。对象只有实现了对应接口,才获得对应扩展方法。
这带来的好处是:
- Controller 能做的事和 Model 能做的事不同。
- Model 可以发事件,但不能随意发 Command。
- Command 可以访问 Model/System/Utility但不变成长期服务。
- 架构规则体现在类型系统里,而不是只写在文档里。
这个方向对 FlowScope 有参考价值。FlowScope 如果后续做业务架构层,可以避免提供一个万能 `FeatureContext`。更好的方式是拆小能力:
```text
ICanResolveService
ICanPublishEvent
ICanReadState
ICanExecuteUseCase
ICanRegisterLifetime
```
但这些应属于 P1/P2 业务层或扩展层。Kernel 的 `FeatureContext` 仍应保持最小,只暴露 Scope、Lifetime、CancellationToken、Handle 等机制概念。
## 7. TypeEventSystem
QFramework 的 `TypeEventSystem` 是一个按事件类型分发的轻量事件系统。
它的核心机制:
```text
TypeEventSystem
-> EasyEvents
-> Dictionary<Type, IEasyEvent>
-> EasyEvent<T>
```
发送事件时按 `EasyEvent<T>` 类型取出事件对象并触发。注册事件时如果没有对应事件对象,就创建一个。
优点:
- 使用简单。
- 类型安全。
- 适合 UI 刷新和轻量业务通知。
- 反注册模型清晰。
风险:
- 没有作用域隔离。
- 没有事件优先级。
- 没有异步派发协议。
- 没有错误隔离。
- 没有事件链路追踪。
- 大型项目里容易变成全局黑箱。
FlowScope 可以借鉴“下层发事件,上层订阅”的方向,但 EventBus 不应进入 Kernel 主依赖。更合理的位置是 `FlowScope.Events` 或 P1 Events 模块:
```text
Kernel
不知道 EventBus
P1 Events
注册 IEventBus 到 RootScope
提供 FeatureLifetime 可登记的订阅句柄
提供同步或异步事件策略
```
这样 Feature 可以使用事件,但 Kernel 不被事件模型绑死。
## 8. BindableProperty<T>
`BindableProperty<T>` 是一个可观察值对象。它在 `Value` 变化时触发 `EasyEvent<T>`,并提供:
- `Register`
- `RegisterWithInitValue`
- `UnRegister`
- `SetValueWithoutEvent`
- 自定义比较器
它适合表达 UI 绑定和简单状态监听,例如金币、生命值、进度条、开关状态。
优点:
- 简单直接。
- UI 订阅体验好。
- `RegisterWithInitValue` 避免 UI 初始刷新遗漏。
- 对中小项目足够实用。
风险:
- 比较器是静态泛型级别配置,使用时要小心全局影响。
- 复杂状态容易被拆成大量可绑定字段,导致状态事务边界不清。
- 不适合表达复杂异步流、错误流、完成流。
- 如果到处暴露可写 `BindableProperty<T>`,仍然会出现状态乱改。
FlowScope 可以把类似能力放到状态或 UI 扩展模块,但不应放进 Kernel。Kernel 只负责 Feature 生命周期;状态响应式属于业务层或表现层。
## 9. IOCContainer
QFramework 的 `IOCContainer` 很小:
- `Register<T>(T instance)``typeof(T)` 注册。
- `Get<T>()``typeof(T)` 获取。
- `GetInstancesByType<T>()` 枚举所有兼容类型实例。
- `Clear()` 清空。
优点:
- 极易理解。
- 调试成本低。
- 足够支撑 QFramework 的 Model/System/Utility 注册。
不足:
- 没有构造函数注入。
- 没有 Scope。
- 没有生命周期释放。
- 没有 `IDisposable` / `IAsyncDisposable` 管理。
- 没有多绑定。
- 没有 named/keyed service。
- 没有循环依赖检测。
- 没有线程安全。
FlowScope 可以借鉴“按类型注册和解析”的最小体验,但实现必须比 QFramework 的容器多一层生命周期能力:
```text
RootScope
长生命周期服务
FeatureScope
Feature 实例和本次启动参数
先查本地,再回退 RootScope
FeatureLifetime
释放运行期句柄、订阅、资源组、临时对象
```
这也是 FlowScope 不能直接采用 QFramework IOC 的核心原因。
## 10. 生命周期对比
| 维度 | QFramework Core | FlowScope Kernel 目标 |
| --- | --- | --- |
| 应用入口 | 静态 `Architecture<T>.Interface` | 显式 `AppKernel` |
| 启动流程 | 首次访问时初始化 | `StartupPipeline` 顺序执行 |
| 依赖容器 | 单个 `IOCContainer` | `RootScope + FeatureScope` |
| 业务运行单元 | Model/System 长驻 | Feature 实例 |
| Feature 生命周期 | 不负责 | `LoadAsync / EnterAsync / ExitAsync` |
| 释放边界 | Model/System Deinit容器 Clear | `FeatureLifetime + FeatureScope + RootScope` |
| 异步关闭 | 不突出 | Kernel 必须处理 async stop/shutdown |
| 运行位置 | 不负责 | `FeatureSlot + FeaturePolicy` |
QFramework 的生命周期适合“一个游戏一个 Architecture若干长驻 Model/System”。FlowScope 的目标是“一个应用 Kernel 管多个 Feature 实例,每个 Feature 有独立依赖边界和释放边界”。
这两个方向不冲突但层级不同。QFramework 更像可选的业务组织方式FlowScope Kernel 更像运行机制。
## 11. 与完整框架的横向比较
| 维度 | QFramework Core | UnityGameFramework | ET Framework | TinaX | FlowScope 应吸收 |
| --- | --- | --- | --- | --- | --- |
| 核心定位 | 轻量业务分层 | 客户端模块全家桶 | 双端大型联网运行时 | 服务化 Package 框架 | 小 Kernel + 可选模块 |
| 启动入口 | Architecture | GameEntry / Procedure | Scene / Fiber / Actor | Core service bootstrap | AppKernel / StartupPipeline |
| 资源系统 | Core 不负责 | 内置 Resource | 可接入包和热更链路 | VFS | P1 Resources 模块 |
| UI | Core 不负责UIKit 在工具生态 | 内置 UI 模块 | 通常通过包扩展 | UIKit | P1 UI 模块 |
| 事件 | TypeEventSystem | Event 模块 | 消息/事件体系 | Event/System service | P1 Events 模块 |
| 状态变更 | Command | 模块 API / Procedure | Entity/System/消息 | Service API | 可选 Command/UseCase |
| 工程流水线 | 不负责 | 部分覆盖 | 强工具链 | Package 化 | Tools/Package 后续模块 |
QFramework 在这张表里的优势是轻量和清晰。它不应该被拿来和 UGF 的资源/UI/Procedure 覆盖度硬比,也不应该被要求承担 BDFramework 的热更构建流水线职责。
## 12. 对 FlowScope 的建议
### 12.1 Kernel 不吸收 QFramework 分层
FlowScope Kernel 继续保持:
```text
AppKernel
StartupPipeline
FeatureHost
Container
FeatureLifetime
FeatureSlot
FeaturePolicy
IFeature
FeatureContext
FeatureHandle
```
Kernel 不加入:
- `IModel`
- `ISystem`
- `IUtility`
- `ICommand`
- `IQuery`
- `BindableProperty`
- `TypeEventSystem`
- `IController`
原因是这些属于业务架构风格,不是 Feature 编排机制。
### 12.2 CoreModules 可吸收状态变更规则
FlowScope 可以在后续 CoreModules 或 template 中提供推荐规则:
```text
View / MonoBehaviour
-> Command / UseCase / Service Method
-> State Service / Feature Local State
-> Event / Observable
-> View / Presenter / Controller
```
这能吸收 QFramework 的优点,同时避免 Kernel 被业务范式锁死。
### 12.3 Events 模块可参考 TypeEventSystem但要补足边界
如果 FlowScope 做 P1 Events不建议只复制 `TypeEventSystem`。至少要明确:
- 事件作用域Root-scoped 还是 Feature-scoped。
- 订阅句柄如何进入 FeatureLifetime。
- 事件处理异常是否聚合、记录或继续派发。
- 是否支持异步事件。
- 是否允许跨 Feature 事件。
- 是否提供调试追踪。
QFramework 的 TypeEventSystem 可作为最小同步事件模型参考,但不是最终模块规格。
### 12.4 State 模块可参考 BindableProperty但不要裸露可写状态
如果 FlowScope 提供类似 BindableProperty 的能力,建议区分:
```text
IReadonlyState<T>
IMutableState<T>
StateWriter<T>
StateChanged<T>
```
UI 层优先拿只读接口,写入通过 UseCase/Command/Service。这样比直接把 `BindableProperty<T>` 暴露给所有人更稳。
### 12.5 业务模板可以提供 QFramework 风格
可以考虑后续提供一个可选模板:
```text
FlowScope.BusinessArchitecture
- ICommand
- ICommand<TResult>
- IQuery<TResult>
- IUseCase
- IStateService
- IEventPublisher
```
但这应是项目选择,不是 Kernel 强制。
## 13. 风险清单
如果直接照搬 QFramework Core 到 FlowScope主要风险是
1. Kernel 变成业务架构框架,失去机制最小化。
2. `Architecture<T>.Interface``RootScope` 形成双全局入口。
3. `IOCContainer` 无法表达 FeatureScope 生命周期。
4. Command/Event 进入 Kernel 后P1/P2 模块会被迫统一业务风格。
5. EventBus 变成隐式跨 Feature 通道,削弱 FeatureHost 的显式编排边界。
6. BindableProperty 进入底层后UI/状态响应式会反向污染 Kernel。
规避方式:
- Kernel 只保留生命周期、依赖边界、Feature 编排。
- QFramework 的分层规则只进入文档、模板或可选模块。
- Event/State/Command 都作为 CoreModules 或扩展层设计。
- FeatureContext 不提供全能业务能力。
## 14. 结论
QFramework Core 是一个优秀的轻量 Unity 业务架构样本。它用很少代码表达了三条关键规则:
- 上层驱动下层。
- 状态变更有入口。
- 下层通过通知影响上层。
FlowScope 应吸收这些规则,但不能把 QFramework 的 Core 形态直接变成自己的 Kernel。FlowScope Kernel 的目标更底层:它负责应用启动、依赖 Scope、Feature 生命周期、运行槽和释放链。QFramework 风格更适合作为 FlowScope 的可选业务层、示例模板或 P1/P2 模块设计参考。
最终边界建议:
```text
FlowScope.Kernel
只负责机制
FlowScope.CoreModules
提供 Events / State / Config / Save / Resources / UI 等模块
FlowScope.BusinessTemplate.QFrameworkLike
可选提供 Command / Query / State / Event 的业务写法参考
```
这样既能保留 QFramework 的轻量分层优点,又不会牺牲 FlowScope 当前“Kernel tiny, modules optional”的主方向。