SuperAPI AI Directory

schema-design

Data & analytics

View repository

Updated 10-16 08:49

About this skill

Schema Design

References: Eric Evans, Domain-Driven Design; E.F. Codd's normalization theory; PostgreSQL documentation; MySQL design guidelines; Stripe / GitHub data modeling practices.

Applicable Scenarios

  • Extract entities from PRDs, API contracts, or business rules
  • Design tables, fields, field types, default values, and nullability rules
  • Design primary keys, foreign keys, unique constraints, and check constraints
  • Decide between normalization, denormalization, and redundant fields
  • Refactor existing table structures / split tables / merge tables
  • Output DDL or migration drafts

Core Principles

1. 先表达业务事实,再考虑页面展示
   Schema 是业务的"记忆",不是 UI 的镜像
  1. Clear entity boundaries and explicit relationship cardinality One-to-one / one-to-many / many-to-many / polymorphic must all be explicit

  2. Constraints live at the database layer Facts that must be unique in the business must have a UNIQUE fallback Fields that do not allow NULL must be NOT NULL Amounts that must be positive must have CHECK > 0

  3. Field semantics consistent with API/backend Naming, types, enum values, and nullability rules stay in sync

  4. Migratable, rollback-capable, verifiable First think through how to change / how to revert / how to verify consistency

  5. Three time fields are mandatory created_at / updated_at / business time (such as paid_at)

  6. Use soft deletes with caution deleted_at looks harmless, but it breaks unique constraints


## 标准流程

```text
1. 读取 PRD / API 契约 / 现有 schema
   ↓
2. 识别实体(DDD 视角)
   - 聚合根(Aggregate Root)
   - 实体(Entity)
   - 值对象(Value Object)
   - 关系(Association)
   ↓
3. 画 ER 图(Mermaid 或工具)
   ↓
4. 设计表和字段
   - 命名规范
   - 类型选择
   - 必填 / 默认值
   ↓
5. 设计主键、外键、唯一约束、检查约束
   ↓
6. 标注空值、默认值、枚举、时间字段、审计字段
   ↓
7. 评估规范化与必要冗余
   ↓
8. 输出 schema 说明 + DDL 草案 + 待确认问题

Naming Conventions

表名:
  ✅ orders         (复数)
  ✅ order_items    (子资源 + 复数)
  ❌ Order          (首字母大写)
  ❌ tbl_order      (冗余前缀)

字段名:
  ✅ user_id        (snake_case)
  ✅ created_at     (时间用 _at)
  ✅ is_active      (布尔用 is_)
  ✅ amount_cents   (金额单位明确)
  ❌ userId         (camelCase 混用)
  ❌ time           (含义不清)
  ❌ amount         (单位不明)

主键:
  ✅ id             (bigint / uuid,统一)
  ❌ order_id       (表内自指用 id 即可)

外键:
  ✅ user_id        (引用 users.id)
  ✅ created_by     (引用人,业务语义)

Field Type Selection

Numeric

Business Field Recommended Type Reason
Primary key / ID bigint or uuid int caps at 2.1 billion, not enough
Amount bigint(smallest unit / cents) or numeric(19,4) Never use float / double
Count integer Generally sufficient
Rating / ratio numeric(5,2) Controllable precision

Strings

Business Field Recommended Type Reason
Short identifier (≤32 characters) varchar(64) Leave buffer
Email varchar(255) RFC 5321 upper limit
URL varchar(2048) or text Depending on actual use
Long text text No length limit (PG)
Rich text / HTML text Same as above
Tag array text[](PG)/ JSON(MySQL) Avoids a separate join table (for small data volumes)

Time

Field Recommended Type Note
created_at / updated_at timestamp with time zone Always UTC, frontend converts to local
Date (birthday / deadline) date Without time
Duration (seconds) integer Do not use interval (incompatible across databases)

Enums

-- 推荐:用 varchar + CHECK 约束(演进灵活)
status varchar(32) NOT NULL CHECK (status IN ('draft','submitted','paid','cancelled'))

-- 不推荐:PostgreSQL ENUM 类型
-- 优点:类型安全
-- 缺点:增删值困难、跨库不兼容

JSON

-- 适合:低频查询的扩展字段、配置、metadata
metadata jsonb DEFAULT '{}' NOT NULL

-- 不适合:核心查询字段、需要排序聚合的字段
-- 反例:把 status 塞 metadata,每次查询都要 metadata->>'status'

Primary Key Strategy

方案 A:自增整数(bigint)
  优点:简单、有序、占空间小
  缺点:暴露规模、可枚举(防爬)
  适合:内部系统

方案 B:UUID v4
  优点:不可枚举、分布式友好、无热点
  缺点:占 16 字节、无序导致 B-tree 写放大
  适合:分布式系统

方案 C:UUID v7 / ULID(推荐)
  优点:时间有序 + 不可枚举 + 分布式友好
  缺点:较新(2024 年标准化)
  适合:新项目

方案 D:业务前缀 + 随机 ID
  Stripe 风格:`cus_abc123`、`ord_xyz789`
  优点:可读、可识别类型
  缺点:自定义实现
  适合:对外 API

Relationship Modeling

One-to-many (most common)

-- 订单属于用户
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  user_id bigint NOT NULL REFERENCES users(id),
  ...
);

-- 索引子表的外键(必加)
CREATE INDEX idx_orders_user_id ON orders(user_id);

Many-to-many

-- 用户和角色的多对多关系
CREATE TABLE user_roles (
  user_id bigint NOT NULL REFERENCES users(id),
  role_id bigint NOT NULL REFERENCES roles(id),
  granted_at timestamptz DEFAULT now(),
  granted_by bigint REFERENCES users(id),
  PRIMARY KEY (user_id, role_id)
);

Polymorphic (use with caution)

反模式(多态外键):
  comments(target_type, target_id)  -- 没有 FK 约束,难维护

替代:每种类型独立外键
  comments(post_id, user_id, ...)
  post_comments / user_comments 单独表

Field Design Checklist

Item Checkpoint Counterexample
Naming Business semantics + consistent with API data / info / time
Type Match database capabilities and future growth int for order numbers, float for amounts
Nullability Whether NULL has business meaning Default NULL (unclear semantics)
Default value Affects historical data and migration Adding a default value causes a full table lock
Enum Extension strategy PG ENUM is hard to add values to
Time Create, update, delete, archive Missing updated_at, no timezone
Audit Operator, source, tenant, trace ID When something goes wrong, the cause cannot be found
Length String fields must have a limit text is unlimited → DoS risk
Uniqueness Business uniqueness → database UNIQUE Deduplication only at the code layer

Multi-tenant design

-- 每张租户数据表必有 tenant_id
CREATE TABLE orders (
  id bigserial PRIMARY KEY,
  tenant_id bigint NOT NULL REFERENCES tenants(id),
  user_id bigint NOT NULL REFERENCES users(id),
  ...
  -- 唯一性:复合(含 tenant_id)
  CONSTRAINT uq_order_number UNIQUE (tenant_id, order_number)
);

-- 行级安全(PostgreSQL RLS,可选兜底)
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
  USING (tenant_id = current_setting('app.tenant_id')::bigint);

See details consistency-multitenancy/SKILL.md。

Soft delete strategy

方案 A:deleted_at(软删除时间戳)
  优点:可恢复、可审计
  缺点:破坏唯一约束(同 email 的活跃用户和已删除用户)
  解决:UNIQUE (email) WHERE deleted_at IS NULL  (部分索引)

方案 B:归档表
  优点:核心表干净、性能好
  缺点:跨表查询历史复杂

方案 C:硬删除 + 审计日志表
  优点:核心表清爽
  缺点:不可"恢复"

推荐:核心业务用方案 A + 部分索引,审计敏感用方案 C

Audit fields (recommended for every table)

created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
created_by bigint REFERENCES users(id),
updated_by bigint REFERENCES users(id),
version integer NOT NULL DEFAULT 1  -- 乐观锁

Normalization vs denormalization

默认规范化(3NF):
  - 不重复存储
  - 修改一处不漏
  - 节省空间

反规范化(合理冗余):
  - 冗余字段:order.user_email(避免 join 拿邮箱)
  - 冗余汇总:user.order_count(避免每次 count)
  - 物化视图:报表场景

权衡:
  读多写少 → 可以反规范化
  写多读少 → 严格规范化
  报表 → 用物化视图 / 数据仓库分离

Supporting templates

  • templates/schema-design-template.md — Complete schema design document (background + ER + tables + constraints + indexes + migration + risks)
  • templates/ddl-template.sql.md — DDL standard template

Quality self-check

□ 每张表是否有明确业务实体或关系含义
□ 每个字段是否有类型、空值、默认值、说明
□ 必须唯一的业务事实是否有 UNIQUE 约束
□ 关系是否有 FK 约束或明确的应用层约束说明
□ 枚举、状态、软删、审计字段是否明确
□ 三个时间字段(created_at/updated_at/业务时间)是否齐全
□ 多租户字段(tenant_id)+ 复合唯一约束
□ DDL 与目标数据库类型匹配
□ JSON 字段不承载核心查询
□ 主键策略明确(int/uuid/ULID/业务前缀)
□ 字符串字段都有长度上限
□ 字段命名风格统一(snake_case / camelCase 二选一)
□ 是否列出待确认问题和迁移影响

Common pitfalls

  1. Create tables only based on page fields——Ignoring the business lifecycle, high refactoring cost
  2. Uniqueness only in code——Concurrency / retries / imports produce dirty data
  3. JSON carries core queries——metadata->>'status' full table scan
  4. Default values cause full table locks——MySQL/PG lock the table when adding NOT NULL DEFAULT
  5. API/ORM/DB field names are inconsistent——Repeated rework during integration
  6. Amounts use float——Precision loss, finance doesn't reconcile
  7. Time without timezone——Time confusion for users across regions
  8. PG ENUM type——Hard to add values, painful cross-database migration
  9. Missing updated_at——No clues when troubleshooting
  10. Soft delete breaks unique constraints——The same email cannot be registered again
  11. Reflexive use of char(255)——Storing 1 character still takes 255
  12. Oversized text with no length limit——Attack surface / DoS
  13. Missing audit fields——When something goes wrong, the operator cannot be found
  14. Polymorphic foreign key——No FK constraint, hard to maintain

Collaboration with other skills

上游:
  api-designer → 提供 API 契约 / 资源模型
  product-manager → 提供业务实体 / 生命周期
  ui-ux-designer → 提供页面字段需求

下游:
  index-access-pattern → 根据访问模式补索引
  migration-rollout → 把 DDL 转成可上线迁移
  consistency-multitenancy → 补事务、租户、生命周期约束
  query-review → 验证 SQL 与 schema 配合
  backend-engineer → 实现 ORM 模型

Related references

  • references/schema-design-guide.md — DDD entity identification, normalization theory, PostgreSQL/MySQL type comparison, big-tech modeling paradigms (Stripe / GitHub / Discord), evolution strategies