使用 Supabase 进行 Schema 设计:分区与规范化

发布: (2025年12月6日 GMT+8 15:49)
6 min read
原文: Dev.to

Source: Dev.to

第 6 天 – 使用 Supabase 的模式设计

PostgreSQL 支持 schemas(模式),它们充当用于分组表的命名空间。
典型的使用场景包括:

  • 多租户 – 为每个客户创建单独的 schema,实现完整的数据隔离。
  • 访问控制 – 对每个 schema 设置权限。
  • 扩展隔离 – 将扩展(例如 pgvector)放在专用的 schema 中。

在许多项目中,所有表都位于默认的 public schema:

-- 默认是 public schema
SELECT * FROM public.users;

-- 创建一个单独的 schema
CREATE SCHEMA app_auth;
SELECT * FROM app_auth.users;

单一 public schema 的问题

  • 随着表数量增长,可视性下降。
  • 难以判断某个表属于哪个功能。
  • 权限管理变得复杂。

为了解决这些问题,我按功能拆分了 schema,并添加了 app_ 前缀。

app_ 前缀

Supabase 保留了系统 schema,如 authstoragepublic
通过在自定义 schema 前加上 app_(即 application 的缩写),可以:

  • 与 Supabase 系统 schema 明显区分。
  • 在 pgAdmin 等工具中按字母顺序首先出现,便于快速定位。

Supabase 系统 schemas

├── auth        # Supabase 认证
├── storage     # Supabase 存储
├── public      # 默认

应用 schemas

├── app_auth      # 自定义认证
├── app_billing   # 计费
├── app_content   # 内容管理
├── app_admin     # 管理功能
├── app_ai        # AI 功能
├── app_social    # 社交功能
├── app_system    # 系统日志等

我完全避免使用 Supabase 的系统 schemas,所有内容都放在自定义 schemas 中,以降低供应商锁定。对于小型服务来说,维护大量 schema 的开销可能不值得,但随着 SaaS 的成长,这种做法的收益会逐渐显现。

当前 Schema 概览

Schema负责范围示例表
app_admin管理功能tenantsteamsmembers
app_aiAI 功能embeddingssearch_vectors
app_auth认证userssessionsaccounts
app_billing计费subscriptionspayment_history
app_content内容管理contentspagestables
app_social社交功能bookmarkscommentsreactions
app_system系统日志activity_logssystem_logs

分区标准

  1. 功能内聚 – 将相关表放在一起(例如 app_auth)。
  2. 变更频率 – 将频繁变更的表单独隔离。
  3. 权限边界 – 将仅管理员可见的数据 (app_admin) 与普通用户数据 (app_content) 分离。

使用 Drizzle ORM 定义 Schemas

// database/app_auth/schema.ts
import { pgSchema } from 'drizzle-orm/pg-core';

export const appAuth = pgSchema('app_auth');
// database/app_auth/users.ts
import { appAuth } from './schema';
import { text, timestamp, uuid } from 'drizzle-orm/pg-core';

export const users = appAuth.table('users', {
  id: uuid('id').primaryKey().defaultRandom(),
  name: text('name').notNull(),
  email: text('email').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
  updatedAt: timestamp('updated_at').defaultNow(),
});

与 schemas 对应的目录结构

src/database/
├── app_auth/
│   ├── schema.ts      # Schema 定义
│   ├── users.ts       # 表定义
│   ├── sessions.ts
│   └── index.ts       # 导出
├── app_billing/
│   ├── schema.ts
│   ├── subscriptions.ts
│   └── index.ts
├── index.ts           # 汇总导出
└── relations.ts       # 关系定义

规范化与反规范化

即使是个人项目,基本的规范化(1NF‑3NF)也非常有价值:

  • 1NF – 消除重复组;使用关联表(junction table)代替 CSV 字段。
  • 2NF – 去除部分依赖;把仅依赖复合键一部分的列拆分出来。
  • 3NF – 去除传递依赖;仅在需要时存储派生值。

实用技巧

  • 关联表 – 处理多对多关系(例如 content_tags)。
  • 复合主键 – 对多租户表使用 (tenant_id, id)
  • 反规范化 – 为读密集型查询存储聚合计数(如书签数量)。

行级安全 (RLS)

RLS 提供每行的访问控制。虽然我目前在应用层实现权限控制,但分区的 schema 布局有助于以后编写 RLS 策略,因为每个 schema 可以拥有独立的策略集合。

在 Supabase 中使用自定义 Schemas

添加自定义 schemas 后,需要配置三件事,否则会出现 “table not found” 错误。

1. 在 Supabase 仪表盘中公开 schemas

Project Settings → Data API → Exposed schemas → 添加你的 schemas(例如 public, app_admin, app_ai, …)。

2. 在 DATABASE_URL 中包含 schemas

# .env.local
DATABASE_URL=postgresql://user:password@host:5432/postgres?schema=public,app_admin,app_ai,app_auth,app_billing,app_content,app_social,app_system

3. 配置 Drizzle ORM 的 schemaFilter

// drizzle.config.ts
import type { Config } from 'drizzle-orm';

export default {
  schema: [
    './src/database/app_admin/index.ts',
    './src/database/app_auth/index.ts',
    './src/database/app_billing/index.ts',
    // …
  ],
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
  // 多 schema 支持
  schemaFilter: [
    'public',
    'app_admin',
    'app_ai',
    'app_auth',
    'app_billing',
    'app_content',
    'app_social',
    'app_system',
  ],
} satisfies Config;

收获

有效之处

  • 通过基于功能的 schemas 实现清晰的职责划分。
  • 目录结构与 schema 名称保持一致,提升可读性。
  • 基础规范化结合有针对性的反规范化,兼顾性能。

注意事项

  • 过多的 schemas 会增加复杂度。
  • 别忘了在 Supabase 设置中公开自定义 schemas。
  • 即使是小项目,也应提前规划好 schema 设计,以便未来扩展。

接下来

明天的文章将讨论 数据库 ID 设计——在 UUID、CUID2、顺序 ID 等之间的取舍。

本系列相关帖子

  • 12/5 – Git 分支策略:面向独立开发者的实用工作流
  • 12/7 – 数据库 ID 设计:在 UUID、CUID2、顺序 ID 等之间的取舍
Back to Blog

相关文章

阅读更多 »

面向平台的 Vercel 介绍

您现在可以使用今天宣布的新产品构建平台,使您能够轻松代表用户创建和运行客户项目。Vercel for Platform…