创建日期: 2025-12-25
架构师: Winston (Architect Agent)
版本: 1.1 (精简版)
状态: Draft
模块化单体架构 (Modular Monolith)
- 单进程部署,模块清晰分离
- 便于 MVP 快速交付
- 预留微服务演进路径
┌─────────────────────────────────────────────────────────────┐
│ apprun BaaS Platform │
├─────────────────────────────────────────────────────────────┤
│ API Gateway (Chi Router + ReverseProxy) │
├──────────┬──────────┬──────────┬──────────┬─────────────────┤
│ Auth │ Data │ Storage │ Functions│ Config │
│ Module │ Module │ Module │ Module │ Module │
├──────────┼──────────┼──────────┼──────────┼─────────────────┤
│ Workflow │ Events │ Realtime │ I18N │ License │
│ Module │ Module │ Module │ Module │ Module │
├──────────┴──────────┴──────────┴──────────┴─────────────────┤
│ Middleware (RBAC, Logging, Metrics) │
├──────────────────────────────────────────────────────────────┤
│ Data Access Layer (Ent ORM + Repository Pattern) │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
PostgreSQL 14+ Redis 7+ Waterflow
(主数据库) (可选/事件) (独立服务)
注: 详细部署架构请参阅 deployment-architecture.md
| 层级 | 技术选型 | 版本 | 用途 |
|---|---|---|---|
| 语言 | Go | 1.24+ | 主要开发语言 |
| 数据库 | PostgreSQL | 14+ | 主数据库 (ACID + JSON + 全文搜索) |
| 缓存 | Redis | 7+ | 可选/事件中心 (Streams) + L2 缓存 |
| ORM | Ent | latest | 类型安全的 ORM + 代码生成 |
| Schema | Atlas | latest | 声明式 Schema 管理和迁移 |
| 路由 | Chi | v5 | HTTP 路由 + 中间件 |
| 认证 | bcrypt + JWT | - | 密码哈希 + Token 认证 (Go Native) |
| 授权 | Casbin | v2 | RBAC 策略引擎 |
| 工作流 | Waterflow | latest | 基于 Temporal 的工作流引擎 |
| WebSocket | coder/websocket | latest | 实时推送 |
| VFS | spf13/afero | latest | 虚拟文件系统 (本地 + S3) |
| 配置 | Viper | v1 | 配置管理 + Watch |
| 监控 | Prometheus | latest | 指标采集 |
| 可视化 | Grafana | latest | 监控面板 |
| 容器 | Docker | 20.10+ | 容器化部署 |
- Linter: golangci-lint 1.64.8
- Security: gosec 2.22.7+
- Testing: Testify
- CI/CD: GitHub Actions
- 文档: Swagger/OpenAPI
实现方式: Go Native (bcrypt + JWT)
// 核心包依赖
- golang.org/x/crypto/bcrypt // 密码哈希
- github.com/golang-jwt/jwt/v5 // JWT Token
- github.com/gorilla/sessions // Session 管理 (可选)
// apprun 自有表
- users // 用户信息 (id, email, password_hash, name)
- user_projects // 用户项目关系认证流程:
- 用户注册 → bcrypt 密码哈希 → 存储到 users 表
- 用户登录 → 验证密码 → 签发 JWT Token (Access + Refresh)
- API 请求 → 验证 JWT → 提取 user_id → 查询 RBAC 权限
关键实现:
// 密码哈希
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 12)
// 密码验证
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
// JWT 签发
token := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
UserID: user.ID,
Email: user.Email,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
})
tokenString, _ := token.SignedString(jwtSecret)关键接口:
POST /api/v1/auth/register- 用户注册POST /api/v1/auth/login- 用户登录(返回 JWT)POST /api/v1/auth/refresh- 刷新 TokenGET /api/v1/auth/me- 获取当前用户信息
模型: Project-based RBAC + 平台全局权限
// 权限模型
User → ProjectRole (per Project) → Permissions
User → GlobalRole (platform-level) → Permissions
// Casbin Policy 示例
p, admin, project:1, *, * // Project 1 的 Admin
p, viewer, project:1, resource:*, read // Project 1 的 Viewer
p, platform_admin, *, *, * // 平台管理员中间件:
// 检查权限
func RequirePermission(resource, action string) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 从 Session 获取 user_id
// 2. 从 Context 获取 project_id
// 3. Casbin.Enforce(user, project, resource, action)
// 4. 通过则 next.ServeHTTP(w, r)
})
}
}DSL → Ent Schema 流程:
用户定义 YAML/JSON
↓
解析并验证 DSL
↓
生成 Ent Schema (Go)
↓
go generate ./ent
↓
生成 CRUD 代码
↓
Atlas 生成迁移脚本
↓
应用数据库变更
示例 DSL:
model:
name: Product
fields:
- name: title
type: string
required: true
- name: price
type: decimal
required: true
- name: stock
type: int
default: 0
relations:
- name: category
type: many-to-one
target: Category生成的 Ent Schema:
// ent/schema/product.go
type Product struct {
ent.Schema
}
func (Product) Fields() []ent.Field {
return []ent.Field{
field.String("title"),
field.Float("price"),
field.Int("stock").Default(0),
}
}
func (Product) Edges() []ent.Edge {
return []ent.Edge{
edge.From("category", Category.Type).Ref("products").Unique(),
}
}虚拟文件系统 (VFS):
// 抽象接口
type FileStorage interface {
Upload(ctx context.Context, path string, reader io.Reader) error
Download(ctx context.Context, path string) (io.Reader, error)
Delete(ctx context.Context, path string) error
List(ctx context.Context, prefix string) ([]FileInfo, error)
}
// 实现
- LocalStorage (基于 afero.OsFs)
- S3Storage (基于 afero.S3Fs)
// 使用
fs := afero.NewOsFs() // 本地优先
// fs := afero.NewS3Fs(...) // 可切换到 S3文件夹模拟:
// 数据库表
type Folder struct {
ID int
Path string // /project-1/docs/
Metadata JSON // {"owner": "user_1"}
}
type File struct {
ID int
Path string // /project-1/docs/file.pdf
FolderID int
Storage string // "local" or "s3"
}执行方式: 进程隔离
// 函数定义
type Function struct {
ID int
Name string
Code string // Go 代码
Runtime string // "go1.24"
Trigger string // "http" or "event"
}
// 执行流程
1. 编译函数代码 → 二进制文件
2. 启动独立进程执行
3. 通过 stdin/stdout 传递数据
4. 超时自动杀死进程HTTP 触发:
POST /api/v1/functions/:name/invoke
{
"input": {"key": "value"}
}
事件触发:
// 订阅 Redis Streams
eventBus.Subscribe("user.created", func(event Event) {
// 调用函数
executeFunction("on-user-created", event.Data)
})gRPC 插件协议:
// plugin.proto
service Plugin {
rpc Execute(Request) returns (Response);
}
message Request {
string operation = 1; // "auth", "storage", "api"
bytes payload = 2;
}插件类型:
- 认证插件: LDAP、OAuth、SAML
- 存储插件: 新存储后端 (OSS、COS)
- API 插件: 中间件扩展
- 工作流插件: 自定义节点
加载流程:
// 启动时加载插件
plugins := []Plugin{
LoadPlugin("plugins/ldap-auth"),
LoadPlugin("plugins/oss-storage"),
}
// 调用插件
for _, plugin := range plugins {
plugin.Execute(request)
}集成 Waterflow:
apprun Waterflow (独立服务)
│ │
│ POST /api/workflows │
├───────────────────────────>│
│ {definition, trigger} │
│ │ 存储工作流定义
│ │
│ POST /api/workflows/:id/execute
├───────────────────────────>│
│ │ 执行工作流
│ │
│ Webhook 回调 │
│<────────────────────────────┤
│ {status, result} │
工作流定义 (YAML):
name: user-onboarding
trigger:
type: event
event: user.created
steps:
- name: send-welcome-email
type: function
function: send-email
params:
template: welcome
- name: create-default-project
type: api
api: POST /api/v1/projects
params:
name: "{{ user.name }}'s Project"Redis Streams 实现:
// 发布事件
eventBus.Publish("user.created", map[string]interface{}{
"user_id": 123,
"email": "alice@example.com",
})
// 订阅事件
eventBus.Subscribe("user.created", func(event Event) {
log.Printf("New user: %v", event.Data)
})
// Redis Streams 操作
XADD events:user.created * user_id 123 email alice@example.com
XREAD BLOCK 1000 STREAMS events:user.created 0特性:
- 短期持久化 (1-7 天)
- 分区内有序
- 消费者组支持
WebSocket + CDC:
// WebSocket 连接管理
type ConnectionManager struct {
connections map[string]*websocket.Conn
}
// ORM Hook (Ent)
func (u *User) AfterCreate(ctx context.Context) error {
// 发送实时通知
wsManager.Broadcast("user.created", u)
return nil
}
// 客户端订阅
ws://apprun/ws?subscribe=user.created,project.updated/api/v1/
├── auth/ # 认证模块
│ ├── whoami
│ └── logout
├── users/ # 资源路由
├── projects/ # 资源路由
│ └── :id/
│ ├── models/ # 嵌套资源
│ └── members/
├── storage/ # 模块路由
│ ├── upload
│ └── download/:path
├── functions/ # 模块路由
│ └── :name/invoke
└── workflows/ # 模块路由
└── :id/execute
注: 详细数据架构(数据模型、缓存策略)请参阅 data-architecture.md
成功响应:
{
"success": true,
"code": 200,
"message": "操作成功",
"data": {
"user": {"id": 1, "name": "Alice"}
}
}分页响应:
{
"success": true,
"code": 200,
"data": {
"items": [{"id": 1}, {"id": 2}],
"pagination": {
"total": 100,
"page": 1,
"pageSize": 10,
"totalPages": 10
}
}
}错误响应:
{
"success": false,
"code": 400,
"message": "参数错误",
"error": {
"code": "INVALID_PARAM",
"details": "email 格式不正确"
}
}- HTTPS/TLS 1.3 - 所有 API 通信
- CORS - 跨域资源共享控制
- CSRF Token - 防跨站请求伪造
- 密码: bcrypt 加密
- 敏感数据: AES-256 加密
- 密钥管理: 环境变量
- SQL 注入: Ent 参数化查询
- XSS: 输入过滤 + 输出转义
- RBAC: Casbin 策略引擎
- API P95 < 100ms
- API P99 < 200ms
- QPS > 10,000 (单机)
- 数据库索引优化
- 多层缓存 (L1+L2)
- 连接池 (DB、Redis)
- 异步处理 (事件队列)
// 系统指标
- cpu_usage, memory_usage, disk_usage
// API 指标
- http_requests_total
- http_request_duration_seconds
- http_errors_total
// 业务指标
- user_count, project_count
- function_executions_total- 结构化日志: JSON 格式
- 日志级别: DEBUG, INFO, WARN, ERROR
- 日志存储: 文件 + 简单查询 API
- Trace ID: 请求链路追踪 (MVP 不实现)
当前 (MVP):
┌────────────────┐
│ apprun (单体) │
└────────────────┘
未来 (微服务):
┌──────┐ ┌──────┐ ┌──────┐
│ Auth │ │ Data │ │ Store│
└──────┘ └──────┘ └──────┘
演进条件:
- 单机性能瓶颈
- 团队规模扩大
- 模块独立部署需求
当前 (单机):
┌────────────────┐
│ apprun + DB │
└────────────────┘
未来 (多实例):
┌─────────────┐
│ Load Balancer│
└─────────────┘
│
┌────┴────┐
│ │
┌───┴──┐ ┌──┴───┐
│apprun│ │apprun│
└──────┘ └──────┘
│ │
└────┬────┘
│
┌────┴────┐
│ PostgreSQL Cluster │
│ Redis Cluster │
└───────────────────┘
- 决策: MVP 使用模块化单体,保留微服务演进路径
- 理由: 降低部署复杂度,加速 MVP 交付
- 权衡: 牺牲部分独立扩展性
- 决策: 集成 Ory Kratos + 共享数据库
- 理由: 生产级安全性,节省开发周期
- 权衡: 额外依赖,但风险可控
- 决策: 使用 Redis Streams,而非 Kafka/NATS
- 理由: 轻量级,复用已有 Redis
- 权衡: 功能相对简单,但 MVP 足够
- 决策: 使用 afero 抽象本地和 S3 存储
- 理由: 云中立,灵活切换存储后端
- 权衡: 略微增加抽象层复杂度
- VFS: Virtual File System (虚拟文件系统)
- CDC: Change Data Capture (数据变更捕获)
- RBAC: Role-Based Access Control (基于角色的访问控制)
- ADR: Architecture Decision Record (架构决策记录)
- DSL: Domain Specific Language (领域特定语言)
文档维护: Winston (Architect Agent)
审核状态: 待技术团队评审
下一步: 创建部署架构文档 (deployment-architecture.md)