第02天 [架构] 总体架构与多租户设计
今日寄语:"糟糕的程序员担心代码,优秀的程序员担心数据结构和它们之间的关系。" —— Linus Torvalds
[注意] 2.0 今日导读
前置依赖:
-
[√] 完成第01天:明确学习路径和MVP范围
-
[√] 了解低代码平台的核心概念
-
[√] 熟悉基本的架构设计原则
核心产出:
-
📐 MetaFlow 七层架构设计图
-
🏢 多租户方案选型与实现方案
-
📁 Monorepo 项目骨架
-
📋 核心模块接口定义(OpenAPI)
预计耗时:
-
[指南] 阅读理解: 30-45分钟
-
[实践] 必做作业: 60-90分钟
-
[*] 选做挑战: 2-3小时
AI 结对策略:
让 AI 扮演架构师,帮你审查模块依赖、生成 OpenAPI (Swagger) 定义草案、分析性能瓶颈。
2.1 写在最前面:架构决定成败
在低代码平台开发中,架构设计就像是建筑的地基。地基不牢,地动山摇。今天我们要搭建的不仅仅是一个技术框架,更是整个平台的"定海神针"。
[提示] AI 加速提示:今天我们将使用 AI 来生成架构文档、审查模块依赖、创建 OpenAPI 定义。
2.2 七层架构模型:系统的骨架
低代码平台之所以复杂,是因为它既是生产工具(Designer),又是运行环境(Runtime)。为了解耦,我们将系统划分为七层。
graph TD
subgraph Client [第一层:交互层 (Presentation)]
Web[Web 端 (React)]
Mobile[H5/小程序]
IDE[IDE 设计器]
end
subgraph Gateway [第二层:接入层 (Gateway)]
Nginx[Nginx / Ingress]
Auth[认证鉴权 (Auth Guard)]
Rate[限流熔断]
Error[全局错误处理]
end
subgraph App [第三层:应用组装层 (App Orchestration)]
PageEngine[页面渲染引擎]
FlowEngine[流程编排引擎]
ApiProxy[API 聚合代理]
Validation[数据校验引擎]
end
subgraph Core [第四层:核心领域层 (Domain Core)]
Meta[元数据服务 (Metadata Service)]
Modeling[数据建模 (Modeling Service)]
Workflow[工作流内核 (Workflow Kernel)]
Rbac[权限内核 (RBAC Core)]
Script[脚本引擎]
end
subgraph Support [第五层:通用支撑层 (Support)]
Plugin[插件机制]
Event[事件总线]
Job[定时任务]
Cache[缓存管理]
Log[日志服务]
end
subgraph Data [第六层:数据持久层 (Persistence)]
MetaDB[(元数据 DB - SQLite/PG)]
BizDB[(业务数据 DB - SQLite/PG/MySQL)]
CacheDB[(缓存 - Redis)]
FileStore[(文件存储 - MinIO)]
end
subgraph Infra [第七层:基础设施层 (Infrastructure)]
K8s[Kubernetes]
OSS[对象存储]
LLM[LLM 网关]
Monitor[监控系统]
end
Client --> Gateway
Gateway --> App
App --> Core
Core --> Support
Core --> Data
Support --> Infra
关键决策点
- 元数据与业务数据分离:
-
MetaDB:存储"页面长什么样"、"表有哪些字段"。这是平台的灵魂。 -
BizDB:存储用户实际录入的"张三"、"隐患单"。这是用户的资产。 -
为什么分离? 为了方便平台升级。MetaDB 升级不应影响 BizDB 的数据完整性。
- 引擎与设计器解耦:
-
设计器只负责生成 JSON。
-
应用组装层只负责消费 JSON。两者通过标准 Schema 通信,互不依赖。
-
错误处理层:新增全局错误处理,确保系统稳定性。
-
脚本引擎:将脚本执行能力下沉到核心领域层,便于统一管理。
2.3 多租户设计:ToB 的必修课
多租户(Multi-Tenancy)是 SaaS 的基石。如果在 Day 02 不定下来,Day 15 再改就是灾难。
2.3.1 常见策略对比
| 策略 | 描述 | 优点 | 缺点 | 适用场景 |
| :--- | :--- | :--- | :--- | :--- |
| 独立数据库 (Database per Tenant) | 每个租户一个 DB | 数据绝对隔离,安全,可单独备份 | 成本极高,运维噩梦 | 银行/政企大客户 |
| 独立 Schema (Schema per Tenant) | 共享 DB,独立 Schema | 隔离性好,成本适中 | 跨租户统计难,迁移略繁琐 | 中型 SaaS |
| 共享 Schema (Discriminator) | 共享表,加 tenant_id 字段 | 成本最低,开发最快 | 代码容易写漏,数据泄露风险 | MVP 首选 |
2.3.2 我们的选择:Discriminator(MVP)+ 生产 RLS 兜底 + 中间件防护
为了 21 天能交付,我们选择 共享 Schema (Discriminator) 模式。
[警告] 风险提示:
共享 Schema 模式在代码层面容易出现漏洞(如开发者忘记加 where 条件),可能导致严重的数据泄露。对于金融、医疗等对数据隔离要求极高的场景,必须采用物理隔离(独立数据库或独立 Schema)。
为了安全,我们采用三层防护(其中数据库层仅在生产 PostgreSQL 启用):
设计规范:
-
所有表(除了系统配置表)必须包含
tenant_id字段。 -
ORM 层拦截:在 Prisma Client 中通过 Middleware 自动注入
tenant_id过滤条件。 -
数据库层防护(生产 PostgreSQL):利用 RLS (行级安全策略) 作为兜底。
-
应用层验证:在业务逻辑层再次验证租户权限。
2.3.3 技术实现示例
// prisma/tenant.middleware.ts
import { Prisma } from '@prisma/client';
export const tenantMiddleware = (tenantId: string): Prisma.Middleware => {
return async (params, next) => {
// 自动添加 tenant_id 过滤条件
if (params.model && params.action.startsWith('find')) {
if (!params.args.where) {
params.args.where = {};
}
params.args.where.tenantId = tenantId;
}
// 创建操作自动添加 tenant_id
if (params.action === 'create') {
if (params.args.data) {
params.args.data.tenantId = tenantId;
}
}
return next(params);
};
};
实现备注(MVP):当前示例以 SQLite 本地开发为主;生产部署建议切换 PostgreSQL 并启用 RLS(行级安全),同时保留应用层的 Header 注入与服务端校验双保险。
完整实现与更多错误映射见附录:多租户中间件与 RLS 策略
2.4 模块拆分与目录结构 (Monorepo)
我们使用 TurboRepo 管理 Monorepo。结构如下:
metaflow/
├── apps/
│ ├── web/ # 前端:设计器 + 运行时 (React)
│ └── server/ # 后端:API 服务 (NestJS)
├── packages/
│ ├── database/ # 共享:Prisma Schema 与 Client
│ ├── shared-types/ # 共享:前后端通用的 TS 类型定义
│ ├── ui/ # 共享:基础 UI 组件库
│ ├── logic-engine/ # 核心:前端逻辑解析引擎 (纯 JS 库)
│ ├── validation/ # 共享:数据校验引擎
│ └── utils/ # 共享:工具函数库
├── docker/ # 部署配置
└── docs/ # 文档
为什么把 logic-engine 独立?
因为逻辑引擎需要在两端运行:
-
浏览器端:实时计算表单联动(如:金额 > 1000 显示审批人)。
-
服务端:提交数据时进行二次校验(防止恶意篡改前端逻辑)。
独立发包可以确保前后端逻辑的一致性。
2.5 AI 结对编程实战:定义接口契约
架构定好了,接下来该写接口定义了。这时候别自己哼哧哼哧写 YAML,让 AI 来帮你。
2.5.1 Prompt 示例
你:我正在开发一个低代码平台,后端使用 NestJS。请帮我定义"应用管理(App Management)"模块的 RESTful API 接口。
要求:
- 遵循 OpenAPI 3.0 标准。
- 包含创建、列表、详情、发布四个接口。
- 字段包含:id, name, description, icon, status (draft/published), created_by, tenant_id。
- 加上 Swagger 装饰器示例代码。
- 包含请求/响应 DTO 定义。
- 添加错误处理和验证规则。
Claude/ChatGPT:没问题,这是为您生成的
apps.controller.ts和 DTO 代码...
2.5.2 更高级的 AI 应用
架构审查
Prompt:请审查以下七层架构设计,指出潜在的问题和改进建议。
性能优化建议
Prompt:基于这个架构设计,请分析可能的性能瓶颈,并提供优化建议。
2.6 性能与安全考虑
性能优化策略
-
缓存策略:元数据缓存、页面模板缓存
-
数据库优化:索引优化、查询优化
-
前端优化:虚拟滚动、懒加载、代码分割
安全防护措施
-
输入验证:所有接口都必须有严格的输入验证
-
权限控制:RBAC + ABAC 双重验证
-
数据加密:敏感数据加密存储
-
审计日志:关键操作记录审计日志
2.7 今日作业
[√] 必做任务 (预计 90-120分钟)
任务1: 初始化Monorepo项目 (预计 30分钟)
-
使用 TurboRepo 或 pnpm workspace 初始化
-
创建
apps/(web+server) 和packages/(database+shared-types) 目录 -
配置统一的
tsconfig.json和.eslintrc -
验收标准:
pnpm install成功,所有package能相互引用
任务2: 设计Prisma Schema (预计 40分钟)
-
在
packages/database/prisma/schema.prisma创建Schema -
定义 User, Tenant, App 三张核心表
-
每张表必须包含
tenant_id字段(除Tenant表外) -
验收标准:
npx prisma generate成功生成Client
任务3: 实现多租户中间件 (预计 30分钟)
-
创建
tenant.middleware.ts -
拦截所有Prisma查询,自动注入
where: { tenantId } -
验收标准: 查询User时自动过滤当前租户数据
[*] 选做挑战 (预计 2-3小时)
-
架构审查: 使用AI分析知名SaaS产品的多租户方案
-
RLS策略: 研究PostgreSQL RLS并编写示例
-
压测方案: 设计七层架构的性能瓶颈分析
[提示] 详细代码模板见附录B
[√] 2.8 今日检查点
请确认你已经:
-
理解七层架构的设计原则和每层职责
-
掌握三种多租户策略的适用场景
-
了解 Discriminator + RLS 三层防护机制
-
熟悉 Monorepo 项目结构和模块划分
-
能够使用 AI 生成 OpenAPI 接口定义
-
完成 Prisma Schema 基础设计
-
实现了多租户中间件原型
[提示] 检查方法: 尝试画出七层架构图和数据流向图,如果能独立画出并解释,说明已掌握核心概念。
[笔记] 2.9 要点回顾
-
七层架构: 交互层 → 接入层 → 应用组装层 → 核心领域层 → 通用支撑层 → 数据持久层 → 基础设施层
-
多租户方案: MVP选择Discriminator(共享Schema),生产环境用PostgreSQL RLS兜底
-
三层防护: ORM中间件 + 应用层验证 + 数据库RLS策略
-
Monorepo结构: apps/(web+server) + packages/(database+shared-types+logic-engine等)
-
AI加速: 架构审查、接口生成、性能分析、文档撰写
[参考] 2.10 延伸阅读
-
[Book] Clean Architecture by Robert C. Martin: 深入研究分层架构的设计原则
-
[Book] Designing Data-Intensive Applications by Martin Kleppmann: 第6章关于分区与多租户数据的讨论
-
[Article] Multi-Tenant Data Architecture: Microsoft 的多租户架构最佳实践
-
[Doc] PostgreSQL Row Level Security: 官方RLS文档与最佳实践
❓ 2.11 常见问题
Q1: 为什么选择共享 Schema 而不是独立数据库?
A: MVP阶段优先考虑开发速度和成本。独立数据库虽然安全性最高,但运维复杂度和成本都很高。对于大部分ToB场景,Discriminator + RLS双重防护已经足够安全。
Q2: 七层架构是不是太复杂了?
A: 对于MVP来说,不需要一次性实现所有层。可以先实现核心的3-4层,其他层随着需求增长逐步补充。重要的是理解分层思想。
Q3: Monorepo 和 MultiRepo 该如何选择?
A: 低代码平台的前后端、多个Package高度耦合,共享大量类型定义,Monorepo可以简化依赖管理和版本同步。如果团队规模大于50人,可考虑MultiRepo。
Q4: 为什么要把 logic-engine 独立出来?
A: 逻辑引擎需要在浏览器端和服务端都执行,独立发包可确保前后端使用完全相同的逻辑,避免不一致导致的安全漏洞。