Appearance
为什么我选择集中式目录结构
项目的第一层目录结构,是架构设计的具象体现。它决定了代码如何被组织、如何被理解、以及如何演进。在为我的VitePress技术博客规划文件组织时,我面临一个根本性的架构选择:采用集中式的结构,还是模块化的布局?
这个决策远不止是“把文件放在哪”的表面问题,而是关于如何平衡当下效率与未来扩展性的深度思考。经过系统性的分析,我坚定地选择了集中式管理作为项目的起点。以下是支撑这一决策的完整架构推演过程。
一、核心架构原则:适合上下文的才是最优的
在软件工程中,一个常见误区是盲目套用“最佳实践”。真正的架构智慧,在于识别当前上下文中的核心约束,并做出针对性的权衡。
我的博客项目当前面临的核心约束是:
- 单人开发团队:从设计、开发到部署,全流程由我一人负责
- 快速验证阶段:需要尽快产出可访问的MVP,验证内容方向和架构可行性
- 内容驱动为主:核心是技术文章的呈现与展示,交互复杂度可控
- 明确的演进路径:计划从简单博客逐步迭代为包含作品展示、AI功能的知识门户
基于这些约束,我对比了两种主流的项目结构范式。
二、两种结构的本质区别
集中式结构(我选择的方案)
docs/
├── types/ # 所有类型定义集中存放
├── components/ # 所有可复用组件
├── composables/ # 所有组合式函数
├── utils/ # 所有工具函数
├── styles/ # 所有样式文件
├── data/ # 所有静态数据
├── essays/ # 庖丁解牛 - 按内容组织
├── projects/ # 好奇你就点点 - 按内容组织
└── tutorials/ # 和尚念经 - 按内容组织核心特征:按技术职能(类型、组件、工具等)横向切分,内容模块保持独立。
模块化结构(暂时不选的方案)
docs/
├── modules/
│ ├── project/ # 作品展示模块
│ │ ├── types.ts
│ │ ├── components/
│ │ ├── composables/
│ │ ├── data.json
│ │ └── pages/
│ ├── blog/ # 博客文章模块
│ │ ├── types.ts
│ │ ├── components/
│ │ └── pages/
│ └── shared/ # 真正跨模块共享的内容
│ ├── types/
│ └── utils/核心特征:按业务功能纵向切分,每个模块内聚所有相关资源。
三、架构决策分析:为什么集中式更适合当前阶段
1. 从团队规模出发:单人开发的效率优先
模块化的主要价值在于降低多团队协作的沟通成本。当多个团队并行开发不同功能模块时,清晰的物理边界可以:
- 减少代码冲突
- 明确职责归属
- 支持独立构建和部署
然而,对于单人开发的场景,这些优势变成了负担:
- 认知负荷增加:我需要记住十几个模块的入口路径,而非几个集中的目录
- 开发流程繁琐:修改一个类型,可能需要跨越多个模块目录进行同步更新
- 重复代码风险:很容易在不同模块中重复定义相似的组件或工具
集中式结构为单人开发提供了“最小认知路径”。所有的类型都在 types/ 目录下,所有的组件都在 components/ 目录下——这种一致性大幅降低了开发时的决策成本。
2. 从项目性质出发:内容站点的技术特点
我的博客本质上是一个内容驱动、展示优先的静态站点,而非复杂的Web应用。
- 数据模型稳定且有限:核心就是文章、项目、教程几类实体
- 组件复用模式清晰:
ProjectCard、BlogHeader等组件会在多个页面被复用 - 共享依赖密集:样式变量、工具函数、类型定义被大量跨页面使用
在这种场景下,集中式管理创造了“单一事实来源”:
typescript
// 任何地方都从同一个地方导入
import { Project } from '@types'
import { formatDate } from '@utils'
import ProjectCard from '@components/ProjectCard.vue'这种一致性避免了“在A模块修改了类型,忘记在B模块更新”的同步问题,这在快速迭代的早期阶段尤其重要。
3. 从技术栈出发:VitePress的架构适配
VitePress有其独特的架构特点:
- 约定大于配置:默认的目录结构已经为文档站点优化
- Markdown为中心:内容文件(
.md)是首要公民 - 主题系统轻量:扩展主要通过主题组件实现
强行将大型SPA的模块化架构套用在VitePress上,会产生架构失配:
- VitePress的构建优化可能无法充分利用模块边界
- Markdown文件的组织逻辑(按内容分类)与按功能模块的组织逻辑会产生冲突
- 增加了不必要的配置复杂度
集中式结构则与VitePress的哲学更加契合:它尊重内容的核心地位,同时在技术层面提供清晰的隔离层。
4. 从演进路径出发:为变化做好准备
优秀的架构不是预测所有变化,而是在变化来临时能够低成本地响应。
我当前的选择包含明确的演进触发条件:
typescript
// 当前:集中式管理
docs/
├── types/
│ ├── project.ts // Project相关类型
│ ├── blog.ts // Blog相关类型
│ └── tutorial.ts // Tutorial相关类型
└── components/
└── ProjectCard.vue
// 未来:当Project类型变得足够复杂时
docs/
├── modules/ // 演进为模块化
│ └── project/
│ ├── types.ts // Project专属类型
│ ├── components/
│ │ └── ProjectCard.vue
│ └── utils/
└── shared/ // 真正共享的内容
├── types/
└── utils/具体的演进信号包括:
- 单一模型复杂化为子域:当
Project类型衍生出OpenSourceProject、CommercialProject等多个变体,并附带大量业务逻辑时 - 功能模块具备独立交付价值:如“AI对话助手”组件计划发布为独立NPM包
- 团队规模扩大:从单人开发变为多人协作时
关键洞察:从集中式重构到模块化的成本,远低于过早模块化带来的持续维护成本。
四、架构思考:平衡的艺术
1. 过度设计 vs. 设计不足
架构中最难把握的是程度。过早模块化是过度设计——为不存在的协作问题和复杂度做准备;完全不考虑可扩展性是设计不足——当变化来临时重构成本高昂。
我的选择是渐进式设计:从简单的集中式开始,在明确的信号出现时进行重构。
2. 认知协调成本
在架构决策中,我们常常忽略认知成本。对于单人项目,保持极低的认知负荷就是最高效的。集中式结构让整个项目的知识图谱变得扁平且直观。
3. 技术债务的重新定义
传统观念认为,选择“不够先进”的模式就是积累技术债务。但我认为:在错误的时间采用“先进”模式,同样是技术债务——你为不需要的复杂性支付了持续利息。
五、具体实施方案
基于以上分析,我实施了以下结构:
docs/
├── .vitepress/ # VitePress配置
│ ├── config/
│ ├── theme/
│ │ ├── components/ # 全局Vue组件
│ │ └── styles/ # 全局样式
│ └── utils/ # VitePress特定工具
├── types/ # 全局TypeScript类型定义
├── composables/ # Vue组合式函数
├── utils/ # 纯工具函数(框架无关)
├── data/ # 静态数据(JSON等)
├── essays/ # 庖丁解牛 - 技术思考
├── projects/ # 好奇你就点点 - 作品展示
└── tutorials/ # 和尚念经 - 系统教程这个结构的核心特征是:
- 技术资源水平分层:相同类型的技术资产放在同一层级
- 内容垂直组织:不同类型的内容有自己的专属目录
- 明确的导入路径:通过别名(
@types、@utils)提供清晰导入
六、总结:在正确的时间做正确的决策
这次目录结构的决策,是一次典型的架构权衡实践:
- 我选择了集中式,因为它最匹配当前“单人、快速构建、内容驱动”的核心诉求
- 我拒绝了模块化,因为它为不存在的协作问题提前支付了认知和维护成本
- 我保留了演进能力,通过清晰的代码组织和明确的演进信号,确保未来可以低成本重构
架构的真正价值,不在于采用了多么“先进”的模式,而在于在特定上下文约束下,做出了最合适的权衡。