返回履历

Conflux · DApp Hub · DDD and Derived State

Conflux Hub

按业务领域组织状态,让 Stake、Vote 与 Portfolio 共用同一套事实

Conflux Hub 是链上工具门户:用户质押并锁定 CFX 获得票权,参与治理投票,再到 Portfolio 查看资产与质押汇总。我按业务领域组织共享状态,让这几处相互关联的功能读取一致的数据,又能各自扩展。

5 领域
Account / Chain / Governance / Token / Assets
状态分离
共享事实放在 service,页面交互留在 route
按需派生
资产总值、Stake、票权与历史轮由事实计算
3 页面
Stake / Vote / Portfolio 组合共享领域

9 章

01

Stake、Vote 与 Portfolio 共用同一套基础

用户在 Stake 质押 CFX,并通过锁仓获得票权;到 Vote 选择治理轮次,对链上参数调整投票;Portfolio 则把持仓价值、质押和票权放在一起查看。质押变化会影响票权与汇总,三个页面不能各算各的。我把共同依赖的账户、网络、资产与治理数据集中组织,切换页面时沿用已有连接和数据上下文。Hub 也提供跨链等工具,本页选取这三个相互关联的任务展开架构。

  • 页面切换沿用当前账户与网络上下文
  • Core Space Base32 与 eSpace Hex 在领域边界归一
  • 授权、交易反馈与错误判断使用共用能力

02

DDD:domain 管事实,页面做组合

src/service/account、chain、governance、token、myAssets 分别维护可跨页面使用的业务事实和操作;只属于某个页面的表单、筛选和弹窗状态留在对应 route/-service。Stake、Vote、Portfolio 通过 hooks 与 actions 使用领域能力,不各自保存资产、Stake 或票权副本。

  • 共享领域:account / chain / governance / token / myAssets
  • 页面局部状态:表单、筛选与交互流程留在 route/-service
  • 页面树组合:route 读取账户和治理汇总,MyAssets 子组件读取资产

03

myAssets:查询与派生 → Portfolio:页面组合

共享 domain 内部也分层:query atom 用 network、space、account 等稳定 key 取得事实,derived atom 只基于已有事实计算结果,hooks 与 actions 再向页面提供能力。myAssets 中,assetsFamily 查询资产列表,assetsValueFamily 派生单项价值、总值与占比。Portfolio 的 route 读取账户和治理汇总,交给账户、质押与票权组件;MyAssets 子组件直接调用资产 hooks。这是整棵页面树组合多个领域,不是把所有查询塞进一个 route。atomFamily 按值比较参数,避免同一个业务请求因对象引用不同而生成重复状态。

  • assetsFamily → 查询并缓存资产列表
  • assetsValueFamily → 派生价值、总值与占比
  • MyAssets → useAssets / useAssetsValue
  • Portfolio route → account / governance → 账户、质押与票权组件

04

DDD:事实唯一、能力复用、变化隔离

每类业务事实只有一个负责人,Stake、Vote、Portfolio 直接复用领域结果;当 Portfolio 从 Core Space 切到 eSpace 时,当前账户、资产列表与票权汇总会沿依赖关系重新计算,历史 Governance 缓存和 Stake 页面草稿等无关状态不必一起重置。

  • 事实唯一:页面不会各自维护一份账户、资产或票权
  • 能力复用:新页面组合已有 query、derived atom 与 action
  • 变化隔离:只更新真正依赖 account / space 的状态

05

加载稳定:缓存管数据,Skeleton 管布局

打开质押页时,金额还没返回,卡片先为金额和操作区留出空间;数据到达后,占位换成金额,下面的操作入口不被挤走。影片用同尺寸的小例子展示这个变化。缓存解决的是另一个问题:compareCacheAndUseRightData 同时请求持久化缓存和远端,先用可用数据;缓存先到时,远端结果只有存在差异才替换。persist-* query 用 BigInt-safe JSON 保存,网络 ID 进入 query key。Round Selector、Stake 卡片和资产行分别复用各自的占位尺寸,让等待更短与布局稳定各有负责的机制。

  • 缓存与远端并发,避免每次冷白屏
  • 只有 persist-* query 进入持久化层
  • Skeleton、真实内容与容器使用同一套稳定尺寸

06

Governance:一份状态,三个页面复用

Governance 不是只输出一个所有页面通用的汇总对象。Stake 读取质押明细,并通过领域 actions 执行质押、解押和锁仓;Vote 读取票权与轮次,再提交投票;Portfolio 读取派生后的质押、锁仓与票权汇总。Core 的汇总包含 PoW 与 PoS,eSpace 则汇总 PoS。共用领域让这些不同视图源于同一套事实,而不是三个页面互相同步副本。

  • Stake:质押明细 + stake / unstake / lock actions
  • Vote:票权 + 轮次 + 投票操作
  • Portfolio:读取 Stake、Lock 与 Voting power 汇总

07

Governance:一轮结果需要多份链上事实

页面要同时展示 Current Value、Coming Effective 和用户选中的历史轮,但链上没有一份直接可用的“完整轮次对象”。系统先读取 currentRound,再按网络配置计算该轮 start / end block;历史查询要把 block 转成 epochTag;totalVotes 返回每个主题的 Increase / UnChange / Decrease,posStakeForVotes 提供最低有效票权基数,cfx_getParamsFromVote 只给出当前执行参数锚点。最终卡片是这些来源共同计算出的投影。

  • 参数在第 6、12 轮后还会新增主题,页面需兼容不同历史结构
  • 有效投票低于 PoS stake 的 5% 时,参数保持原值
  • 当前轮每 30 秒刷新,历史轮使用持久化 query key

08

Governance History:从锚点与票数推算历史轮

用户切换旧轮次时,前端从已执行参数这个锚点出发,结合各轮历史票数与 PoS 基数向目标轮逐步推算。中间参数与历史查询分别进入 execParamsCache 和 persist-* 缓存,重复回看时复用已有结果,不必每次从头查询和计算。

09

复杂度没有消失,但有了明确负责人

集中领域 atom 不会自动消除复杂度:前端仍要理解 Core / eSpace 调用语义、Base32 / Hex 转换、缓存新鲜度、历史区块查询、治理公式和交易边界。真正的收益是这些复杂度都有明确负责人和数据路径:domain 维护事实,页面组合能力,资产、锁仓、票权与历史轮结果按需派生并可以恢复。新增功能可以沿既有边界继续扩展,而不必在页面里重新理解整套协议。

  • 共享事实各有唯一负责人
  • 产品页面只保存局部交互并组合领域能力
  • 用户结果可以按需派生、缓存和恢复

代码与证据

证据边界:本页依据本地 conflux-hub 源码与关键提交 diff 整理。页面能够证明架构、数据流和实现边界,不把未执行的真实链上写入、用户规模、流量或商业结果写成已验证成果。