独立开发者多项目架构:共享模块+AI串联,我为什么放弃Monorepo

0 阅读1分钟

我手上有两个上线产品——智枢矩阵(智能表单生成平台)和智播坊(口播视频生成平台),还有一个浏览器插件、一个AI对话实验项目,加上日常维护的开源仓库和官网。一个人,六个项目。

如果你也是独立开发者,大概率也遇到过这个问题:项目越做越多,代码怎么组织?

我花了三个月试了三种架构方案,最后选了一个最"笨"的——不搞Monorepo,不搞微服务,用一套统一技术栈 + 提炼共享模块 + 让AI当胶水

这篇文章把三种方案的取舍、最终架构的设计、以及一个人怎么维护多项目的心法,完整拆开。

一、问题的本质:不是代码组织,是认知负荷

先说一个反直觉的结论:独立开发者的多项目架构,核心矛盾不是代码复用,是认知切换成本

团队开发可以按项目分人,每个人只管自己的代码库。独立开发者今天改表单平台,明天调视频平台,后天又得修官网bug。每次切换都要重新回忆:这个项目用的什么数据库?路由结构是什么?Auth怎么写的?

如果两个项目的目录结构、技术栈、命名风格完全不同,每次切换就像换了一个公司。这个认知负荷才是真正吃人的东西。

所以我的架构原则只有一条:让所有项目长得一模一样,直到不得不不同的地方才不同

二、三种方案对比

2.1 方案一:Monorepo

所有项目放一个仓库,用pnpm workspace或Turborepo管理。

zhishu-matrix/
├── packages/
│   ├── shared/          # 共享模块
│   ├── form-platform/   # 表单平台
│   └── video-platform/  # 视频平台
├── pnpm-workspace.yaml
└── turbo.json

优点:代码共享最彻底,依赖版本统一,一个命令装完所有包。

放弃的原因

1. 工具链太重。Turborepo的缓存配置、pnpm workspace的依赖拓扑、TypeScript的project references……光是让这个结构跑起来就花了两天。一个人维护六个项目的构建管线,ROI太低。

2. 部署耦合。表单平台改了一个共享模块的类型定义,视频平台的CI也得跑一遍。独立开发者最怕的就是"改A崩B"。

3. 学习成本高。Monorepo的坑大部分是"你以为改了但其实没生效"——缓存没清、依赖没link、构建产物是旧的。这种玄学bug对团队来说有人帮你查,对独立开发者来说就是纯浪费时间。

结论:Monorepo是给团队设计的,不是给一个人设计的。两个人的团队可以考虑,一个人就是给自己加活。

2.2 方案二:完全独立

每个项目完全独立的代码库、独立的技术选型、独立的部署。

优点:零耦合,改谁都不影响谁。

放弃的原因

1. 重复劳动太多。两个项目都要写登录注册、文件上传、AI调用封装、错误处理……第一次写是功能,第二次写就是浪费生命。

2. 认知切换成本最高。表单平台用Prisma+PostgreSQL,视频平台用SQLite;表单平台用JavaScript,视频平台用TypeScript。每次切换都要想"这个项目是哪个风格来着?"

3. bug修两遍。登录token过期的bug在表单平台修了,视频平台还是老的。一个人的精力不允许同一类bug修两次。

2.3 方案三:统一技术栈 + 共享模块(我的选择)

每个项目独立仓库,但强制统一技术栈,提炼公共模块作为NPM包或直接复制。

表单平台(独立仓库) 视频平台(独立仓库)

├── src/                    ├── src/
│   ├── components/         │  
├── components/
│   ├── utils/              │   ├── utils/
│   │  
└── shared-auth/    │   │  
└── shared-auth/   ← 共享模块
│   ├── views/              │   ├── views/
│   └── stores/             │  
└── stores/
├── server/                 ├── server/
│   ├── routes/             │  
├── routes/
│   ├── middleware/          │  
├── middleware/
│   │  
└── auth.js         │   │  
└── auth.js        ← 相同结构
│   └── services/           │  
└── services/
└── package.json            └──
package.json

核心思路:目录结构一样,技术栈一样,公共逻辑提炼成模块,项目特有的东西各自实现

三、统一技术栈:让所有项目"长得一样"

3.1 前端统一

两个产品线的前端技术栈完全一致:

{
  "dependencies": {
    "vue": "^3.4",
    "vue-router":
"^4.3",
    "pinia": "^2.1",
    "element-plus":
"^2.6",
    "tailwindcss":
"^3.4",
    "axios": "^1.6"
  },
  "devDependencies": {
    "vite": "^5.2",
    "@vitejs/plugin-vue":
"^5.0"
  }
}

为什么选这套?不是因为它最好,是因为它最不用想

• Vue3 + Vite:启动快,生态成熟,AI生成代码的兼容性最好

• Element Plus:表单和视频平台都需要大量UI组件,自己写不现实

• TailwindCSS:快速出样式,不需要纠结CSS架构

• Pinia:状态管理,Vue3的标准答案

这套组合的结果是:我在表单平台写的一个组件,复制到视频平台几乎不用改——import路径可能不同,但API和写法完全一样。

3.2 后端统一

后端也是统一选型:

选型

为什么统一

框架

Express

轻量,AI生成代码的兼容性好

数据库ORM

智枢矩阵用Prisma,智播坊用SQLite

这里没有统一,后面解释

认证

JWT

两个项目都是前后端分离,JWT最简单

AI调用

火山方舟

两个项目都需要大模型能力

文件存储

腾讯云COS

统一云厂商,CDN和鉴权逻辑可复用

3.3 为什么数据库没有统一

这是唯一没有统一的层,也是很多人会纠结的点。

智枢矩阵是多租户SaaS,需要关系型数据库的事务和复杂查询,选了Prisma + PostgreSQL。智播坊是单用户/小团队场景,数据结构简单,SQLite就够了,部署也更轻量——一个文件就是一个数据库,不用装PostgreSQL。

独立开发者的原则:能统一就统一,统一了反而更复杂的就别硬统一。 数据库这一层如果硬统一到PostgreSQL,智播坊的部署成本就上去了,一个SQLite文件能搞定的事非得跑一个数据库服务,不值。

四、共享模块设计:哪些该共享,哪些不该

4.1 共享决策树

我总结了一个简单的判断标准:

这段代码,在另一个项目里会不会也这么写?
├─ 不会 → 不共享,项目内实现
└─ 会 → 写法完全一样吗?
├─ 不一样 → 抽象接口,各自实现
└─ 一样 → 直接共享

举几个实际例子:

模块

会一样吗

共享方式

JWT认证中间件

完全一样

复制共享文件

AI调用封装

逻辑一样,模型参数不同

抽象基类 + 配置化

文件上传到COS

完全一样

复制共享文件

错误处理中间件

格式一样,状态码映射略有不同

复制 + 项目内扩展

表单Schema解析

只在表单平台用

不共享

FFmpeg视频处理

只在视频平台用

不共享

4.2 实际的共享模块:AI调用封装

两个项目都调用火山方舟的API,但参数不同。我设计了一个基类:

// shared/ai-client.ts
interface AIConfig {
  endpoint: string
  model: string
  temperature: number
  maxTokens: number
  systemPrompt: string
}

class AIClient {
  private config: AIConfig
  private apiKey: string

  constructor(config: AIConfig) {
    this.config = config
    this.apiKey =
process.env.VOLC_API_KEY!
  }

  async chat(messages: Array<{role:
string, content: string}>) {
    const response = await
fetch(this.config.endpoint, {
      method: 'POST',
      headers: {
        'Content-Type':
'application/json',
        'Authorization': `Bearer
${this.apiKey}`
      },
      body: JSON.stringify({
        model: this.config.model,
        messages: [
          { role: 'system', content:
this.config.systemPrompt },
          ...messages
        ],
        temperature:
this.config.temperature,
        max_tokens: this.config.maxTokens
      })
    })

    if (!response.ok) {
      throw new Error(`AI调用失败:
${response.status} ${response.statusText}`)
    }

    const data = await response.json()
    return
data.choices[0].message.content
  }

  // 流式调用(视频平台TTS需要)
  async chatStream(messages:
Array<{role: string, content: string}>) {
    const response = await
fetch(this.config.endpoint, {
      method: 'POST',
      headers: {
        'Content-Type':
'application/json',
        'Authorization': `Bearer
${this.apiKey}`
      },
      body: JSON.stringify({
        model: this.config.model,
        messages: [
          { role: 'system', content:
this.config.systemPrompt },
          ...messages
        ],
        temperature:
this.config.temperature,
        max_tokens:
this.config.maxTokens,
        stream: true
      })
    })

    return response.body // 返回ReadableStream,调用方自己处理
  }
}

表单平台的使用:

// 表单平台的AI配置
const formAI = new AIClient({
  endpoint:
'https://ark.cn-beijing.volces.com/api/v3/chat/completions',
  model: 'doubao-seed-1-6-251015',
  temperature: 0.7,
  maxTokens: 1200,
  systemPrompt:
buildFormSystemPrompt()  // 表单专用Prompt
})

视频平台的使用:

// 视频平台的AI配置
const videoAI = new AIClient({
  endpoint:
'https://ark.cn-beijing.volces.com/api/v3/chat/completions',
  model: 'doubao-seed-1-6-251015',
  temperature: 0.8,          // 视频文案需要更多创造性
  maxTokens: 2000,           // 文案通常比表单Schema长
  systemPrompt:
buildVideoSystemPrompt()  // 视频专用Prompt
})

同一个类,不同配置,两个项目各自初始化。如果以后要换模型、加重试逻辑、加token统计,改AIClient一个地方就行。

4.3 实际的共享模块:文件上传

腾讯云COS上传逻辑,两个项目完全一样:

// shared/cos-upload.ts
import COS from 'cos-nodejs-sdk-v5'

const cos = new COS({
  SecretId: process.env.COS_SECRET_ID,
  SecretKey: process.env.COS_SECRET_KEY,
})

export async function uploadToCOS(
  filePath: string,
  key: string,
  bucket: string =
process.env.COS_BUCKET!
): Promise<string> {
  await cos.putObject({
    Bucket: bucket,
    Region: process.env.COS_REGION!,
    Key: key,
    Body: fs.createReadStream(filePath),
  })

  return
`https://${bucket}.cos.${process.env.COS_REGION}.myqcloud.com/${key}`
}

export async function deleteFromCOS(key: string, bucket?: string) {
  await cos.deleteObject({
    Bucket: bucket || process.env.COS_BUCKET!,
    Region: process.env.COS_REGION!,
    Key: key,
  })
}

这段代码在两个项目里一字不差。如果你问我为什么不用npm包——两个项目用同一个共享文件,改了手动同步,比发包版本管理简单。NPM包的版本号、发布流程、semantic versioning对一个人来说太重了。

五、AI是胶水:不是架构组件,是效率乘数

很多人问:一个人怎么同时维护六个项目?

答案是:我不是一个人在维护,AI是第二个我

5.1 AI在架构中的角色

AI不是架构图上的一个组件,而是让这个架构成立的前提条件。没有AI,一个人根本不可能在三个月内完成两个全栈产品 + 周边设施。

我的开发模式是这样的:

1. 需求定义:跟DeepSeek对话,把模糊想法变成清晰需求文档

2. 编码实现:用CodeBuddy(AI编码助手)生成80%的代码,我负责架构决策和关键逻辑

3. 调试修bug:AI分析错误日志,我确认修复方向

4. 代码审查:让AI检查安全漏洞和性能问题

每个环节AI节省的时间不一样,但整体算下来,一个人干出了3-4个人的产出

5.2 统一技术栈让AI更高效

这里有一个很多人忽略的点:统一技术栈不只是为了你自己,也是为了让AI更高效

AI训练数据里Vue3+Vite+Element Plus的模式比Vue2+Webpack+自定义UI的模式多得多。当你用主流技术栈时,AI生成的代码质量明显更高,需要手动修改的地方更少。

这也是我选Element Plus而不是自己造UI的原因之一——AI知道怎么写Element Plus的代码,但不知道你的自定义组件库的API。

5.3 多项目的AI工作流

同时推进两个项目时,我的AI工作流是: 

上午:跟AI对话定义表单平台新功能需求
  ↓
AI生成代码骨架 → 我审查+补关键逻辑 → 提交
  ↓
下午:切换到视频平台
  ↓
跟AI对话定义视频平台新功能需求
  ↓
AI生成代码骨架 → 我审查+补关键逻辑 → 提交

因为两个项目的技术栈和结构一样,AI的上下文切换几乎没有成本。如果表单平台用Vue3+Prisma,视频平台用React+TypeORM,每次切换都得重新给AI"上课"——告诉他你的路由怎么组织、ORM怎么用、组件怎么封装。统一技术栈消除了这个问题。

六、部署架构:简单到无聊

两个项目的部署架构几乎一样:

┌─────────────────────────────────┐
│         腾讯云轻量服务器          │
│                                 │
│  ┌───────────┐  ┌───────────┐ 
│
│  │ 表单平台   │  │ 视频平台   │  │
│  │ :3000     │  │
:3001     │  │
│  │ Express   │  │
Express   │  │
│  │ +Prisma   │  │
+SQLite   │  │
│  └─────┬─────┘  └─────┬─────┘ 
│
│        │              │        │
│  ┌─────┴──────────────┴─────┐  │
│  │      
Nginx 反向代理      │  │
│  │ 
域名A → :3000           │  │
│  │ 
域名B → :3001           │  │
│  └──────────────────────────┘  │
│                                 │
│  ┌──────────────────────────┐  │
│  │ 
腾讯云COS(静态资源+文件)│  │
│  └──────────────────────────┘  │
└─────────────────────────────────┘

没有Kubernetes,没有Docker Compose,没有CI/CD流水线。就是一个Nginx反代两个Express服务,静态资源扔COS。

独立开发者的部署原则:能用一行命令解决的,不要用十行。 我现在部署新版本的流程是:git pull && npm run build && pm2 restart。整个流程30秒,出问题SSH上去看日志。

Docker和CI/CD我以后团队化的时候再加,现在加就是给自己加活。

七、一个人维护多项目的心法

最后分享几个实操层面的心法,不是架构理论,是日积月累的血泪经验。

7.1 目录结构必须一致

两个项目的目录结构是我手动对齐的,连文件命名风格都统一:

src/
├── components/       # 公共组件
│   ├── common/       # 通用组件(Button, Modal等)
│   └── business/     # 业务组件
├── views/            # 页面
├── stores/           # Pinia store
├── utils/            # 工具函数
│   ├── request.ts    # axios封装
│   └── auth.ts       # 认证工具
├── router/           # 路由
└── styles/           # 全局样式

这样做的效果是:我在表单平台想找认证逻辑,闭着眼睛就知道去src/utils/auth.ts。切换到视频平台,还是同一个位置。不需要回忆,不需要搜索,直接上手改。

7.2 错误处理统一模板

两个项目的错误处理中间件共享同一个模板:

// middleware/errorHandler.ts
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  console.error(`[${new
Date().toISOString()}] Error:`, err.message)

  // 已知的业务错误
  if (err.name === 'BusinessError') {
    return res.status(err.statusCode ||
400).json({
      code: err.code,
      message: err.message
    })
  }

  // 未知错误
  res.status(500).json({
    code: 'INTERNAL_ERROR',
    message: process.env.NODE_ENV ===
'production'
      ? '服务器内部错误'
      : err.message  // 开发环境显示详细错误
  })
})

统一的错误格式意味着:我写监控脚本时,两个项目的日志可以用同一个解析器。如果错误格式不一样,光写两套日志分析逻辑就够烦的了。

7.3 一个人的版本管理

两个项目各自独立Git仓库,版本号各自管理。共享模块的更新靠手动同步。

听起来很原始,但实际上——一个人能改多快? 共享模块一个月改一两次,手动复制30秒搞定。你要是上了Monorepo,光处理依赖拓扑和构建顺序的时间就不止30秒。

7.4 不要提前抽象

共享模块不是我一开始就设计的。开发表单平台的时候,所有代码都在项目内。开发视频平台的时候,我发现有些代码跟表单平台一样,才提炼出来共享。

先写两遍,再提炼一遍——这比一开始就设计"完美共享架构"靠谱得多。因为你只有写第二遍的时候,才知道哪些真的需要共享,哪些只是看起来相似但实际不同。

八、踩坑总结

现象

解法

两个项目CSS冲突

表单平台的Tailwind配置影响视频平台

各自独立的tailwind.config,共享主题变量文件

共享模块改了忘同步

修了A项目的bug,B项目还在出

改共享模块后加TODO注释提醒同步

环境变量名冲突

两个项目部署在同一台服务器,COS_SECRET_ID冲突

环境变量加项目前缀:FORM_COS_KEY / VIDEO_COS_KEY

数据库选型纠结

想统一到PostgreSQL但视频平台不需要

接受不统一,让合适的项目用合适的工具

AI生成的代码风格不一致

不同项目AI生成的组件写法不同

统一Prompt里的编码规范约束

九、架构没有银弹

回过头看,我选的"统一技术栈 + 共享模块"方案也不是没有缺点:

技术栈被锁死:如果我以后想做Python项目,共享模块就得重写

共享模块是弱约束:没有TypeScript类型检查跨项目强制同步,全靠自觉

不适合3人以上团队:人多了手动同步就崩了,得上Monorepo或私有npm

但对于"一个人维护2-6个项目"这个阶段,这是我能找到的最优解。它足够简单,足够高效,足够让我把精力花在产品而不是基础设施上。

独立开发者的架构选择,标准不是"技术上最优",而是"你一个人能持续运转的最优"

智枢矩阵是一个开源AI智能表单生成平台,智播坊是一个开源口播视频生成平台,都是这套架构下的产物。如果你也在一个人做多项目,或者对这个"AI增强型独立开发"的模式感兴趣,欢迎来交流。

你一个人做多项目时,代码是怎么组织的?Monorepo还是独立仓库? 评论区聊聊 👇

关于作者:独立开发者,用 AI 当队友,一个人干一个团队的活。专注 AI + 工具矩阵方向,智枢矩阵 & 智播坊作者。两个项目均已开源,在 Gitee 搜索「zhishu-matrix-open」或「zhibofang」可找到。