Files
gplan-2/design/dev/architecture/adr-0001-event-bus-and-orchestration-carrier.md
T

8.3 KiB
Raw Blame History

ADR-0001: 事件总线与编排载体(Event Bus & Orchestration Carrier)

Status

Accepted(2026-09-12,solo 模式用户授权代决;用户迭代期可随时推翻重议——推翻时须走本 ADR 修订 + 下游 AC 连锁复核)

Date

2026-09-12

Engine Compatibility

Field Value
Engine Godot 4.7.2-stable
Domain Core / Scripting(Signal/Callable/生命周期)
Knowledge Risk LOW(4.x 基线稳定;4.5–4.7 变更清单零命中,godot-specialist 校验通过)
References Consulted docs/engine-reference/godot/(VERSION / breaking-changes / deprecated-apis / current-best-practices);godotengine/godot#6360
Post-Cutoff APIs Used None
Verification Required None(切片 88/88 冒烟已实证:双源 round_end 汇聚、可回放步骤流)

ADR Dependencies

Field Value
Depends On None
Enables ADR-0003(场景结构引用本 ADR 的接线纪律);A2/B1 全部事件序类 AC 从纸面契约转为可执行
Blocks 正式工程骨架搭建(架构文档 Required ADRs 条件)
Ordering Note 与 ADR-0008(切片机制正式化)并行无冲突;B1 结算触发语义以本 ADR 载体承载

Context

Problem Statement

A2(三事件外发)、A3(stopped 直发 round_end,双发射源)、B1(结算触发 = Waiting 相位编排点、不依赖事件到达序)之间的信号载体需要一个落地形态——GDD 层只定行为契约(A2 OQ2 / B1 OQ1 均登记为"实现期 ADR 确认项")。

Constraints

  • 单线程回合制(D2 EC13);Core 层零 UI 依赖、headless 可跑(D2 Rule 7)
  • 结算可回放是硬承诺(B1 Core Rule 15:禁 await、零随机)
  • 事件序契约:flag_judged < round_end 全路径统一,但到达序不承载语义
  • RefCounted 纯逻辑类无场景表示,无法在 .tscn 编辑器里对接信号

Requirements

  • 支持 A2 三事件面 + A3 stopped 直发的双发射源汇聚
  • 支持多订阅者(B1 facts 缓冲;呈现层只订 B1/runner)
  • 测试可捕获事件序列(A2/B1 AC 的事件捕获断言面)
  • 生命周期确定(无悬挂连接、无循环引用)

Decision

组合根直连 signal 接线 + B1 Waiting 相位触发;否决 autoload 全局事件总线。

  1. 信号载体:A2(RefCounted)声明并发射 GDD 三事件——cell_revealed / flag_judged / round_end_board(GDD 概念名 round_end 棋盘路径的实现实名);A3 停手路径发射 round_end_stopped(payload 与棋盘路径同构)。回发 next_round_mode / battle_over 同为 A3/B1 侧 signal。
  2. 接线位置:全部 signal.connect 发生在组合根(主场景 Node 的装配代码)——core↔core 无静态耦合,依赖单向由接线拓扑保证。RefCounted 无 .tscn 编辑器对接能力,组合根是唯一自然落点。
  3. B1 触发:订阅 A3 相位变更(→Waiting)触发结算事务;触发判定只读 A3 相位观测量与 last_round_end,不消费事件到达序(同步发射 + 双发射源的正确防御—— specialist 确认信号为同步发射、多订阅者投递序 = 连接序 FIFO)。
  4. 订阅纪律:呈现层永不直接订阅 A2/A3——只订 B1 步骤流 / runner(切片现状固化,防呈现层绕过编排读核心生命周期状态——切片缺陷②教训)。

Architecture Diagram

[A2 minesweeper_rules]──cell_revealed/flag_judged/round_end_board──┐
[A3 action_economy]────round_end_stopped / phase→Waiting──────────┤
                                                                   ▼
                                        [组合根 main 装配接线]→[B1 battle_orchestrator]
                                                                   │ 步骤流行协议
                                                                   ▼
                                                        [D1 视图单元(只订 B1/runner)]

Key Interfaces

  • 信号均为 方法 Callable 连接(见禁令);信号参数带类型标注仅作文档,无运行时检查——payload 校验由消费侧断言承载(A2 AC 前言口径)。
  • A2 事件捕获测试 API:冒烟经 signal connect 到捕获数组(AC 前言"事件捕获断言面"的落地形态)。

Alternatives Considered

Alternative 1: autoload 全局事件总线

  • Description: 中心 autoload 单例持有全部具名信号,系统经总线收发。
  • Pros: 解耦发射者/订阅者;全局可发现。
  • Cons: 全局耦合(任何系统可达任何信号——与"依赖单向"原则冲突);单线程回合制无此需求;事件溯源变难。
  • Rejection Reason: 过度设计;切片直连形态已验证且更简单。

Alternative 2: 轮询(B1 每帧查 A3 相位)

  • Description: B1 在 process 中轮询 A3 相位观测量。
  • Cons: 延迟(帧粒度)+ 空转;与"回合制无逐帧玩法逻辑"原则冲突。
  • Rejection Reason: 无收益;信号已同步即时。

Alternative 3: 回调注册表(A2 持有 handler 列表)

  • Description: A2 暴露 register(handler) API,非 signal。
  • Cons: 非 GDScript 惯用;多订阅者接线繁琐;丢掉引擎信号工具链(.tscn 对接、调试器可视)。
  • Rejection Reason: signal 即本用途的引擎原生方案。

Consequences

Positive

  • 事件生命周期确定性优于 Node 方案:RefCounted 引用计数归零即双向自动断连,无 queue_free 帧末窗口(specialist 核实)——不存在"向半死对象发射"
  • 组合根接线使依赖方向可视化、可审计;切片 88/88 已验证

Negative

  • 组合根承担全部接线职责——视图单元增多时接线代码线性增长(切片 main.gd 433 行已容纳;演进为装配器函数分组即可)
  • 信号同步发射的再入语义需实现者理解(本 ADR 的"不依赖到达序"即防御)

Risks

  • lambda 捕获 self 的循环引用:GDScript lambda 捕获持强引用——短命 RefCounted 以捕获 self 的 lambda 连到长命发射者会延长生命周期乃至成环,自动断连永不触发 → 缓解(固化为本 ADR 静态规则):core↔core 信号连接只许方法 Callable;发射者禁止持有对订阅者的反向引用
  • 信号参数无运行时类型检查 → 缓解:消费侧断言(既有 AC 口径)+ 呈现载荷 schema 成文义务(code-conventions)

GDD Requirements Addressed

GDD System Requirement How This ADR Addresses It
a2-minesweeper-rules.md Core Rule 2 三事件回边契约(发射面封闭);OQ2 事件外发载体 signal 载体 + 实名映射(round_end→round_end_board)+ 组合根接线;捕获 API 承载 AC 断言面
b1-settlement.md Core Rule 2 结算触发 = Waiting 相位编排点;OQ1 事件总线载体 订阅 A3 相位触发、只读 last_round_end、不依赖到达序
a3-action-economy.md Rule 9 round_end 双发射源(stopped 直发、payload 同构) round_end_stopped 独立信号,同构 payload 由组包纪律保证
systems-index.md 唯一回边裁决(A2 只外发、B4/B1 订阅) 组合根接线拓扑 = 裁决的落地形态

Performance Implications

  • CPU: 信号发射 = 同步调用链,回合制下每事务个位数次发射——可忽略
  • Memory: 方法 Callable 弱引用目标、不延长生命周期;lambda 禁令消除成环风险
  • Load Time: 无
  • Network: 不适用

Migration Plan

切片即本决策的实现(core/minesweeper_rules.gd 三信号、action_economy.gd round_end_stopped、battle_orchestrator.gd 双源汇聚)——无迁移,正式化 = 把接线纪律与静态规则写入扫描域(ADR-0004)。

Validation Criteria

  • A2/B1 冒烟事件捕获断言面全绿(既有 88 条覆盖)
  • 静态扫描:core↔core 连接零 lambda;发射者零反向引用(ADR-0004 扫描域承载)
  • 回放确定性:同种子同输入步骤流逐行相同(B1 AC-33 先例,切片已证)
  • ADR-0003(场景结构与组合根)、ADR-0004(测试载体与静态扫描域)
  • docs/architecture/architecture.md(Data Flow §2 事件路径)