ERD与API设计的关系
数据库模型(ERD)和API设计是应用架构的两个核心层面,二者应当紧密协作而非各自独立。一个良好的ERD为API设计提供了清晰的资源边界和关联语义;反过来,API的使用场景也应当反馈并优化ERD的结构。
以ERD为起点设计API,可以确保数据层和接口层的概念一致性,减少阻抗失配(Impedance Mismatch)带来的转换成本。
从实体到资源
ERD中的每个实体通常对应一个REST资源。以博客系统为例:
-- ERD定义的核心实体
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email VARCHAR(255) UNIQUE NOT NULL,
username VARCHAR(100) UNIQUE NOT NULL,
bio TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
author_id UUID NOT NULL REFERENCES users(id),
title VARCHAR(300) NOT NULL,
content TEXT NOT NULL,
published_at TIMESTAMP, -- NULL表示草稿
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
post_id UUID NOT NULL REFERENCES posts(id) ON DELETE CASCADE,
author_id UUID NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
对应的REST资源设计:
GET /users # 用户列表
GET /users/{userId} # 用户详情
POST /users # 创建用户
PATCH /users/{userId} # 更新用户
GET /posts # 文章列表
GET /posts/{postId} # 文章详情
POST /posts # 创建文章
PATCH /posts/{postId} # 更新文章
DELETE /posts/{postId} # 删除文章
GET /posts/{postId}/comments # 文章的评论列表(嵌套资源)
POST /posts/{postId}/comments # 为文章创建评论
DELETE /comments/{commentId} # 删除评论(顶层资源路径)
嵌套资源的深度:建议最多嵌套一层(/posts/{postId}/comments),避免过深嵌套(如/users/{userId}/posts/{postId}/comments/{commentId}),过深嵌套使URL难以理解且维护成本高。
高效处理关联数据
从ERD的外键关系出发,设计合理的数据聚合策略:
-- 文章详情API的数据库查询
-- 一次查询获取文章+作者基本信息+评论数
SELECT
p.id,
p.title,
p.content,
p.published_at,
p.created_at,
json_build_object(
'id', u.id,
'username', u.username,
'bio', u.bio
) AS author,
COUNT(c.id) AS comment_count
FROM posts p
JOIN users u ON p.author_id = u.id
LEFT JOIN comments c ON p.id = c.post_id
WHERE p.id = $1
GROUP BY p.id, u.id;
-- 评论列表单独查询(分页加载,按需获取)
SELECT
c.id, c.content, c.created_at,
json_build_object('id', u.id, 'username', u.username) AS author
FROM comments c
JOIN users u ON c.author_id = u.id
WHERE c.post_id = $1
ORDER BY c.created_at ASC
LIMIT $2 OFFSET $3;
字段选择(Sparse Fieldsets):允许客户端通过fields参数指定需要的字段,减少传输数据量:
GET /posts?fields=id,title,author.username,comment_count
分页与过滤设计
-- 基于游标的分页(适合大数据量,避免OFFSET性能问题)
-- 请求:GET /posts?after=cursor_value&limit=20
SELECT p.id, p.title, p.published_at, p.created_at
FROM posts p
WHERE p.published_at IS NOT NULL
AND (p.created_at, p.id) < ($1, $2) -- 游标:上次最后一条的(时间, ID)
ORDER BY p.created_at DESC, p.id DESC
LIMIT $3;
-- 返回响应包含下一页游标
-- {
-- "data": [...],
-- "pagination": {
-- "hasMore": true,
-- "nextCursor": "base64(created_at:id)"
-- }
-- }
-- 复合过滤查询
-- GET /posts?authorId=xxx&status=published&tags=ai,database&sort=created_at:desc
SELECT p.* FROM posts p
WHERE ($1::uuid IS NULL OR p.author_id = $1)
AND ($2::varchar IS NULL OR
(p.published_at IS NOT NULL) = ($2 = 'published'))
ORDER BY
CASE WHEN $3 = 'created_at:desc' THEN p.created_at END DESC,
CASE WHEN $3 = 'created_at:asc' THEN p.created_at END ASC;
API版本管理策略
数据库Schema的演进必然带来API的变化,版本管理策略需要提前规划:
-- 向后兼容的Schema变更(无需新版本)
ALTER TABLE posts ADD COLUMN view_count BIGINT NOT NULL DEFAULT 0;
-- 新增字段,旧版API忽略它即可
-- 破坏性变更需要新API版本
-- 旧:GET /v1/users/{id} 返回 { "fullName": "Zhang San" }
-- 新:GET /v2/users/{id} 返回 { "firstName": "San", "lastName": "Zhang" }
-- 在数据库层同时维护两个字段(展开-收缩模式)
ALTER TABLE users ADD COLUMN first_name VARCHAR(100);
ALTER TABLE users ADD COLUMN last_name VARCHAR(100);
UPDATE users SET
first_name = split_part(full_name, ' ', 2),
last_name = split_part(full_name, ' ', 1);
-- v1 API继续读full_name,v2 API读first_name/last_name
-- 待v1 API退役后,再删除full_name列
使用Erdmates加速API设计
Erdmates的ERD可视化是API设计会议的理想工具。在确定资源边界时,通过ERD直观展示实体间的关联关系,帮助团队对齐API的资源层级和嵌套策略。将ERD分享给前端团队,让他们在API设计阶段就能参与讨论,减少接口设计来回返工的成本。
API设计和数据库设计是相互影响的——优化数据库查询效率的需求会影响API的字段设计,API的使用模式会反馈影响数据库的索引策略。保持两者的同步演进,是构建高效系统的关键。