WHY SAKURA WORKS THIS WAY

Architecture Decisions:公开判断,也公开代价

十项决策以 Problem、Constraints、Naive Approach、Failure、Decision、Trade-offs、Value 七段结构呈现。

WHAT THIS SOLVES

解决什么

把架构结果还原为可讨论的约束与取舍,而不是只展示最终目录。

WHAT THIS COSTS

付出什么

需要持续更新失效假设,并接受某些决策只适用于特定规模和证据阶段。

WHERE IT DOES NOT APPLY

哪里不适用

这些决策不是通用教条;项目规模、团队和平台约束不同,应重新评估。

TEN DECISIONS · SEVEN SECTIONS

为什么这样设计

决策不是最佳实践清单;每项都保留约束、失败方案、代价和适用边界。

01

为什么 Core 不引用引擎

Problem

可复用规则容易被 Unity 类型和生命周期绑死。

Constraints

Core 必须可在无引擎环境验证,平台能力仍需完整表达。

Naive Approach

在 Core 中直接引用 MonoBehaviour、Time 或 Asset。

Failure

迁移到 Godot、Server 或纯测试环境时边界失效。

Decision

Core 保持 no-engine;Unity、Godot 与 Editor 能力进入 Adapter。

Trade-offs / Where Not Apply

增加端口和装配代码;高度平台化能力不做伪抽象。

Value

语义可迁移,平台差异可见且可测试。

02

为什么 Ownership 必须显式

Problem

全局服务和异步任务容易没有明确清理者。

Constraints

场景切换、取消和失败都必须释放订阅与句柄。

Naive Approach

依赖静态单例或对象销毁时碰运气清理。

Failure

重复初始化、残留回调和半提交副作用。

Decision

按 App、Gameplay、Scene 或纯 C# Scope 定义 Owner。

Trade-offs / Where Not Apply

调用方承担更明确的生命周期协议。

Value

清理、取消和故障恢复可以被独立验证。

03

为什么治理要 Architecture as Code

Problem

文档规则会与 147 个包的真实依赖快速漂移。

Constraints

清单、层级、Profile 与计划状态存在多个 Owner。

Naive Approach

只在 README 维护包数量和允许依赖。

Failure

文字仍正确但代码和生成状态已经不同。

Decision

用机器清单、resolver 和 verifier 固化规则。

Trade-offs / Where Not Apply

变更必须同步更多投影与门禁。

Value

架构边界可以在提交前失败关闭。

04

为什么 DI 不是通用服务定位器

Problem

统一容器很容易吞并生命周期和业务访问。

Constraints

替换、测试和装配需要 DI,但业务语义需要清晰入口。

Naive Approach

所有对象都从全局容器按类型解析。

Failure

依赖隐藏、Scope 泄漏且初始化顺序不可见。

Decision

DI 只负责依赖、生命周期、替换、测试和 composition root。

Trade-offs / Where Not Apply

需要维护显式构造和 Facade。

Value

依赖图和生命周期保持可读。

05

为什么 MVVM 不是默认

Problem

把一种 UI 范式用于所有页面会放大小页面成本。

Constraints

页面复杂度、协作规模和状态寿命差异很大。

Naive Approach

要求所有 View 都创建 ViewModel 和完整绑定层。

Failure

样板代码超过业务价值,调试路径变长。

Decision

Direct、Binding、MVVM、Structured 按成本渐进选择。

Trade-offs / Where Not Apply

Framework 需维护多条互操作路径。

Value

简单页面保持简单,复杂页面仍有结构化升级路线。

06

为什么证据必须先于成熟度声明

Problem

代码存在、本地通过和 Production 经常被混为一谈。

Constraints

不同证据由不同系统和提交拥有。

Naive Approach

用测试总数或单次演示概括成熟度。

Failure

读者无法判断结论适用范围和是否同 SHA。

Decision

Designed 到 Unknown 八级独立呈现。

Trade-offs / Where Not Apply

公开文案更克制,状态可能长期显示 Pending。

Value

每个结论都有权威来源和恢复入口。

07

为什么波动事实由 Owner 生成

Problem

包数、路由、文章与 Consumer 证据持续变化。

Constraints

网站需要可读策展,但不能复制第二套事实。

Naive Approach

手工更新页面数字和列表。

Failure

页面与源仓库在下一次变更后分叉。

Decision

源仓导出闭合 JSON,站点 generator 负责公开投影。

Trade-offs / Where Not Apply

生成链和幂等测试成为必要维护面。

Value

网站本身成为可验证的数据驱动工程证据。

08

为什么 Consumer 证据独立于 Framework 状态

Problem

框架自测不能证明真实项目会正确采用。

Constraints

Consumer 可能固定在不同 Framework commit。

Naive Approach

把示例或 README 引用当成消费证明。

Failure

缺少实际编译、运行和生命周期反馈。

Decision

Consumer Matrix 固定双方提交并记录局部验证边界。

Trade-offs / Where Not Apply

证据不会自动随 Framework main 更新。

Value

抽象是否可用由独立项目事实反向校验。

09

为什么能力晋级失败关闭

Problem

Preview Adapter 容易因演示成功被提前写成 Supported。

Constraints

晋级需要文档、测试、Runner、Consumer 与维护承诺。

Naive Approach

实现完成后直接修改营销标签。

Failure

支持面超过可持续验证和维护能力。

Decision

缺少任一权威证据时保持 Preview、Pending 或 Unknown。

Trade-offs / Where Not Apply

成熟度推进更慢,但避免不可兑现承诺。

Value

支持等级与真实维护能力一致。

10

为什么 Domain Framework 最后扩张

Problem

玩法域很容易产生相似但不稳定的公共抽象。

Constraints

Core 只应容纳跨 Consumer 的稳定原语。

Naive Approach

每个新游戏都先创建一个公共 Domain 包。

Failure

模块数量增长快于复用价值,语义重复。

Decision

先复用、再 Bridge,跨多个 Consumer 后才下沉最小 Kernel。

Trade-offs / Where Not Apply

早期 Consumer 会保留更多本地实现。

Value

Framework 优先收敛维护面,而不是追求模块数量。