Phase 0: 知识库
- docs/knowledge-base/loco-rs-patterns.md — loco-rs 10 个可借鉴模式研究
Phase 1: 数据层重构
- crates/zclaw-saas/src/models/ — 15 个 FromRow 类型化模型
- Login 3 次查询合并为 1 次 AccountLoginRow 查询
- 所有 service 文件从元组解构迁移到 FromRow 结构体
Phase 2: Worker + Scheduler 系统
- crates/zclaw-saas/src/workers/ — Worker trait + 5 个具体实现
- crates/zclaw-saas/src/scheduler.rs — TOML 声明式调度器
- crates/zclaw-saas/src/tasks/ — CLI 任务系统
Phase 3: 性能修复
- Relay N+1 查询 → 精准 SQL (relay/handlers.rs)
- Config RwLock → AtomicU32 无锁 rate limit (state.rs, middleware.rs)
- SSE std::sync::Mutex → tokio::sync::Mutex (relay/service.rs)
- /auth/refresh 阻塞清理 → Scheduler 定期执行
Phase 4: 多环境配置
- config/saas-{development,production,test}.toml
- ZCLAW_ENV 环境选择 + ZCLAW_SAAS_CONFIG 精确覆盖
- scheduler 配置集成到 TOML
237 lines
7.3 KiB
Markdown
237 lines
7.3 KiB
Markdown
# loco-rs 架构模式研究 — zclaw-saas 重构参考
|
||
|
||
> 本文档记录 loco-rs (https://loco.rs) 的架构模式分析,评估其在 zclaw-saas 重构中的适用性。
|
||
> 创建时间: 2026-03-29
|
||
|
||
## 1. loco-rs 概述
|
||
|
||
- **定位**: "Rust on Rails" — 约定优于配置的全栈 Web 框架
|
||
- **技术栈**: Axum + SeaORM + PostgreSQL/SQLite
|
||
- **成熟度**: ~8K+ GitHub stars, 131+ contributors
|
||
- **官方站点**: https://loco.rs
|
||
|
||
## 2. 10 个可借鉴模式及评估
|
||
|
||
### 2.1 模式一: Worker trait(后台任务抽象)⭐ 高价值
|
||
|
||
**loco-rs 实现**:
|
||
- 基于 `sidekiq-rs` 的 `Worker` trait,支持强类型参数
|
||
- 三种模式: `BackgroundQueue`(Redis 队列)、`BackgroundAsync`(进程内 tokio)、`ForegroundBlocking`(同步)
|
||
- Worker 在 `Hooks::connect_workers()` 中注册
|
||
- 支持重试、延迟执行
|
||
|
||
**zclaw-saas 现状**:
|
||
- `main.rs` 中用 `tokio::spawn` + 手动 `interval` 循环
|
||
- 无重试、无命名、无观察性
|
||
|
||
**建议**: 采用框架无关的 Worker trait,不依赖 Redis(先用 `tokio::spawn` + channel),未来可迁移到 Redis 队列。
|
||
|
||
```rust
|
||
// 建议实现
|
||
#[async_trait]
|
||
pub trait Worker: Send + Sync + 'static {
|
||
type Args: Serialize + DeserializeOwned + Send + Sync;
|
||
fn name(&self) -> &str;
|
||
async fn perform(&self, db: &PgPool, args: Self::Args) -> Result<(), SaasError>;
|
||
fn max_retries(&self) -> u32 { 3 }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.2 模式二: 声明式 Scheduler ⭐ 高价值
|
||
|
||
**loco-rs 实现**:
|
||
- YAML 配置的 cron/interval 定时任务
|
||
- 支持 shell 命令或注册的 Task
|
||
- 按环境配置、按 tag 分组
|
||
|
||
**zclaw-saas 现状**:
|
||
- 硬编码 `tokio::spawn` + `Duration::from_secs(300)` 等
|
||
- 修改定时策略需要改代码重新编译
|
||
|
||
**建议**: 用 TOML 配置 scheduler(与项目已有 TOML 基础一致),支持 interval 表达式。
|
||
|
||
```toml
|
||
[scheduler]
|
||
jobs = [
|
||
{ name = "cleanup_rate_limit", interval = "5m", task = "cleanup_rate_limit" },
|
||
{ name = "cleanup_devices", interval = "24h", task = "cleanup_devices" },
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
### 2.3 模式三: 类型化数据库模型 (FromRow) ⭐⭐ 最高价值
|
||
|
||
**loco-rs 实现**:
|
||
- SeaORM 实体自动生成(`sea-orm-cli generate entity`)
|
||
- 强类型字段、关系、验证
|
||
|
||
**zclaw-saas 现状**:
|
||
- 原始元组解构 `(String, String, String, ...)`
|
||
- 无编译期字段检查,容易出错
|
||
|
||
**建议**: 不使用 SeaORM,但用 `#[derive(sqlx::FromRow)]` 实现同样效果。
|
||
|
||
```rust
|
||
// Before (当前)
|
||
let (id, username, email, role, status) = sqlx::query_as::<_, (String, String, String, String, String)>(...)
|
||
// 5 个 String 参数,顺序错误编译器无法发现
|
||
|
||
// After (目标)
|
||
#[derive(sqlx::FromRow)]
|
||
pub struct AccountRow {
|
||
pub id: String,
|
||
pub username: String,
|
||
pub email: String,
|
||
pub role: String,
|
||
pub status: String,
|
||
pub created_at: DateTime<Utc>,
|
||
}
|
||
let row = sqlx::query_as::<_, AccountRow>("SELECT ...").fetch_one(db).await?;
|
||
```
|
||
|
||
---
|
||
|
||
### 2.4 模式四: SQL 迁移系统 ⭐ 高价值
|
||
|
||
**loco-rs 实现**:
|
||
- 迁移优先,先写 migration,再从 DB 自动生成实体
|
||
- `sea-orm-cli migrate` 管理版本
|
||
|
||
**zclaw-saas 现状**:
|
||
- `db.rs` 内联 `SCHEMA_SQL` 常量,每次启动执行
|
||
- 无回滚能力,DDL 混在应用代码中
|
||
- `SCHEMA_VERSION = 5` 手动追踪
|
||
|
||
**建议**: 使用 `sqlx-cli` 的迁移系统。
|
||
|
||
```
|
||
migrations/
|
||
├── 20260329000001_initial_schema.sql
|
||
├── 20260329000002_fix_timestamps.sql
|
||
└── 20260329000003_add_indexes.sql
|
||
```
|
||
|
||
---
|
||
|
||
### 2.5 模式五: CLI Task 系统 ⭐ 中等价值
|
||
|
||
**loco-rs 实现**:
|
||
- `Task` trait 用于手动执行运维操作
|
||
- `cargo loco task <NAME>` 命令行调用
|
||
- 支持参数传递
|
||
|
||
**zclaw-saas 现状**:
|
||
- 运维任务(seed admin、schema 初始化)嵌入在 `db.rs` 的 `init_db()` 中
|
||
- 无法手动触发、无法传参
|
||
|
||
**建议**: 添加 Task trait + CLI 解析。
|
||
|
||
```rust
|
||
#[async_trait]
|
||
pub trait Task: Send + Sync {
|
||
fn name(&self) -> &str;
|
||
fn description(&self) -> &str;
|
||
async fn run(&self, state: &AppState, args: &HashMap<String, String>) -> SaasResult<()>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 2.6 模式六: 多环境配置 ⭐ 中等价值
|
||
|
||
**loco-rs 实现**:
|
||
- `development.yaml` / `production.yaml` / `test.yaml`
|
||
- 通过 `LOCO_ENV` 环境变量选择
|
||
- 支持 Tera 模板语法引用环境变量
|
||
|
||
**zclaw-saas 现状**:
|
||
- 单个 `saas-config.toml` 文件
|
||
- 通过 `ZCLAW_SAAS_CONFIG` 路径覆盖
|
||
|
||
**建议**: 采用环境分离配置。
|
||
|
||
```
|
||
config/
|
||
├── saas-development.toml
|
||
├── saas-production.toml
|
||
└── saas-test.toml
|
||
```
|
||
|
||
---
|
||
|
||
### 2.7 模式七: 可配置中间件栈 ⭐ 低价值
|
||
|
||
**loco-rs 实现**:
|
||
- YAML 配置 CORS、timeout、payload limit 等中间件参数
|
||
- Per-handler 和 per-route-group 的中间件层
|
||
|
||
**zclaw-saas 现状**:
|
||
- CORS origins、timeout 等硬编码在 `main.rs` 的 `build_router()` 中
|
||
|
||
**建议**: 当前优先级低,后续可将中间件参数移入 TOML 配置。
|
||
|
||
---
|
||
|
||
### 2.8 模式八: 集成测试辅助 ⭐ 中等价值
|
||
|
||
**loco-rs 实现**:
|
||
- `request::(|request, ctx| async { ... })` 风格的请求测试
|
||
- 自动启动测试 app + 测试数据库
|
||
- `assert_debug_snapshot!` 快照测试
|
||
|
||
**zclaw-saas 现状**:
|
||
- 基础的 `tests/account_test.rs`
|
||
|
||
**建议**: 添加测试辅助工具(app 启动 + 请求发送 + fixture)。
|
||
|
||
---
|
||
|
||
### 2.9 ~~模式九: Fat Model~~ — 不建议采用
|
||
|
||
**loco-rs 实现**: 业务逻辑放在 `impl ActiveModel` 中(Active Record 模式)
|
||
**原因**: 依赖 SeaORM 实体结构。zclaw-saas 使用 SQLx,Service Layer 模式更适合。
|
||
|
||
### 2.10 ~~模式十: SeaORM / YAML 配置~~ — 不建议采用
|
||
|
||
**原因**:
|
||
- SeaORM 引入 ORM 抽象层,与现有 SQLx 原始查询冲突
|
||
- YAML 在 Rust 生态中不如 TOML 类型安全
|
||
- loco-rs creator jondot 确认无增量集成路径,必须创建新项目迁移
|
||
|
||
## 3. zclaw-saas 与 loco-rs 架构对照
|
||
|
||
| 维度 | loco-rs | zclaw-saas (当前) | 建议 |
|
||
|------|---------|-------------------|------|
|
||
| Web 框架 | Axum | Axum | 保持不变 |
|
||
| 数据库层 | SeaORM (ORM) | SQLx (原始查询) | 保持 SQLx + FromRow |
|
||
| 后台任务 | sidekiq-rs (Redis) | tokio::spawn (手动) | **引入 Worker trait** |
|
||
| 定时任务 | YAML Scheduler | 硬编码 interval | **引入 TOML Scheduler** |
|
||
| 数据库模型 | 自动生成实体 | 元组解构 | **引入 FromRow** |
|
||
| 迁移系统 | sea-orm-cli | 内联 SQL | **引入 sqlx-cli** |
|
||
| 错误处理 | 统一 Error enum | SaasError (thiserror) | 已优秀,保持不变 |
|
||
| 配置系统 | YAML per-env | TOML 单文件 | **引入多环境配置** |
|
||
| CLI 任务 | Task trait | 嵌入 init 代码 | **引入 Task trait** |
|
||
| 测试 | 请求测试辅助 | 基础测试 | **引入测试辅助** |
|
||
|
||
## 4. 实施优先级
|
||
|
||
| 优先级 | 模式 | 价值 | 工作量 |
|
||
|--------|------|------|--------|
|
||
| 1 | FromRow 类型化模型 + 迁移系统 | 最高 | 中 |
|
||
| 2 | Worker trait 后台任务 | 高 | 低 |
|
||
| 3 | 声明式 Scheduler | 高 | 低 |
|
||
| 4 | CLI Task 系统 | 中 | 低 |
|
||
| 5 | 多环境配置 | 中 | 低 |
|
||
| 6 | 集成测试辅助 | 中 | 中 |
|
||
| 7 | 可配置中间件 | 低 | 低 |
|
||
|
||
## 5. 参考
|
||
|
||
- [loco.rs 官方文档](https://loco.rs/docs/the-app/)
|
||
- [loco-rs GitHub](https://github.com/loco-rs/loco)
|
||
- [Reddit: 如何从 Axum 迁移到 loco-rs](https://www.reddit.com/r/rust/comments/1g9hc1z/how_to_integratemigrate_to_locors_from_axum_app/)
|
||
- [Shuttle.dev: 介绍 Loco](https://www.shuttle.dev/blog/2023/12/20/loco-rust-rails)
|