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

17 KiB
Raw Blame History

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 业务代码的依赖方向。

它的核心模型可以概括为:

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

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 需要显式 AppKernelRootScopeStartupPipelineFeatureHost,让启动顺序、依赖边界和关闭顺序都可测试、可替换、可释放。

4. Model / System / Utility

QFramework 的分层规则可以简化理解为:

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 不再到处写:

model.Coins.Value += 10;

而是写:

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。更好的方式是拆小能力:

ICanResolveService
ICanPublishEvent
ICanReadState
ICanExecuteUseCase
ICanRegisterLifetime

但这些应属于 P1/P2 业务层或扩展层。Kernel 的 FeatureContext 仍应保持最小,只暴露 Scope、Lifetime、CancellationToken、Handle 等机制概念。

7. TypeEventSystem

QFramework 的 TypeEventSystem 是一个按事件类型分发的轻量事件系统。

它的核心机制:

TypeEventSystem
  -> EasyEvents
    -> Dictionary<Type, IEasyEvent>
      -> EasyEvent<T>

发送事件时按 EasyEvent<T> 类型取出事件对象并触发。注册事件时如果没有对应事件对象,就创建一个。

优点:

  • 使用简单。
  • 类型安全。
  • 适合 UI 刷新和轻量业务通知。
  • 反注册模型清晰。

风险:

  • 没有作用域隔离。
  • 没有事件优先级。
  • 没有异步派发协议。
  • 没有错误隔离。
  • 没有事件链路追踪。
  • 大型项目里容易变成全局黑箱。

FlowScope 可以借鉴“下层发事件,上层订阅”的方向,但 EventBus 不应进入 Kernel 主依赖。更合理的位置是 FlowScope.Events 或 P1 Events 模块:

Kernel
  不知道 EventBus

P1 Events
  注册 IEventBus 到 RootScope
  提供 FeatureLifetime 可登记的订阅句柄
  提供同步或异步事件策略

这样 Feature 可以使用事件,但 Kernel 不被事件模型绑死。

8. BindableProperty

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 的容器多一层生命周期能力:

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 继续保持:

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 中提供推荐规则:

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 的能力,建议区分:

IReadonlyState<T>
IMutableState<T>
StateWriter<T>
StateChanged<T>

UI 层优先拿只读接口,写入通过 UseCase/Command/Service。这样比直接把 BindableProperty<T> 暴露给所有人更稳。

12.5 业务模板可以提供 QFramework 风格

可以考虑后续提供一个可选模板:

FlowScope.BusinessArchitecture
  - ICommand
  - ICommand<TResult>
  - IQuery<TResult>
  - IUseCase
  - IStateService
  - IEventPublisher

但这应是项目选择,不是 Kernel 强制。

13. 风险清单

如果直接照搬 QFramework Core 到 FlowScope主要风险是

  1. Kernel 变成业务架构框架,失去机制最小化。
  2. Architecture<T>.InterfaceRootScope 形成双全局入口。
  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 模块设计参考。

最终边界建议:

FlowScope.Kernel
  只负责机制

FlowScope.CoreModules
  提供 Events / State / Config / Save / Resources / UI 等模块

FlowScope.BusinessTemplate.QFrameworkLike
  可选提供 Command / Query / State / Event 的业务写法参考

这样既能保留 QFramework 的轻量分层优点,又不会牺牲 FlowScope 当前“Kernel tiny, modules optional”的主方向。