前后端功能开发架构方案
1. 背景与范围
本方案沉淀 Project2 关于 C# 游戏服务端路线、Unity 客户端功能分层、共享契约、权威状态同步和高频操作处理的长期结论。它用于指导后续功能设计和迁移,不表示所有目标能力已经落地。
本方案覆盖:
- 以完整固定源码基线独立维护 Wenjing C# 服务端框架;
- 上游 ET / 历史 Project2 ET 候选的关系和变更吸收方式;
- 策划、服务端和客户端之间的共享边界与契约生成;
- 服务端权威状态、RPC、
OperationId、ChangeSet和主动推送; - Unity 客户端的
Command / Handler / Application / Presentation / View分层; - 快速连续操作、重试、乱序、重复回包和断线恢复;
- 以 Lottery 为首个迁移样板的开发、验证和验收路径。
不在本方案范围内:
- 宣布 Wenjing 已可生产、批准切服或替换当前线上后端;
- 迁移生产数据、修改凭据、权限、部署或运行时;
- 把服务端权威逻辑、数据库模型或管理接口发布到 Unity;
- 在未登记仓库和未审核协议的情况下直接开始跨仓实现。
2. 已确认决策
2.1 服务端路线
- Project2 的长期 C# 服务端目标路线是独立维护的 Wenjing,而不是长期跟随一个可自动更新的 ET fork。
- Wenjing 使用完整、固定版本源码复制形成自包含仓库。编译、测试和部署不得依赖外部框架 checkout、Git submodule、隐藏补丁队列或 post-checkout 源码物化。
- 仓库采用 Wenjing 白标身份和自主版本线。外部实现只能作为评估输入;需要的行为通过普通 Wenjing MR 有选择地移植,并补齐动机、测试和兼容性证据。
project2-et是过渡 / 历史候选,不是目标主线;它的实现和压测结论可以作为证据,但不能重新成为产品架构权威。- 切换 Project2 当前后端必须是单独的 producer-reviewed 迁移决策。架构选型、代码存在、MR 合并、测试通过都不等于已批准切服。
2.2 权威状态与双端职责
- 服务端是资产、抽奖、体力、任务进度、角色成长、支付和其他业务结果的唯一权威。
- Unity 只共享 wire contract、错误语义、客户端安全 DTO、生成代码和经过审核的纯计算;不共享数据库文档、权威 Handler、密钥、反作弊决策或管理操作。
- 一次写操作由稳定
OperationId标识。客户端重试同一逻辑操作时复用原 ID;新的玩家意图必须使用新 ID。 - 写 RPC 的成功结果和服务端主动变化都进入同一
ChangeSet应用通道。OperationId负责命令幂等,ChangeSetId + domain revision负责状态去重和乱序保护,两者不可混用。 - 客户端展示必须消费已经应用的权威结果快照;动画跳过、界面关闭或重开不能改变业务结果。
2.3 Unity 功能分层
目标依赖方向固定为:
View
-> Command
-> CommandHandler
-> RPC / local application use case
-> Application ChangeSet Handler + Model/Store
-> Presentation
-> View
View采集输入和渲染,不改业务状态。Command是不可变玩家意图,不持有控件、界面实例或可变 Model。CommandHandler负责用例编排、客户端体验级前置检查、调用 RPC 和取消/超时处理。Application负责接收、校验、去重并应用权威状态,不直接播放 UI 或特效。Presentation把已经提交的应用层结果变成动画、音效、弹窗和导航序列。View只订阅 ViewModel / Presentation 输出并刷新。
迁移采用按功能渐进替换,不要求一次性推翻现有 CommandEvent。
3. 当前事实与落地状态
以下状态用于防止把讨论、分支或候选环境结论写成主干事实。
| 表面 | 当前事实 | 结论 |
|---|---|---|
| Project2 Workspace 路由 | repos.yaml 当前登记的 backend 仍是 project2-go,base branch 为 master | 普通 Project2 后端实现仍必须路由到 project2-go;Wenjing 未登记前不得替代该路由 |
| Wenjing 主干 | wenjing-server-framework/master 为 ce4ca1a,单个 bootstrap commit 已包含完整自包含框架、Project2 业务包和生产候选门禁 | 完整源码独立建仓已落地;“已可生产”未成立 |
| Wenjing 固定基线 | eng/source-baseline.json 记录 framework source 5cab01f7...、Project2 source 047cb8b...、independent-full-source、无 Git relationship,并物化 14 项修复 | 固定基线与自主维护策略已有仓库证据 |
| Wenjing 共享契约样板 | 开放 MR wenjing-server-framework!1 HEAD 4f72a7d 只新增 Role Summary 双端样板,尚未合入 master | 样板是已完成候选实现,不是主干能力 |
| Wenjing contract | eng/contracts.lock.json 的 authority 为 project2-config,review status 仍是 migration-snapshot-pending-planning-promotion | 当前快照不可冒充已提升的 planning canonical contract |
| Wenjing 性能 | 2026-08-03 target-lab 通过 10,000 connected sessions;steady command 各 shard p95 为 138–158 ms,超过 50 ms 门槛,并复现 ERR_PeerDisconnect (100208);burst / fault gate 未运行 | 不得声称满足行业性能、production candidate 或 cutover 条件 |
| 历史 ET 候选 | project2-et/master 为 047cb8b,包含过渡期实现和候选验证记录 | 仅作迁移与问题证据,不作为目标框架或 Project2 Workspace 正式 backend |
| Unity 主干 | frontend origin/develop 为 66dcd691...,已有 CommandEvent、Lottery Command / Model / UI、BuildAndPresentAsync 和单 Command 生命周期 | 当前命令仍兼有编排、网络和部分表现职责;不能把目标五层结构视为已落地 |
| Lottery 分层候选 | frontend MR !1871 HEAD ffd4657 仍为 opened / cannot_be_merged | Application / Presentation / immutable snapshot / Coordinator 候选不得写成 develop 事实 |
| 体力同步 | 当前可从角色初始数据、战斗回包、NoticeItemChanged、奖励回包和客户端局部增量等多条路径更新 | 已有功能,但缺少统一 ChangeSet、revision 和单一应用入口 |
因此,目标架构的首要前置不是继续扩写实现,而是先完成仓库纳管、契约权威提升和一个可合并的端到端样板。
4. 目标架构
4.1 逻辑组件
Planning contract source
-> reviewed schema / IDs / config semantics
-> deterministic server + client generation
Unity View
-> immutable Command
-> CommandHandler (operation policy)
-> generated RPC client
-> Wenjing authoritative actor/handler
-> transaction + idempotent receipt
-> OperationResult(ChangeSet, domain result)
-> Application ChangeSet pipeline
-> Model/Store revision advance
-> immutable Presentation snapshot
-> Presentation Coordinator
-> View rendering
Wenjing server-originated change
-> Push(ChangeSet)
-> same Application ChangeSet pipeline
4.2 依赖红线
View不得调用协议、直接写 Model 或决定权威成功。Presentation不得扣资源、写保底、补发奖励或触发第二次权威请求。Application不得引用具体 Panel、Prefab、Animator 或音频资源。CommandHandler不得通过客户端预测生成最终奖励。- 服务端不得信任客户端上传的消耗后余额、抽奖结果、体力最终值或版本号。
- 生成代码不得人工修改;源 contract 和生成器版本必须进入 lock / hash 证据。
5. ET / Wenjing 上游同步策略
“完整固定版本复制”意味着主动放弃自动 upstream merge 的便利,以换取可审计、可白标、可独立发布的源码边界。维护流程如下:
eng/source-baseline.json锁定引入来源、版本、工具链和导入日期;导入后不保留 Git ancestry 或运行时源码依赖。- 不提供定时 mirror、自动 merge、补丁重放或“构建时拉上游”路径。
- 对安全公告、缺陷修复和能力升级建立人工 intake:说明问题、适用版本、许可证 / notice、行为差异和 Project2 影响。
- 在 Wenjing 分支重写或移植最小行为,使用 Wenjing namespace/package/diagnostic identity,并补充聚焦测试。
- 运行品牌边界、Debug/Release build、Robot、协议兼容和与变更风险相称的 production-candidate gate。
- 通过 producer-reviewed MR 合入;更新 Wenjing 自身版本、变更日志、SBOM、baseline / fix registry,而不是伪装成一次“上游同步”。
替代方案是保留可追踪 ET fork 并周期 merge。它适合希望快速跟进 ET 且能承受上游冲突、品牌泄漏和发布耦合的团队;与当前“白标、自主维护、固定基线”决策不兼容,除非制作人明确改变维护模型。
6. 前后端共享边界和契约生成
6.1 当前权威约束
Project2 Workspace 当前规定:配置、字段、默认值、ID、proto 和语言归 planning。Wenjing 的 contracts.lock 也把 project2-config 标记为 authority,但快照尚待 planning promotion。
因此当前合法路径是:
project2-config reviewed contract
-> deterministic export / generation
-> Wenjing server generated messages
-> Unity client-safe generated package
Wenjing MR !1 中“server domain proto -> client runtime public copy”的做法可作为技术样板,但在 Project2 正式采用前必须解决与 Workspace planning ownership 的一致性。若要把物理 proto 源迁到 Wenjing,必须先通过 Project Workspace / repos.yaml / planning migration review 改变权威路由,不能靠代码分支既成事实。
6.2 可以共享
- 请求、响应和推送 wire DTO;
- 稳定消息 ID、错误码、枚举和兼容版本;
OperationId、ChangeSetenvelope 和 typed domain change;- 客户端安全只读投影;
- 无 Unity / DB / 网络副作用的确定性纯计算;
- 生成器、schema hash、生成产物 hash 和兼容性测试。
6.3 禁止共享
- MongoDB / SQL 文档、持久化索引和迁移内部;
- 服务端 Handler、Actor aggregate、会话 lease 和管理员能力;
- 凭据、签名、支付验证、反作弊和风控规则;
- Unity View、输入、Prefab、动画、资源引用和设备能力;
- 为减少重复而把服务端权威算法放进客户端。
6.4 生成门禁
- 单一 reviewed source,禁止双端分别手改 proto。
- 兼容变更只追加字段 / ID;禁止复用已发布 ID 或改变旧字段语义。
- CI 重生成后必须是 clean diff,或显式提交全部确定性生成结果。
- client package 必须经过 allowlist,证明不含 server-only assembly / source。
- lock 文件记录 source commit、source path、generator version、source hash 和 generated hash。
7. 权威状态、ChangeSet、RPC 与 OperationId
7.1 基本契约
推荐公共 envelope:
OperationRequest
operation_id
expected_revision? # 只在需要并发前置条件的操作使用
domain_request
OperationResult
operation_id
outcome # committed / rejected / duplicate
error
change_set?
domain_result? # 例如抽奖展示顺序;不替代状态变更
ChangeSet
change_set_id
origin_operation_id? # 服务端主动变化可以为空
server_time
domain_changes[] # typed, versioned, authoritative post-state
不要做“字符串路径 + 任意值”的万能补丁。公共部分只统一 envelope;资产、体力、英雄、任务和 Lottery 使用强类型 domain change。
7.2 幂等和顺序
- 服务端对已接受写操作持久化 receipt:相同
OperationId + canonical payload返回相同结果;相同 ID 不同 payload 必须 fail closed。 OperationId只去重请求,不能判断 push 是否已应用。- 每个 domain aggregate / entity change 带单调 revision。客户端只应用高于当前 revision 的 post-state;相同 revision 可安全忽略。
- 同一结果可能先通过 push 到达、后通过 RPC 返回,或反过来;两条路径必须汇入相同 application handler。
- 跨 domain 原子业务由一个服务端事务提交,再发布一个含多个 typed change 的 ChangeSet;客户端不得先展示部分成功。
- 超时只代表客户端未确认结果,不代表服务端失败。重试必须复用原
OperationId,或先查询 receipt。
7.3 服务端主动变化
邮件到达、运营补偿、自然结算、跨设备操作或后台任务等没有当前客户端 Command 的变化:
- 使用新的
ChangeSetId,origin_operation_id为空或记录原始后台操作; - 通过可恢复 push / inbox 投递;断线后按 revision 补齐或请求 snapshot;
- 业务 UI 只响应 application store 提交后的 domain event;不直接在网络 packet callback 内操作 View。
8. 体力同步样板
体力是统一状态管线的最小可验证样板,因为它同时包含自然恢复、战斗消耗、购买、广告奖励、其他奖励和服务端推送。
推荐权威状态:
StaminaState
base_value
recovery_started_at
buy_count
ad_count
revision
server_time
规则:
- 登录 / 重连返回权威 snapshot。
- 战斗开始、购买体力、广告奖励和道具奖励都返回包含
StaminaChanged(post_state)的 ChangeSet。 - 客户端可以根据
base_value + recovery_started_at + server_time做显示投影,但不能把逐秒显示值写回权威 Model。 - 任何服务端 push 使用同一
StaminaChanged;Application 按 revision 接受或忽略。 - 客户端不再对权威路径调用
AddPhysicalValue/Increment*进行猜测性提交。迁移期若保留旧接口,必须限定为 legacy adapter 并有一致性断言。 - 回前台、重连、时钟漂移过大或 revision 缺口时请求 snapshot,不用连续 delta 猜测补偿。
验收必须覆盖:购买与 push 乱序、请求超时后重试、满体力停止恢复、奖励使体力超过上限、跨设备修改、断线期间自然恢复和服务器时间漂移。
9. 客户端快速操作策略
“响应快”不等于允许所有写操作并发。按业务语义选择 lane policy:
| 操作类型 | 默认策略 | 示例 |
|---|---|---|
| 经济 / 奖励 / 抽奖写操作 | 同一 aggregate lane 串行;按钮可立即反馈,下一操作有界排队或拒绝 | 抽奖、购买、领奖、强化 |
| 可合并设置 | latest-wins / coalesce,服务端只提交最终值 | 音量、画质、跳过动画偏好 |
| 独立对象操作 | 按 entity key 分 lane,可并行不同对象 | 不同邮件标记、不同装备查询 |
| 只读查询 | 有界并发、取消旧请求、缓存和 stale-while-revalidate | 列表、详情、预览 |
| 纯表现 | 本地立即执行,可取消;不生成权威状态 | 点击反馈、过渡动画 |
关键规则:
- lane key 至少包含 domain / role / aggregate 或 entity,不使用一个全局 Normal busy 状态阻塞所有功能。
- 每次真正被接受的玩家意图创建一个
OperationId;网络重试不创建新 ID。 - 经济操作默认不 optimistic commit。可以先播放按压、loading 或骨架屏,但余额和奖励以 ChangeSet 为准。
- 有界队列必须可观测并设上限;队列满时给明确反馈,不能静默丢操作。
- 取消只取消客户端等待 / 表现。已被服务端接受的操作仍需通过 receipt / ChangeSet 对账。
- 快速十连点击等同一业务应由产品明确选择“忽略、排队一个、全部排队”之一,并在客户端和服务端共同测试。
现有 CommandEvent 按 System / Normal / Background 组控制并发,且同组忙碌时会拒绝不同命令类型。迁移时保留 middleware、日志和跟踪价值,但把并发策略逐步下沉为显式 domain lane,避免全局互相阻塞。
10. Command / Handler / Application / Presentation / View 分层
10.1 Command
- 不可变参数;表达“玩家想做什么”。
- 不包含网络响应、Model 快照、Panel 或动画对象。
- 只在真正开始一个写意图时创建
OperationId;展示类本地 Command 不需要业务 OperationId。
10.2 CommandHandler
- 解析 lane policy,执行非权威 UX 前置检查。
- 调用 generated RPC client,处理超时、取消和 receipt query。
- 把 RPC / push 的 ChangeSet 交给 Application;不直接修改 UI。
- 返回
ApplicationResult或 immutable presentation input。
10.3 Application
ChangeSetInbox:统一接收 RPC 和 push。ChangeSetDeduplicator:按 change ID 和 revision 去重。- typed handlers:更新 domain store 并发布已提交的 application event。
- 不引用 Unity View / Animator;可在纯 C# 测试中运行。
10.4 Presentation
- 输入是 immutable snapshot / result token,不读取不断变化的 live Model 来还原历史结果。
- Coordinator 负责动画顺序、跳过、关闭、重开和取消;任何路径最终都只影响展示完成状态。
- Presentation 失败要记录并允许安全降级为静态结果,不回滚服务端提交。
10.5 View
- 显示状态、收集输入、派发 Command。
- 不调用
SendRequestAsync,不写 Model,不在点击回调里计算奖励或扣资源。 - 订阅和解绑成对;关闭界面不会取消已提交的业务结果。
11. Lottery 功能样板
11.1 当前主干事实
frontend origin/develop@66dcd691 已有:
HeroNormalLotteryCommand等 Command 直接组包并调用 RPC;LotteryResponseProcessor.BuildAndPresentAsync在同一 Command 生命周期构建结果并驱动LotteryResultPresentationFlow;- Lottery Model、UI、GM、红点和部分测试。
这是相对“回包后再次派发 Command”的改进,但仍把网络编排、Application 状态提交和 Presentation 连接在一个流程内。MR !1871@ffd4657 的进一步分层尚未合并,因此只能作为候选设计输入。
11.2 目标闭环
View: 点击普通十连
-> DrawLotteryCommand(pool, count=10)
-> DrawLotteryCommandHandler
- lane = lottery:<role>
- operation_id = new logical operation
- optional non-authoritative affordability hint
-> DrawLottery RPC
-> server actor
- authenticate + validate config/open state
- idempotency receipt lookup
- transaction: cost, pity, rewards, roster/inventory
- commit receipt + ChangeSet + ordered reveal result
-> Application applies Inventory/Lottery/Hero changes once
-> LotteryPresentationSnapshot immutable copy
-> LotteryPresentationCoordinator
-> View animation / skip / static fallback
11.3 Lottery 强制规则
- 随机、保底、消耗和奖励全部服务端权威。
domain_result可以包含经审核的揭示顺序和表现标签,但资产入库只认 ChangeSet。- 相同
OperationId重试返回相同抽奖结果,不二次扣费、不二次累计保底。 - RPC 回包和库存 push 乱序时只应用一次;Presentation 使用本次 operation 的 immutable snapshot,不从 live inventory 猜抽奖结果。
- 玩家跳过动画、关闭界面、切后台或断线都不能改变已提交结果;重开后可从 receipt / presentation record 恢复静态结果。
- 失败结果不得播放成功动画;表现异常不得触发补发或第二次抽奖。
11.4 样板验收
- 单抽、十连、百连和高级抽取主流程;
- 资源不足、卡池未开放、版本不兼容;
- double tap、队列上限、超时重试和相同 ID 不同 payload;
- RPC / push 两种到达顺序和重复投递;
- 跳过、关闭、重开、切后台和 Presentation 异常降级;
- 断线后 receipt 查询和 ChangeSet revision 对账;
- 服务端 Robot + client pure application tests + Unity EditMode / PlayMode + UI smoke。
12. 正常功能开发流程
- Planning 先行:确认功能规则、配置语义、ID、错误语义、权威状态、快速操作 policy 和验收矩阵。
- 契约评审:定义 request / result、typed changes、revision、兼容策略和生成 source;更新 lock/hash。
- 服务端实现:Actor / Handler、校验、事务、idempotent receipt、ChangeSet、push / reconnect,并补 Robot 与故障测试。
- Application 实现:纯 C# ChangeSet handler、去重、乱序、snapshot 和 reconciliation 测试。
- CommandHandler 实现:lane、超时、重试、取消和错误映射。
- Presentation 实现:immutable input、Coordinator、跳过 / 恢复 / 降级。
- View 接入:只派发 Command、渲染和采集交互。
- 集成验证:生成一致性、server/client contract、Robot、Unity tests、网络乱序 / 重复 / 断线、UI smoke。
- 分支与审核:每个 owning repo 使用 fresh-base branch + MR;跨 repo 以同一功能结果协调,但每个 MR 保留独立验证与回退证据。
- 发布决策:MR 合并不等于运行时激活、数据迁移或切服;这些必须使用单独的 producer gate。
13. 验证与验收门禁
13.1 Contract
- source / generated hash 一致,生成后 clean;
- 新旧客户端兼容矩阵和 unknown field 行为;
- server-only allowlist / client package 泄漏检查;
OperationId冲突、重复和 receipt 持久化测试。
13.2 Server
- domain unit / Robot / transaction rollback;
- duplicate debit、negative inventory 和 accepted-command loss 必须为 0;
- reconnect、actor failover、DB failover、push replay 和 revision gap;
- 目标性能门槛不因“单项通过”放宽。Wenjing 必须先修复 p95 和
100208,再完整运行 steady、burst、fault、drain、Mongo failover、backup/restore 和稳定性 campaign。
13.3 Client
- Application 层无 Unity 依赖的纯测试;
- RPC-before-push、push-before-RPC、重复 ChangeSet、旧 revision 和 gap recovery;
- lane 并发、队列上限、取消、重试和界面关闭;
- Presentation snapshot 不随 live Model 漂移;
- Unity EditMode / PlayMode、编译、目标 UI smoke 和关键设备回归。
13.4 Producer acceptance
- 核心玩家流程与失败反馈;
- 资产和进度最终一致;
- 重登 / 跨设备 / 断线恢复;
- MR base/head、验证、残余风险、回退和未完成 gate 可审核;
- 未完成性能、契约 promotion、repo registration 或 runtime approval 时保持 fail closed。
14. 风险与替代方案
| 风险 | 影响 | 控制 / 替代 |
|---|---|---|
| Wenjing 未登记为 Project2 repo | Workspace 无合法实现路由 | 先做 producer-reviewed repo registration;此前只做文档和只读评估 |
| 当前 backend 仍是 Go | 双后端长期并行、行为漂移 | 明确 source of truth,使用 shadow / contract comparison;切换必须单独批准 |
| Planning contract 尚未 promotion | 双端生成可能基于迁移快照 | 先完成 planning review 和 lock 更新,禁止在业务实现中补猜字段 |
| 通用 ChangeSet 过度抽象 | 类型安全下降、迁移成本上升 | 只统一 envelope,domain change 强类型、按样板逐步扩展 |
| 全局 Command busy | 无关操作互相阻塞、快速操作丢失 | 迁移到显式 domain lane 和有界队列 |
| 过度 optimistic update | 资产回滚、动画与权威结果冲突 | 经济操作只做视觉反馈,权威状态等待 ChangeSet |
| 固定源码失去上游自动补丁 | 安全修复滞后 | 建立公告 intake、SBOM / vulnerability audit 和有选择的移植 SLA |
| 10k session 被误解为 production ready | 在已失败 gate 上提前切服 | 保留 machine-readable failed status,完整性能 / 故障 / soak gate 不可跳过 |
| Lottery 一次性大重构 | MR 冲突、难回归、无法合并 | 从 Application inbox + immutable snapshot 小步落地,保留 legacy adapter,逐阶段删除 |
15. 后续路线
Phase 0:纳管和权威确认
- producer review 本方案;
- 把 Wenjing 作为何种 lifecycle / owner 登记到 Project2 Workspace,或明确它由其他 Workspace 管理并建立消费契约;
- 解决
contracts.lock的 planning promotion; - 明确
project2-go、project2-et、Wenjing 的保留、shadow 和退休边界。
Phase 1:最小基础设施
- 定义 typed ChangeSet envelope、
OperationId/ receipt policy、revision / gap recovery; - 建立 deterministic contract export、client-safe package 和泄漏门禁;
- 在 Unity 建立纯 Application inbox / handler / store 测试骨架。
Phase 2:体力样板
- 把体力所有写入收口到权威 post-state ChangeSet;
- 覆盖自然恢复、战斗、购买、广告、奖励、push 和重连;
- 用对账测试证明旧局部增量可删除。
Phase 3:Lottery 样板
- 处理 frontend MR
!1871的冲突并按本方案重切可合并增量; - 完成 server receipt / typed changes、client Application、immutable Presentation snapshot 和 Coordinator;
- 通过双击、重试、乱序、断线和表现恢复矩阵。
Phase 4:规模化迁移
- 按经济风险优先迁移商店、领奖、邮件、装备和任务;
- 将 CommandEvent 全局并发逐步替换为 domain lane;
- 建立模板、静态检查和功能级验收清单。
Phase 5:后端候选与切换评审
- 修复 Wenjing steady latency 和
ERR_PeerDisconnect (100208); - 完整通过 burst、fault、drain、Mongo failover、backup/restore、72-hour soak 和对照验证;
- 在 exact reviewed head 上单独提交数据迁移、回退和 cutover 决策。未经 producer 明确授权,不进入生产切换。