Node.js 项目既可能是一个十几行脚本,也可能是一个大型服务端系统或多包仓库。不同规模、不同目标的项目,目录结构和职责拆分方式并不一样。这一篇会把脚本型项目、库项目、服务项目和 Monorepo 的组织思路放到同一张图里看。
为什么项目结构不能一刀切?
因为不同项目解决的问题根本不一样。一个一次性工具脚本、一个面向外部使用的 npm 包、一个持续运行的 API 服务,它们最关心的事情完全不同。
比如:
- 脚本更关心输入输出和执行入口;
- 库更关心 API 暴露边界和版本兼容;
- 服务更关心分层、配置、生命周期和部署;
- Monorepo 更关心多包协作和共享基础设施。
如果你拿同一套目录结构去套所有问题,最后通常不是过度设计,就是结构很快失控。
脚本、库和服务分别该怎么想?
脚本型项目最重要的是简单和直接。它不一定需要层层抽象,但依然要尽量把输入、输出和辅助逻辑分清楚。只要脚本开始变长、开始反复复用,拆文件和提炼模块就会变得必要。
库项目的重点则不同。它更像是在向外暴露一套稳定能力,因此需要更重视:
- 对外 API 边界;
- 文档与示例;
- 类型或接口契约;
- 版本管理和兼容性。
服务项目则又不一样。它天然会涉及:
- 请求入口;
- 业务分层;
- 数据访问;
- 配置和环境;
- 运行与部署。
这意味着服务项目比脚本和库都更需要明确职责边界。
Monorepo 什么时候值得引入?
Monorepo 最大的诱惑在于:多个相关包和应用可以放在一个仓库里统一管理。但它的收益和成本都很真实。
它的典型收益包括:
- 共享代码和配置更方便;
- 多包协作更直接;
- 工具链和质量约束更容易统一。
而它的成本则包括:
- 仓库结构和工具链复杂度上升;
- 初期配置和维护门槛变高;
- 边界不清时更容易互相污染。
所以,一个更稳妥的原则是:只有当你真的存在多个强相关包或应用,并且共享收益明显高于管理成本时,再考虑 Monorepo。不要为了“看起来高级”而提前引入它。
结构选择背后真正该看的是什么?
很多人讨论项目结构时,容易执着于“目录长什么样”,但更本质的判断其实是:
- 这个项目的主要职责是什么;
- 哪些部分会一起变化;
- 哪些能力需要对外暴露;
- 哪些边界必须稳定。
换句话说,目录结构只是结果,不是起点。真正决定结构的,是职责划分和协作方式。
总结
这一篇我们把几种常见 Node 项目形态放到同一视角下理解了:脚本追求简单直接,库强调对外边界,服务更关注分层和生命周期,而 Monorepo 适合多包协作但也自带复杂度。
只要你开始从“问题类型”而不是“目录模板”出发,项目结构这件事就会清晰很多。
下一篇我们会继续补上另一个高频工程问题,也就是配置管理该如何做得清楚、稳定、可协作。