大多数副业项目都把业务逻辑放在应用层里。CarInsight反其道而行之,把评分、对比、风险权重和成本模型全部从Next.js应用中剥离,放到独立的编排层运行。这个决定,成了整个项目最正确的架构选择。

CarInsight评估德国市场的二手车列表,生成一份决策报告:这个价格是否合理、该车型通常哪里容易出问题、持有成本是多少。输出不是数据堆砌,而是一个判断——判断必须经得起推敲。这个约束驱动了所有设计。

打开网易新闻 查看精彩图片

技术栈并不新奇

前端是Next.js 14 App Router、TypeScript、Tailwind、shadcn/ui,数据库用Supabase上的Postgres,支付走Stripe Checkout,部署在Vercel上。没有任何花哨的东西。有趣的部分恰恰是那些不在应用里的东西。

评分逻辑、对比逻辑、风险权重、成本模型——这些都不在Next.js的路由处理器里运行,而是作为独立的编排层运行,包含一个编排器和一组子工作流

为什么要把逻辑搬出去

三个理由推动了这个决定。

第一是可审计性。当报告说某个价格比可比集合高出12%时,几个月后需要重建:当时是哪些列表构成了这个集合,它们是什么时候抓取的。如果是一个serverless函数内联计算后直接返回JSON,你没有任何东西可以回放。

第二是独立迭代。评分逻辑的变化频率远高于结账流程。把它们耦合在一起,意味着每次调整权重都要做一次完整的应用部署。

第三是故障隔离。报告生成失败不应该拖垮用户正在付款的页面。

四阶段流程

所有面向用户的内容都要经过四个阶段。底层规则是:没有来源,就没有结论。如果一个数字找不到出处,相关部分就保持不完整,而不是用看似合理的内容填补。这比听起来难得多,因为“看似合理但错误”恰恰是生成式系统最擅长产出的东西。

这里说的不是基础设施层面的来源,而是欧元计价的声明。“这款车型每年维护成本大约X”这种话,生成起来轻而易举,但要找到可靠出处几乎不可能。

成本拆分:可推导与估算

最终他们把成本组件分成两类:可推导的和估算的。

可推导意味着有公式或官方参考。比如德国车辆税,法律有明确规定——发动机排量和二氧化碳排放量,固定费率。这不是预测,是算术。他们用代码实现,并对照官方计算器验证,直到偏差归零。

估算则涵盖其他所有内容,并且会被明确标注为估算值,同时披露估算方法。

一个令人不适的发现

这个过程中最让人不舒服的发现是:买家最想要硬数据的类别,恰恰是硬数据不存在的类别。比如关于车型可靠性的公开统计,通常不会区分同一车型的不同发动机版本。所以,在发动机版本这个粒度上的声明,无法引用这些统计——无论你多想引用。

他们选择在产品里直接说明这一点,而不是掩盖过去。

如果你的产品是一个判断而不是一次查询,那就把来源追溯当作一等架构关注点,而不是日志记录的事后补充。这个约束会反过来塑造你的系统设计——从数据抓取到报告生成的每一个环节,都在为“可追溯”服务。