每个产品都要重复的活
新产品始于一个具体问题。但它的头几周往往没那么具体:会话、工作区记录、订阅状态、后台任务与邮件投递。这些部件必须先协同起来,产品才能做任何有用的事——每一件在抽象层面都是已解决的问题,在具体层面都是集成风险。
supastack 给这些重复的工作安了家。Next.js Web 应用与 Expo 移动应用共享底层基础设施的 TypeScript 包。目标是让这些边界足够易懂,使产品能够配置它们而不必继承别人的领域模型。
没有哪一件孤立地看是难的。成本藏在它们的协同里——必须变成权益的支付、必须变成工作区范围的会话、必须在合上的笔记本之后存活的任务。产品很少败在缺一个功能上,它们败在接缝上。
每个模块只承担一项职责
身份确定谁在发请求。工作区决定这个人能访问哪些记录。计费记录支付状态,而权限回答某项能力是否可用。把这些关注点分开,可以防止一次支付成功的跳转被当成授权决定。
同样的纪律适用于后台任务。共享的 JobRunner 契约只承诺 enqueue、status 与 cancel,工作流行为归 Trigger.dev 所有。一个小接口的价值在于边界清晰可见;把重试或调度藏进通用方法只会让系统更难推理。
Better Auth 回答身份。Postgres 与 Drizzle 保管记录。Stripe 或 Lemon Squeezy 上报支付状态。没有哪个被要求对别的模块的职责抖机灵——接缝窄到可以推理——而一个地基的可信度,至多等于它接缝的可信度。
为产品留出空间
获客工作流、文档编辑器与研究助手可以使用相同的身份与计费模块,但它们的记录、语言与工作流仍然不同。产品专属逻辑属于使用方的代码库,通过配置接入基础。
这种分离也是务实的维护决策。工作区隔离的修复应当可复用;某个产品评估潜在客户方式的变更,不应成为其他所有产品的新需求。
这就是地基与皮肤的区别。皮肤给你一套可以保留或换掉的外观。地基给你承重的决定——谁拥有身份、记录如何划界、支付在哪里变成访问——产品构建于其上,而不是绕着它走。项目结构指南标出了一者结束、另一者开始的位置。
从一条完整流程开始
先跑本地参考流程:登录、查看权限、提交任务、读取结果。fake 与本地适配器让确定性测试无需付费供应商请求即可走通这条路径——五分钟快速上手端到端地跑了一遍。之后再配置你的产品真正需要的生产集成。
本地验收与生产验收回答的是不同问题。通过的本地测试确立的是受控条件下的应用行为;真实的认证持久化、供应商投递、部署与设备行为仍需各自的验证。随着产品成长,始终保持这种区分可见。
地基的名字是慢慢挣来的:挣在第二个比第一个更快交付的产品里,挣在只修一个模块而不是五个代码库的维护日里。这就是 supastack 背后的赌注——也是每个模块被要求的及格线。
这套思路背后的问题
这套思路下面垫着几个问题,值得坦率地回答。
- supastack 是模板还是框架? —— 一个由模块组成、模块之间靠契约连接的地基。产品代码住在 Starter Core 之外,通过配置接入,所以平台可以更新而不必 fork 你的功能。
- 共享基础设施不会让产品变得千篇一律吗? —— 共享的只有管道。记录、工作流和语言都留在产品代码库里——客户真正看得到的那些部分仍然是你的。
- 产品自己的代码从哪里开始? —— 从只有它才有的记录与工作流开始。地基负责认证用户、限定数据范围;它不知道“潜在客户”是什么。
- 为什么是 monorepo? —— 这样 Web、移动端和桌面端应用才能和底下的包一起变化:一个类型系统、一道测试关卡、仓库之间没有漂移。
- 如果我只需要 Web 应用呢? —— 你买的是地基;移动端和桌面端是可以采用也可以无视的端面。没有什么强迫你发布不需要的应用。