Twenty CRM 架构设计
Twenty 是一个开源 CRM 系统,定位为 Salesforce 的现代替代品。采用 TypeScript 全栈 + 元数据驱动架构,最大的设计亮点是 Schema-per-tenant 多租户隔离和运行时动态 API 生成,使它不仅是一个 CRM,更是一个可编程的业务应用平台。v2.0 引入了 App 系统 + Git 版本控制,并将 AI Agent 作为一等公民。
🏗️ 整体架构
┌──────────────────┐
│ Browser │
└────────┬─────────┘
│
┌────────▼─────────┐
│ twenty-front │ React 18 SPA
│ Vite + Apollo │
└────────┬─────────┘
│ GraphQL / REST
┌──────────────┼──────────────┐
│ │ │
┌────────▼───┐ ┌──────▼──────┐ ┌───▼──────────┐
│ Core API │ │ Metadata API│ │ Admin API │
│ /graphql/ │ │ /metadata/ │ │ /admin-panel │
└────────┬───┘ └──────┬──────┘ └───┬──────────┘
│ │ │
└──────────────┼──────────────┘
│
┌────────▼─────────┐
│ twenty-server │ NestJS
│ TypeORM │
│ BullMQ Worker │
└──┬──────────┬────┘
│ │
┌────────▼──┐ ┌────▼──────────┐
│ PostgreSQL │ │ Redis │
│ 多Schema │ │ 缓存/会话/队列 │
└───────────┘ └────────────────┘
│
┌─────▼──────┐
│ ClickHouse │ 分析指标
└────────────┘
📦 Monorepo 结构
基于 Nx 22 + Yarn 4 (PnP) 管理,共 18 个子包:
twenty/
├── packages/
│ ├── twenty-server/ # NestJS 后端(所有业务逻辑)
│ ├── twenty-front/ # React 18 前端 SPA
│ ├── twenty-ui/ # 共享 UI 组件库
│ ├── twenty-shared/ # 前后端共享类型/工具
│ ├── twenty-utils/ # 通用工具函数
│ ├── twenty-emails/ # React Email 邮件模板
│ ├── twenty-sdk/ # App 开发 SDK 核心
│ ├── twenty-cli/ # npx twenty CLI 工具
│ ├── twenty-client-sdk/ # 外部 API 调用 SDK
│ ├── twenty-apps/ # App 扩展层
│ ├── twenty-docker/ # Docker Compose 部署配置
│ ├── twenty-docs/ # Mintlify 文档站
│ ├── twenty-e2e-testing/ # E2E 测试(Playwright)
│ ├── twenty-website/ # 官网 twenty.com
│ ├── twenty-zapier/ # Zapier 集成
│ ├── twenty-oxlint-rules/ # 自定义 Oxlint 规则
│ ├── twenty-claude-skills/# Claude Code 集成技能
│ └── create-twenty-app/ # 脚手架工具
├── nx.json # Nx workspace 配置
└── tsconfig.base.json # 共享 TS 配置
工具链特点:
- oxfmt(Rust 格式化器,替代 Prettier)
- tsgo(Go 编写的 TS 类型检查器)
- SWC + esbuild(编译加速)
🔑 核心设计思想
1. Schema-per-tenant 多租户隔离
Twenty 最重要的架构决策——每个 Workspace 对应 PostgreSQL 中一个独立 Schema,而非共享表 + tenant_id:
PostgreSQL Instance
├── core schema # 全局数据(用户、工作空间、成员关系)
├── metadata schema # 元数据(Object/Field/Relation 定义)
├── workspace_1 schema # 工作空间1的业务数据
└── workspace_2 schema # 工作空间2的业务数据
Tradeoff:
| 维度 | Schema-per-tenant(Twenty 选的) | Row-level(传统方案) |
|---|---|---|
| 数据隔离 | 彻底,跨 workspace 无法泄漏 | 需要每条查询加 WHERE |
| 迁移复杂度 | 高,需遍历所有 schema 执行 | 低,一张表一次迁移 |
| 连接池开销 | 较大(schema 切换) | 小 |
| 扩展上限 | 中等(schema 数有上限) | 高 |
| 适用场景 | 中小规模 SaaS,隔离要求高 | 大规模 SaaS,成本敏感 |
2. 元数据驱动架构
数据模型不是硬编码的,而是通过 metadata 定义。用户在 UI 中创建自定义 Object(如 Invoice)后,系统自动生成:
- REST 端点(
/rest/companies、/rest/invoices) - GraphQL 类型与 Resolver
- 数据库表(TypeORM 动态 Entity)
- UI 列表视图 / 详情视图 / 筛选器
这意味着 Twenty 本质上是一个低代码平台——CRM 只是它的默认应用。
3. 双 GraphQL 端点分离
将三个关注点分为独立端点,避免 schema 膨胀:
| 端点 | 路径 | 职责 |
|---|---|---|
| Core API | /graphql/ | 业务记录 CRUD、关联遍历 |
| Metadata API | /metadata/ | 数据模型管理(Object/Field/Relation) |
| Admin API | /admin-panel | 实例管理操作 |
前端对应三个 Apollo Client Provider:ApolloCoreProvider、ApolloMetadataProvider、ApolloAdminProvider。
4. App 系统 + Git 版本控制(v2.0)
v2.0 引入的 App 体系将 workspace 配置背后交由 Git 驱动:
- App 以 TypeScript 包开发,通过 CLI 同步到 Twenty
- 支持 Objects、Fields、Relations、Views、Logic Functions、Front Components、AI Skills/Agents
- Workspace 可分支、PR review、回滚——数据模型即代码
5. AI-Native 设计
AI 不是”附加功能”,而是架构的一等公民:
- 原生 AI Agent 执行引擎(基于
@ai-sdk) - MCP(Model Context Protocol)集成
- E2B 代码沙箱(Agent 可执行代码)
- AI Agent 可在 Workflow 中作为 Action 使用
- Skills(可复用指令)和 Agents(自主助手)都是 App 类型
🧩 核心模块
| 模块 | 功能 | 关键技术 |
|---|---|---|
data-model | 元数据驱动数据模型 | TypeORM 动态 Entity |
auth | 认证 | Passport + bcrypt + express-session |
permissions | V2 权限系统 | 自定义角色 + 对象级 CRUD |
workflow | 可视化工作流引擎 | BullMQ + 触发器/条件/动作 |
ai-agent | AI Agent 执行 | @ai-sdk + E2B 沙箱 |
calendar | 日历同步 | Google/Outlook API |
messaging | 邮件集成 | Gmail/Outlook + AWS SES |
app | App 扩展管理 | Git + TypeScript SDK |
file-storage | 文件存储 | S3 / Local |
audit | 审计日志 | - |
cron-jobs | 定时任务 | BullMQ repeatable jobs |
logic-function | 逻辑函数 | Serverless 执行 |
内置数据模型
| Object | 说明 |
|---|---|
| Companies | 企业/组织 |
| People | 联系人 |
| Opportunities | 商机/交易 |
| Tasks | 任务 |
| Notes | 备注 |
字段类型体系
基础类型:Text、Number、Boolean、Date、Currency、Rating、Select 复合类型:Address、Full Name、Links、Phones、Emails 特殊类型:Relation、File Attachment、JSON、Actor
每个 Object 自动获得系统字段:id、createdAt、updatedAt、createdBy、position
🔌 API 设计
双 API 体系
由于元数据驱动,没有静态 API 文档——每个 workspace 的 schema 决定它的端点。
认证方式:Authorization: Bearer YOUR_API_KEY(API Key 可绑定角色限制权限)
| API | 路径 | 用途 |
|---|---|---|
| Core REST | /rest/ | 记录 CRUD |
| Core GraphQL | /graphql/ | CRUD + 关联遍历 + 批量 upsert |
| Metadata REST | /rest/metadata/ | Schema 管理 |
| Metadata GraphQL | /metadata/ | 同上 GraphQL 版 |
限流:100 请求/分钟,批量操作 60 条/次
GraphQL 特性
- Resolver 通过装饰器区分 scope:
@CoreResolver()/@MetadataResolver()/@AdminResolver() - 前端通过 GraphQL Code Generator 自动生成类型安全 hooks
- 批量 upsert 使用复数名称(
CreateCompanies)
Webhooks
支持 Webhook 回调,可与 Zapier、n8n 等集成平台配合。
💾 数据库设计
ORM 与存储
| 组件 | 技术 | 用途 |
|---|---|---|
| 主数据库 | PostgreSQL 16 | 业务数据 + 元数据 |
| 缓存/队列 | Redis | 会话、缓存、BullMQ 队列 |
| 分析 | ClickHouse | 使用指标和遥测 |
迁移命令系统
Twenty 有独特的迁移命令分层:
| 命令类型 | 装饰器 | 职责 |
|---|---|---|
| Instance Command | @RegisteredInstanceCommand | 全局 schema 变更 |
| Workspace Command | @RegisteredWorkspaceCommand | 遍历所有 workspace 执行 |
每种命令分 fast(仅 DDL)和 slow(DDL + 数据迁移)两种。严禁删除或改写已提交的迁移逻辑。
请求上下文
使用 Node.js AsyncLocalStorage 在请求生命周期中维护 workspace 认证上下文(workspace-auth-context.middleware.ts)。
🔐 认证与权限
认证方式
| 方式 | 实现 |
|---|---|
| 邮箱/密码 | bcrypt 哈希 |
| Google OAuth | Google Workspace SSO |
| Microsoft OAuth | MSAL (@azure/msal-node) |
| SAML 2.0 | 企业级 SSO |
权限系统(V2)
- 自定义角色管理
- 对象级权限:每个 Object 独立授予 Read/Create/Edit/Delete
- 设置级权限:细粒度控制谁能管理用户、数据模型、API
- API Key 可绑定到特定角色
🚀 部署
Docker Compose(推荐)
services:
server: # NestJS API :3000
image: twentycrm/twenty
depends_on: [db, redis]
worker: # BullMQ 后台任务(同镜像)
image: twentycrm/twenty
command: ["yarn", "worker:prod"]
db: # PostgreSQL 16
image: postgres:16
redis: # 缓存/队列
image: redis
command: ["--maxmemory-policy", "noeviction"]
一键安装:bash <(curl -s https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/install.sh)
系统要求:2GB RAM + Docker
关键环境变量
| 变量 | 用途 |
|---|---|
PG_DATABASE_URL | PostgreSQL 连接 |
REDIS_URL | Redis 连接 |
SERVER_URL | 对外服务 URL |
STORAGE_TYPE | 文件存储(S3/Local) |
ENCRYPTION_KEY | 加密密钥 |
IS_MULTIWORKSPACE_ENABLED | 多工作空间开关 |
⚖️ 架构 Tradeoff 总结
| 决策 | 收益 | 代价 |
|---|---|---|
| Schema-per-tenant | 数据隔离彻底 | 迁移复杂、schema 数有上限 |
| 元数据驱动 API | 动态扩展能力强 | 运行时开销、调试困难 |
| 双 GraphQL 端点 | 关注点分离清晰 | 前端需要管理三个 Apollo Client |
| TypeScript 全栈 | 类型安全、前后端复用 | 性能不如 Go/Rust 方案 |
| AGPL-3.0 许可 | 保护开源生态 | 限制企业闭源二次开发 |
| 无移动端 | 专注 Web 体验 | CRM 场景下是明显短板 |
📊 竞品对比
| 维度 | Twenty | Salesforce | HubSpot | EspoCRM |
|---|---|---|---|---|
| 许可证 | AGPL-3.0 开源 | 闭源 SaaS | 闭源 SaaS | AGPL-3.0 |
| 价格 | 自托管免费 | $175+/用户/月 | $890+/月起 | 自托管免费 |
| 技术栈 | TypeScript/NestJS/React | Java | Java | PHP |
| API | REST + GraphQL | REST + SOAP | REST | REST |
| AI 集成 | 原生 Agent/MCP | Einstein AI | AI Hub | 无 |
| 自托管 | 支持 | 不支持 | 不支持 | 支持 |
| 适合 | SMB、开发者友好 | 企业 | SMB+ | SMB |
📌 可复用设计模式
- 元数据驱动 API 生成:适用于任何需要动态数据模型的平台产品
- Schema-per-tenant:适用于数据隔离要求高的多租户系统
- 双 GraphQL 端点:适用于数据操作和 Schema 管理需要分离的场景
- App 系统 + Git 版本控制:将配置即代码的理念推向极致
- 迁移命令分层:Instance/Workspace/Command 三级迁移策略适用于多租户场景
关联笔记
- 元数据驱动架构 — Twenty 的核心设计范式
- Schema-per-tenant 多租户模式 — 数据库隔离策略
- Nx Monorepo — Monorepo 管理工具
- GraphQL Code Generator — 类型安全 GraphQL 开发
- NestJS — 后端框架
- TypeORM — TypeScript ORM
- BullMQ — Redis 任务队列
- AGPL 许可证 — 开源许可证选择