微信公众号阅读器的架构与实现

接下来我想聊一聊,在开发微信公众号阅读器的过程中,我涉及到的一些技术点,以及整体的架构设计。

技术选型的基本原则

在项目一开始,我先确定了几个大的技术方向:

首先,这是一个 TypeScript 的全栈 React 应用,并且支持 SSR(服务端渲染)

其次,在安全性方面,我希望系统具备基本的抗攻击能力,比如认证机制要健全、数据存储要可靠。因此在这一层,我直接选择了一套现成方案——Directus CMS,来负责用户、权限以及数据管理。

在开发框架上,我选择了自己最熟悉、同时也足够简单的 React Router

UI 方面则比较直接,使用了 Tailwind CSS + Shadcn,这套组合在开发效率和可控性上都很合适。

BFF 层:基于 React Router 的服务端能力

React Router 本身具备编写服务端 API 的能力(Resource Route),因此在这个项目里,我相当于是直接拥有了一层 BFF(Backend For Frontend)。

这带来的一个好处是:

很多原本需要放在 Directus Flow 或 Extension 里的服务端逻辑,现在可以直接写在应用侧完成。

这样做明显提升了开发体验,同时也让整体的迭代速度更快。

服务拆分:Genesis / Nova / Impulse

整个项目被我拆分成了三个服务:

1. Genesis:主应用

Genesis 是整个系统的核心模块,主要负责:

可以理解为用户直接接触到的“主站”。

2. Nova:中间层服务

之所以单独拆出 Nova,是因为有一些能力不太适合直接放在主应用里,比如:

这些功能本质上更偏“中间层”。

另外一个更现实的原因是:

整个项目部署在 Cloudflare Workers 上,而单个 Worker 有 CPU 10ms 的限制

为了避免单个 Worker 负载过重,我必须把一部分逻辑拆分到另一个 Worker 中去执行。

Nova 就承担了这个角色。

3. Impulse:后台任务系统

Impulse 是一个相对独立的模块,本质上是一个 cron job 服务,主要负责:

它不直接参与用户请求,而是在后台“默默工作”。

数据层的选择:为什么使用 Directus

在数据存储方面,我目前没有使用像 Supabase(Postgres)或者 Cloudflare D1 这样的方案。

原因有两个:

  1. 当前阶段用不上这么复杂的数据库能力
  2. 这些方案在免费或默认配置下都有一定的容量限制

相比之下,我选择直接使用 Directus 管理的数据库

部署方式

Directus 被我部署在一台国内的云主机上,一年大概一百多块钱。

这种方式的好处是:

文件管理能力

Directus 在文件管理这一块的体验非常好:

这意味着:资源访问天然具备权限控制,可以有效防止盗链

网络与部署策略

由于 Genesis / Nova 部署在 Cloudflare,而 Directus 在国内机房,两者之间存在一定的物理距离。

为了解决这个问题,我做了两件事:

  1. 使用 Cloudflare Worker 的智能调度(placement),让请求尽量就近执行
  2. 通过 Nova 做一层代理,隐藏源站(Directus)的真实地址

因此:Directus 的域名是不会被直接暴露的

最终架构总结

整体来看,目前的架构是这样的:

三者各自职责清晰,同时又保持了比较好的解耦。

这就是目前这个项目的整体技术实现方式。

发表于 2026 年 4 月 9 日,星期四