架构设计

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,它暴露:

  • 领域类型(带 DebugSerialize/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 的情况:

  1. 模块有显著不同的依赖(如 crates/db 使用 sqlx
  2. 模块有独立的发布周期
  3. 多个二进制需要它但 core 不需要
  4. 编译单元足够大,可以从并行构建中受益

保持模块在一起的情况:

  1. 它们共享相同的依赖图
  2. 它们一起演进并共享类型
  3. API 表面很小,crate 数量的开销不合理

设计原则

Platform Trait 抽象

通过 Platform trait 统一三平台差异,依赖单向流动,禁止循环依赖。

安全优先

forbid(unsafe_code),生产代码禁 unwrap()/expect(),使用 SafePath 防止路径遍历。

可测试性

每个模块可独立测试,契约测试覆盖适配器,测试命名 test_should_<expected_behavior>

工程纪律

pedantic clippy、零 TODO/FIXME、missing_docs 警告、所有公开 API 必须有文档。

深入了解代码

查看完整的架构设计和代码实现