成为全栈·产品篇·技术选型不是投票
本文目标:拆清楚这套专栏每个端的技术栈是怎么定的,尤其要把后端栈(Hono + Drizzle + Cloudflare D1/R2)为什么「已经定死」讲透。更重要的是,把背后的选型方法论交给你——这套方法能迁移到你自己的任何项目,比记住某个框架名字值钱。
前置知识:建议先读 用一个真实系统串起全栈(七个子项目全貌)。本文会展开那张七端表格里每个端的技术栈。
先澄清一个常见误会
很多人以为「技术选型」是投票:团队里谁嗓门大、哪个框架 GitHub 星多,就选哪个。或者更随意——「大家都在用,我也用」。
这是本末倒置。选型是按约束定的,不是按人气定的。而且我们有条铁律:每次选型都必须写理由——交代备选方案和取舍依据。这恰恰是本系列区别于普通教程的核心:普通教程告诉你「用这个」,我们只告诉你「为什么是它,以及什么时候该换」。
还有一点要先说在前:本文标题写「七个子项目技术栈的定法」,但后端栈其实在 M1 动手前就已经定死了,本文是「反向约束」——它先存在,然后要求我把理由讲清楚。所以你读到的不是「我们现场选」,而是「它已经定了,我来拆解这个决策」。这个区别很重要。
一、选型的方法论:按约束定,不是集美投票
做选型时,我会把约束摊在桌面上,看它们互相怎么打架:
| 约束 | 它要什么 |
|---|---|
| 团队熟悉度 | 别选没人会、踩坑没人填的 |
| 部署环境 | 跑在 Cloudflare 还是自管 Linux?这决定数据库和存储选型 |
| 成本 | serverless 按量计费 vs 常驻服务器月租,量级差很多 |
| 生态与长期维护 | 框架是不是还在活跃迭代、文档全不全 |
| 与现有栈的契合 | 前端已是 TypeScript 体系,后端能不能共用一套类型 |
关键不在列出约束,而在约束会互相打架。比如「想用 Cloudflare 省运维」和「又想能随时搬回普通 Linux 服务器」就是一对矛盾——这正是后面适配层要解决的。选型不是找「全 5 分」的选项,而是在矛盾里做权衡、并能说清「我为什么这么让」。
把这条决策链画成图,谁是根、谁是被约束的结果,一眼就清楚:
二、后端栈:为什么已经定死
Node 后端(M1)采用Hono + Drizzle ORM + Cloudflare D1(数据库)+ Cloudflare R2(对象存储),并且同时兼容部署在普通 Linux 服务器。
它定死,是因为它是七端的共同地基——所有前端、App、小程序都消费它产出的同一套 API。地基不能每个端换一套,否则「一个系统七端共享契约」的复利就垮了。
逐个说理由:
- Hono:超轻量、跨运行时的 Web 框架。同一份代码能跑在 Cloudflare Workers、Node、Bun、Deno。这一点对「一套后端两种部署目标」是前提——框架本身就不过度绑定某个运行时。
- Drizzle ORM:TypeScript 原生、类型安全;更关键的是在 Cloudflare 用 D1(SQLite),在 Linux 上用 SQLite 文件或 PostgreSQL,schema 不变。你写的表结构,换数据库不用重写。
- D1 / R2:Cloudflare 生态的数据库与对象存储。它们最大的卖点是零运维、按量计费——对个人项目和副业极其友好。对应到 Linux 上,就是 SQLite/PostgreSQL 与本地磁盘 / S3 / MinIO。
你看,这一套选型的底层逻辑就一句话:用 serverless 把运维和成本压到最低,同时用 Hono + Drizzle 的跨运行时特性,保住「随时能搬回普通 Linux」的退路。
三、唯一的陷阱,也是高价值解法:适配层
上面那句「保住退路」不是白说的。D1/R2 是 Cloudflare 专有服务,但真实世界里你完全可能要在自己的 Linux 服务器上跑同一套系统。这就有个矛盾:
业务逻辑只有一份,但底层存储(D1 vs SQLite/Postgres、R2 vs 本地磁盘/S3)是两套。
解法就是本系列一个高价值题材:写一层「适配层」,让同一份业务逻辑在边缘(Cloudflare)和自管服务器(Linux)上都能跑。业务逻辑不直接调用 Cloudflare 专有 API,而是调用适配层暴露的统一接口;适配层在两种部署目标下各自实现。
把「一份逻辑、两种落地」画成图,就是典型的「中间件隔离」结构:
这带来一个很妙的副产品——「一套后端,两种部署目标」本身就是值得单独成篇的内容({{LINK:M1-24}} 会专门讲适配层抽象)。你学到的不只是「怎么接 Cloudflare」,而是「怎么写出不绑定任何云厂商的业务代码」,这套能力到哪都用得上。
连附件存储也是同一思路:上传走适配层,R2 为主、本地磁盘兜底,由一个STORAGE_DRIVER配置驱动。你看,架构约束一旦想清楚,连文件存储都自动跟着整齐了。
四、前端各端:按场景选,不统一
后端栈定死,但前端各端是自由选型的——因为它们是「端」,不是「地基」。每个端按自己的场景挑最合适的框架:
| 端 | 技术栈 | 为什么是它 |
|---|---|---|
| M2 管理后台 | Vite + React | 后台要的是开发效率和组件生态,React 成熟稳妥 |
| M3 网站前台 | Next.js(App Router) | 要 SEO,且能在框架内直接写后端(Route Handler / Server Actions),读者前台首选 |
| M4 App | Flutter | 一套代码出双端原生 App,跨平台体验一致 |
| M5 小程序 | Taro | 用 React 语法写微信小程序,前端心智零切换 |
| M7 后台重写 | Vue3 | 同契约重写,展示换一套前端框架是什么体验 |
注意这里的自由边界:框架可以换,契约不能换。你用 React 还是 Vue 写后台,是选型自由;但无论哪个框架,调用的都是同一套 API。这正是 用一个真实系统串起全栈 说的复利——地基不变,端随便长。
五、Go 后端重写:语言是变量,契约是常量
最后说 M6。它用Go 把同一套后端重写一遍,接口与 Node 版完全一致。
这件事的启示很锋利:当你做技术选型时,编程语言只是变量,系统设计(契约)才是常量。你今天用 Node、明天用 Go,换的是工具;实体关系、鉴权模型、错误码约定这些「设计」,是一次想清楚、长期不变的东西。
所以如果你纠结「我该学 Node 还是 Go」,换个问法更有价值:「我的系统设计清楚了吗?」设计清楚了,换语言只是体力活。
给你一句可带走的心法:下次面对选型,先别问「哪个好」,先拿一张纸把约束列出来——团队会不会、跑在哪、多少钱、生态活不活、和现有栈契不契合。列完你会发现,很多「纠结」其实是因为你还没把约束摊开。方法比结论耐用:今天这个结论可能过时,但「按约束定、写理由」这套动作,到你下一个项目仍然成立。
顺便,这套「契约是常量」的认知会反过来治你的选型焦虑:当你不再纠结「该学哪个框架」,而是先问「我的系统设计清楚了吗」,你就已经站在全栈的门槛上了。
把这句记下来,它会在你后面每一次选框架时跳出来提醒你。
小结
- 选型按约束定,不是投票;每次选型必须写理由——这是本系列区别于普通教程的核心。
- 后端栈(Hono + Drizzle + D1/R2)已经定死,因为它是七端共同地基;理由就一条:serverless 压成本,跨运行时保退路。
- 唯一的陷阱是 Cloudflare 锁定,解法是一层适配层——「一套后端,两种部署目标」,本身是高价值内容。
- 前端各端自由选型(按场景),但契约不变;M6 用 Go 重写证明语言是变量、设计是常量。
- 真正要带走的不是某个框架名,而是「把约束摊开、做权衡、写理由」这套方法论。
延伸阅读
- 用一个真实系统串起全栈——本文展开的就是那张七端表格里的技术栈。——本文展开的就是那张七端表格里的技术栈。
- {{LINK:M0-04}}《领域建模》——下一篇,讲「设计是常量」里那个不变量:实体和关系怎么定。
订阅这个专栏
如果你也想跟着一个真实系统,从「调接口的人」走到「设计系统的人」,欢迎订阅我的《成为全栈开发工程师》专栏。后续每篇都会带着可运行的代码和完整的设计取舍走下来,欢迎在评论区讨论、指正。
- 本系列专栏:https://blog.csdn.net/fungleo/category_13204651.html(订阅看全部篇章)
- 完整项目仓库:https://github.com/fengcms/become-a-full-stack-developer