目录结构设计原则:模块化、版本隔离、平台分离、依赖清晰、用户友好

说实话,我见过太多 SDK 项目死在目录结构上。不是代码写得不好,而是开发者打开压缩包就懵了——文件散落一地,不知道从哪开始看。我自己早期也踩过这个坑,一个 SDK 塞了十几个文件夹,连我自己三个月后都找不到头绪。

后来我总结出五条原则。每次设计目录结构前,我都会拿这五条过一遍。今天咱们一条一条拆开聊。

核心观点:目录结构不是「怎么放文件」,而是「怎么让用户和系统都舒服」。五条原则缺一不可。

模块化:每个功能块独立成包

模块化说白了就是「高内聚、低耦合」。每个模块只做一件事,并且做好。我习惯把 SDK 拆成这么几块:

  • 核心层(core)——基础数据结构、公共工具函数、错误码定义
  • 功能层(features)——具体业务能力,比如登录、支付、推送
  • 接口层(api)——对外暴露的公共 API,用户直接调用的入口
  • 内部实现(internal)——不对外暴露的实现细节,比如网络请求、数据库操作

举个例子。我做过一个支付 SDK,一开始把签名逻辑、网络请求、UI 弹窗全塞在一个文件夹里。后来要支持新支付渠道,改一个地方崩三个功能。重构后按模块拆分,每个渠道独立文件夹,改支付完全不影响支付宝。这才算真正解耦。

小技巧:模块目录名用名词,不要用动词。比如用 payment/ 而不是 doPayment/。用户找起来更直观。

版本隔离:别让旧版本污染新项目

版本隔离这件事,我吃过一次大亏。有一次发布 SDK 时,不小心把旧版本的动态库留在包里,用户集成后链接了旧版本,跑出来一堆诡异崩溃。查了两天才发现是版本冲突。

现在我的做法很简单:每个大版本独立目录,版本号写在文件夹名里。

sdk/
├── v1.0.0/
│   ├── lib/
│   ├── include/
│   └── docs/
├── v2.0.0/
│   ├── lib/
│   ├── include/
│   └── docs/
└── v3.0.0/
    ├── lib/
    ├── include/
    └── docs/

用户想用哪个版本,直接复制对应目录就行。不会出现「我明明升级了怎么还是旧代码」的尴尬。

注意:版本目录里不要放 latest 或 current 这种软链接。很多用户会直接引用这个路径,一旦你更新了链接指向,他们的构建就不可复现了。

平台分离:不同平台各走各的路

SDK 经常要支持多个平台——Android、iOS、Windows、macOS、Linux。如果混在一起,用户找文件就像大海捞针。

我建议按平台分顶层目录:

sdk/
├── android/
│   ├── libs/
│   └── src/
├── ios/
│   ├── Frameworks/
│   └── Sources/
├── windows/
│   ├── bin/
│   └── lib/
└── linux/
    ├── lib/
    └── include/

每个平台目录内部再按模块组织。这样用户一眼就知道「我是 iOS 开发者,只看 ios/ 文件夹就行」。

我记得有一次,一个用户反馈说「你们的 Windows 版 SDK 怎么有 .dylib 文件?」我一看,原来是平台目录没分开,iOS 的库混进了 Windows 包里。从那以后,平台分离就成了我的铁律。

依赖清晰:让用户一眼看懂需要什么

依赖管理是 SDK 发布中最容易被忽视的环节。用户拿到 SDK,第一件事就是看「我还需要装什么?」

我习惯在 SDK 根目录放一个 dependencies/ 文件夹,里面按模块列出所有外部依赖:

dependencies/
├── core/
│   ├── openssl-1.1.1/
│   └── zlib-1.2.11/
├── payment/
│   ├── libcurl-7.79.1/
│   └── jsoncpp-1.9.5/
└── push/
    ├── protobuf-3.19.0/
    └── libwebsockets-4.3.0/

同时,每个模块目录下放一个 DEPENDENCIES.md 文件,写明依赖的版本、用途、以及可选的替代方案。

建议:如果依赖是开源的,直接附上 LICENSE 文件。很多公司有法务审核,缺了许可证文件会卡流程。

用户友好:从用户视角出发

最后这条最容易被忽略。你想想看,用户第一次打开你的 SDK 压缩包,他最先想找什么?

  1. 快速开始文档
  2. 示例代码
  3. API 参考
  4. 库文件
  5. 头文件

所以根目录应该长这样:

sdk/
├── README.md              # 一句话介绍 + 快速链接
├── GETTING_STARTED.md     # 5 分钟集成指南
├── examples/              # 可直接运行的示例项目
├── docs/                  # 完整文档
├── api/                   # 公共头文件
├── lib/                   # 编译好的库文件
└── CHANGELOG.md           # 版本更新记录

我见过最离谱的 SDK,根目录放了 30 多个文件夹,连个 README 都没有。用户得一个个点开猜用途。这不是技术问题,是态度问题。

嗯,这里要注意:示例代码一定要能直接跑起来。我见过太多 SDK 的示例代码缺依赖、缺配置、甚至缺文件。用户跑不通示例,第一反应就是「这 SDK 有问题」。

五条原则的关系图

下面这张图是我自己画的结构,帮你理清五条原则之间的层级关系:

目录结构设计五原则 SDK 目录结构 模块化 版本隔离 平台分离 依赖清晰 用户友好 各原则的落地要点 • 模块化:每个功能独立目录,不跨文件夹引用 • 版本隔离:大版本号作为目录名,不混用 • 平台分离:顶层按平台分,内部结构保持一致 • 依赖清晰:集中管理依赖,每个模块附带依赖说明 • 用户友好:根目录放 README、示例、快速开始文档 五条原则缺一不可,顺序按优先级排列

一个完整的目录结构示例

最后,我把五条原则整合成一个完整的 SDK 目录结构。你可以直接拿来当模板:

my-sdk/
├── README.md
├── GETTING_STARTED.md
├── CHANGELOG.md
├── LICENSE
│
├── v1.0.0/
│   ├── android/
│   │   ├── libs/
│   │   ├── src/
│   │   └── build.gradle
│   ├── ios/
│   │   ├── Frameworks/
│   │   └── Sources/
│   └── docs/
│       ├── api-reference/
│       └── migration-guide/
│
├── v2.0.0/
│   ├── android/
│   ├── ios/
│   └── docs/
│
├── examples/
│   ├── android/
│   ├── ios/
│   └── web/
│
├── dependencies/
│   ├── core/
│   └── features/
│
└── tools/
    ├── code-generator/
    └── dependency-checker/

这个结构我用了好几年,团队新成员上手很快,用户反馈也很好。说白了,目录结构设计不是炫技,是替用户省时间。你替用户省了时间,用户就会信任你的 SDK。