Rust 项目结构与分层
bin/lib 拆分、模块分层、依赖方向与中小型服务的推荐骨架。
#synthesis
#growing
#dev
#rust
[!info] related notes
Rust 项目结构与分层
这个概念为什么出现
能编译不等于可维护。
可以约定入口、领域与接口适配器的位置及依赖方向;小工具不必预先建立所有层。
[!abstract] 一句话理解 二进制保持瘦入口;核心逻辑放 library;按领域模块划分;外层依赖内层,避免循环与“utils 垃圾场”。
分层示意与独立可运行函数
场景:订单服务骨架目录
order-service/
Cargo.toml
src/
main.rs # 组装与启动
lib.rs # 核心导出
domain/ # 实体与规则
mod.rs
order.rs
app/ # 用例服务
mod.rs
infra/ # 文件/HTTP/DB 适配
mod.rs
tests/
smoke.rs
main.rs 只做:读配置 → 构造服务 → 跑。
domain 不依赖 infra。这是可选架构约定,不是 Rust 语法要求;只有目录不会自动声明模块,还需在 lib.rs/mod.rs 中声明 mod 并控制可见性。下面是独立函数示例,不是完整目录工程。
// 示意:领域纯函数不依赖框架
pub fn can_cancel(status: &str) -> bool {
status == "created" || status == "paid"
}
fn main() {
println!("cancel_allowed={}", can_cancel("paid"));
}
结合场景再看三个关注点
- 可测性:domain 无 IO
- 替换 infra 不影响规则
- workspace 可再拆 crate
核心概念与准确模型
- 依赖方向:
main → app → domain,infra →实现接口 - feature 开关可选能力
- examples/ 放可运行样例
- benches/ 放基准
设计动机
- 长期演进
- 测试金字塔可行
- 团队导航成本下降
边界与误区
- 过早微服务/过多 crate
- crate 的普通依赖图不能形成循环;同一 crate 内模块互相引用不等于 Cargo 循环依赖,分层约束需另外审查
- 把框架类型漏进 domain
[!warning] 常见误区:所有代码塞 main.rs 随业务复杂度增长可提取可测试的 lib API;短小单文件 CLI 无需为形式分层。
工程实践
- 先单 crate 分层,再 workspace
- 公共 API 最小化
- README 画模块图
- 通过可见性、crate 边界和定向检查维护分层;cargo-deny 检查依赖的许可证、来源等策略,不自动检查 src 内的模块依赖方向
本节总结
- 瘦 bin 厚 lib
- 领域独立
- 依赖单向
自测题
- 为什么 domain 不该依赖具体 DB crate?
tests/放什么?
参考答案
- 便于替换与单测,避免基础设施细节污染规则。
- 面向公共 API 的集成/端到端级测试。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Cargo project layout | 官方 | 目录约定 |
| The Book — Packages | 官方书 | 组织 |