| name | data-design |
| description | Use when designing data structures, database schemas, state management, or data relationships - covers single source of truth, complete data flow thinking |
数据设计
Overview
设计数据结构、数据库 schema、状态管理时必须遵循的原则。
核心原则: 先设计再编码,完整思考数据流,保持单一数据源。
When to Use
触发场景:
- 设计新的数据库表结构
- 设计前端状态管理方案
- 处理多个数据源之间的关系
- 级联筛选、关联查询等复杂数据交互
- 遇到状态不一致、竞态条件问题
原则 1: 先设计数据结构再写代码
流程:
- 画出数据关系图
- 明确每层数据有哪些字段
- 标注哪些字段可能缺失
- 设计映射函数时考虑上下文传递
示例:
Event (父级)
├── id
├── slug
├── title
└── markets[] (嵌套)
├── id
├── eventId ← 可能缺失!需要从父级传入
└── question
代码体现:
function mapToMarket(raw: MarketRaw, parentEventId?: string): Market {
return {
eventId: raw.eventId || parentEventId,
}
}
原则 2: 完整思考数据流(生产到消费)
不只考虑「获取端」,还要考虑「使用端」的真实场景。
思考框架:
数据从哪来 → 存到哪 → 在哪展示 → 用户如何交互
示例:Activity 页面的 Event 筛选
- 获取端:activities 表有
event_slug 字段
- 存储:events 表存储 event 详情(title、icon)
- 展示:筛选下拉框需要显示 title 和 icon
- 问题:events 表可能还没同步某些 slug
解决:设计降级策略
const existingEvents = await db.from('events').select('*').in('slug', slugs)
const missingSlugs = slugs.filter(s => !existingEvents.has(s))
const placeholders = missingSlugs.map(slug => ({
slug,
title: null,
icon: null,
}))
return [...existingEvents, ...placeholders]
原则 3: 单一数据源
复杂状态变更应有唯一的控制点,避免多处同时修改导致竞态。
反模式:
onSelect={() => {
onLeaderChange(value)
onEventChange(null)
}}
正确模式:
const handleLeaderChange = useCallback((address) => {
setUrlState({ page: 1, leader: address, event: null })
}, [setUrlState])
onSelect={() => onLeaderChange(value)}
Reference