架构设计
gf 工作区布局与设计理念
工作区布局
gf/
├── apps/
│ └── cli/ # Binary crate — CLI 入口
├── crates/
│ ├── core/ # Library crate — 公开 API、领域类型
│ ├── github/ # GitHub 平台适配器
│ ├── gitlab/ # GitLab 平台适配器
│ └── gitcode/ # GitCode 平台适配器
├── docs/ # 项目文档
├── specs/ # 功能规格说明
├── Cargo.toml # 工作区清单
├── Makefile # 自动化目标
└── CLAUDE.md # Agent 指南 Crate 职责
crates/ — 库
库 crate 包含公开 API、领域类型、业务逻辑和核心抽象。它们:
- 可复用:其他工具和库可以依赖它们
- 可测试:单元测试和文档测试覆盖逻辑,无需二进制文件
- 版本化:每个库 crate 有自己的 semver、变更日志条目和 API 稳定性保证
主库 crate 是 crates/core,它暴露:
- 领域类型(带
Debug、Serialize/Deserialize、转换 trait) - 基于
thiserror的错误枚举 - 核心操作的纯函数和异步工作流
apps/ — 二进制
二进制 crate 是薄入口点。它们:
- 通过
clap解析 CLI 参数 - 加载和合并配置
- 设置日志和信号处理器
- 连接库并调用
crates/core - 处理进程退出码
二进制应包含最少逻辑。如果函数复杂到需要单元测试,它应该属于库 crate。
依赖流向
apps/cli ──depends on──> crates/core 依赖箭头是单向的:二进制依赖库,永远不会反向。crates/core 不能依赖任何 apps/ crate。这确保了:
- 编译时隔离:修改二进制不会重新编译库
- API 边界:库不知道 CLI 标志、配置文件路径或日志后端
- 可测试性:库测试快速且确定性强;不需要 CLI 工具
何时添加新 Crate vs 新模块
| 情况 | 操作 |
|---|---|
| 新领域类型或纯逻辑 | 在 crates/core 添加 pub mod |
| 多个二进制复用的功能 | 在 crates/ 下新建库 crate |
| 新二进制(CLI、守护进程、迁移工具) | 在 apps/ 下新建 crate |
| 私有实现细节 | 相关 crate 中的 mod(非 pub) |
| 第三方集成(如数据库) | 如果引入大量依赖则新建库 crate;否则作为 core 中 feature flag 后的模块 |
Crate 拆分指南
拆分为新库 crate 的情况:
- 模块有显著不同的依赖(如
crates/db使用sqlx) - 模块有独立的发布周期
- 多个二进制需要它但
core不需要 - 编译单元足够大,可以从并行构建中受益
保持模块在一起的情况:
- 它们共享相同的依赖图
- 它们一起演进并共享类型
- API 表面很小,crate 数量的开销不合理
设计原则
Platform Trait 抽象
通过 Platform trait 统一三平台差异,依赖单向流动,禁止循环依赖。
安全优先
forbid(unsafe_code),生产代码禁 unwrap()/expect(),使用 SafePath 防止路径遍历。
可测试性
每个模块可独立测试,契约测试覆盖适配器,测试命名 test_should_<expected_behavior>。
工程纪律
pedantic clippy、零 TODO/FIXME、missing_docs 警告、所有公开 API 必须有文档。
深入了解代码
查看完整的架构设计和代码实现