AgentSkillsCN

data-design

在设计数据结构、数据库模式、状态管理或数据关系时使用——涵盖单一数据源、完整数据流思维。

SKILL.md
--- frontmatter
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: 先设计数据结构再写代码

流程:

  1. 画出数据关系图
  2. 明确每层数据有哪些字段
  3. 标注哪些字段可能缺失
  4. 设计映射函数时考虑上下文传递

示例:

code
Event (父级)
  ├── id
  ├── slug
  ├── title
  └── markets[] (嵌套)
        ├── id
        ├── eventId  ← 可能缺失!需要从父级传入
        └── question

代码体现:

typescript
// 映射函数接收父级上下文
function mapToMarket(raw: MarketRaw, parentEventId?: string): Market {
  return {
    eventId: raw.eventId || parentEventId,  // 处理缺失
    // ...
  }
}

原则 2: 完整思考数据流(生产到消费)

不只考虑「获取端」,还要考虑「使用端」的真实场景。

思考框架:

code
数据从哪来 → 存到哪 → 在哪展示 → 用户如何交互

示例:Activity 页面的 Event 筛选

  • 获取端:activities 表有 event_slug 字段
  • 存储:events 表存储 event 详情(title、icon)
  • 展示:筛选下拉框需要显示 title 和 icon
  • 问题:events 表可能还没同步某些 slug

解决:设计降级策略

typescript
// 已关联的 events
const existingEvents = await db.from('events').select('*').in('slug', slugs)

// 未关联的 slugs,创建占位对象
const missingSlugs = slugs.filter(s => !existingEvents.has(s))
const placeholders = missingSlugs.map(slug => ({
  slug,
  title: null,  // 降级显示 slug
  icon: null,
}))

return [...existingEvents, ...placeholders]

原则 3: 单一数据源

复杂状态变更应有唯一的控制点,避免多处同时修改导致竞态。

反模式:

typescript
// ❌ 子组件内协调多个状态变更
onSelect={() => {
  onLeaderChange(value)   // 触发状态变更 A
  onEventChange(null)     // 触发状态变更 B(可能竞态!)
}}

正确模式:

typescript
// ✅ Container 统一管理,子组件只触发单一回调
// Container
const handleLeaderChange = useCallback((address) => {
  setUrlState({ page: 1, leader: address, event: null })  // 一次性更新
}, [setUrlState])

// Filter 组件
onSelect={() => onLeaderChange(value)}  // 只触发一个回调

Reference