普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月11日首页

万字长文之——AI落地规范

作者 codingWhat
2026年2月11日 13:49

AI已经走入普通大众的生活,那么在程序员的世界中呢?我想,随着AI的浪潮滚滚前进,我们的工作中一定或多或少使用到了AI工具。比如一开始的vscode中的代码提示插件,如GitHub Copilot、通义灵码等,到现在的代码生成编辑器,如Cursor、Trae等。

一开始尝试使用Cursor时,会不禁感叹,这个真牛啊!它真的有这么厉害?我不信,让我来挑挑刺:

你看,他生成的代码风格和现有工程不一致;
你看,你看,他需求理解歪了啊,需求你来看看,是不是理解错了;
你看,你看,你看,他每次生成的代码都不一致啊,这怎么维护啊,ababab~ ~ ~


那么,真要放弃AI吗?那肯定不能,毕竟能干活啊;那问题来了,怎么让他更趁手些呢?在努力近一年后,我尝试写一写在现有工程中让 AI 辅助开发能够稳定落地的一些规范与实践。


一、为什么需要「AI 落地规范」

在真实业务项目里引入 AI(如 Cursor、Claude Code)写代码时,常遇到:

  • 生成代码风格与现有工程不一致:目录、命名、组件结构各写各的。
  • 需求理解偏差:缺少结构化需求与接口说明,AI 只能“猜业务”,更容易出现幻觉。
  • 可维护性差:没有统一规则,后人或 AI 难以在既有约定下续写。

要解决这些问题,需要两件事同时到位:

  1. 输入规范:需求、接口、配置等写成「AI 可读、可执行」的结构化文档。
  2. 输出规范:用项目规则(如 Cursor Rules)约束 AI 的目录、命名、组件与接口写法。

下面以一个vue项目中的模块「xxxx缴费记录查询」和 xxx-xxx-h5-rules 为例,说说我们团队是如何做的。


二、输入规范:把需求写成 AI 读得懂的语言

AI 落地的前提是:需求与接口被清晰、结构化地写出来,而不是只存在于口头上或零散的聊天里(比如多轮对话,这样只会让代码成为一坨,,,)。

2.1 功能文档结构

xxxx缴费记录查询/
├── xxxx缴费记录查询-需求.md         # 功能概述、流程、页面与规则
├── xxxx缴费记录查询接口-需求.md     # 接口入参、出参、取值与规则
├── 系统参数配置.md                # 提示信息、开关等可配置项
└── 原型文件/                     # HTML/CSS/JS 原型
  • 需求文档(前端用):功能概述、流程图(如 Mermaid)、页面分区(导航栏、查询区、列表区)、控件表(名称、类型、必录、规则、数据元)、页面规则(如 4.1 页面初始化、4.2 xx数据查询规则)。
  • 接口需求文档(后端用):接口概述、入参/出参表(含数据元标识、字段名、类型、是否可空)、接口规则(入参校验、表来源、取值规则)。
  • 系统参数配置(共用):按模块/页面列出可配置项(如主页面提示信息),便于前端从配置读取而非写死。

这类结构让 AI 能够:按「页面 + 控件 + 规则」生成页面与交互;按「入参/出参 + 规则」生成 API 调用与 DTO;按「配置项」生成从 store/config 读取的提示或开关。


2.2 AI下的需求文档该怎么写

统一使用对AI更友好的.md文档编写需求,这对需求侧的同学也是一个不小的挑战,但我们认为这是很有必要的!

一、功能概述

用一两句话说明功能面向谁、做什么、范围是什么。例如:

本功能支持xxx

  • 对 AI 的用途:确定模块边界、页面标题、路由命名。

二、详细流程

先画流程图(建议用 Mermaid),再配简短流程说明。例如:

sequenceDiagram
    actor 用户
    participant 本功能 as 本功能
    participant 外部系统 as 外部系统
    rect rgba(135, 206, 250, 0.4)
        Note over 用户,外部系统: 1. 【页面初始化】
        用户->>本功能: 1) 访问xxxx缴费记录查询页面
        本功能->>外部系统: 引用4.1规则初始化
        外部系统-->>本功能: 返回初始化配置及数据
        本功能-->>用户: 展示xxxx缴费记录
    end

流程说明示例:

  1. 【页面初始化】访问功能后,系统获取个人信息及缴费列表数据,展示xxxx缴费记录查询主页。引用 4.1 规则初始化。
  • 写法要点:流程与后文「四、页面规则说明」中的规则编号对应(如“引用 4.1”),便于实现时按规则写初始化逻辑。
  • 对 AI 的用途:生成 mounted/created 中的调用顺序,知道先取配置再取列表。

三、页面需求分析

页面 → 区域 → 控件分层,用表格把每个区域的控件写清楚。

  • 导航栏:操作按钮 + 标题。用表格式列出控件名称、可操作、控件类型、交互事件及规则;标题单独一行说明。

  • 页面提示区域:不写死文案,而是引用配置。例如:

    提示内容取自「系统参数配置.md」,具体路径:xxxx缴费记录查询 → 主页面提示信息参数配置,若未配置则不显示。

    这样 AI 会生成「从配置读取,未配置则不展示」的逻辑,而不是写死一段文字。

  • 查询条件选择区域:用一张表统一描述控件,列建议包含:

    列名 说明与示例
    控件名称 xxx
    控件类型 年度选择框、下拉框
    可操作/必录/是否可见 Y/N,便于做校验与展示
    初始状态/数据 默认当前系统年度、暗文「请选择」
    操作规则及取数口径 格式 YYYY、最大不超过当前年度、下拉选项:xxxx;引用“xx数据查询规则”
    数据元字段名 与接口对齐

    示例(节选):

    控件名称 控件类型 可操作 必录 是否可见 初始状态/数据 操作规则及取数口径
    年度 年度选择框 Y Y Y 默认显示当前系统年度 格式 YYYY;最大不能超过当前系统年度。引用“xx数据查询规则、xx组件”
    xx 下拉框 Y N Y 暗文:请选择 下拉选项:xxx。引用“xx数据查询规则”
  • 列表区域:同样用表列出「展示项 + 取数口径 + 数据元」。例如缴费金额写清单位、格式(¥、保留两位小数)。
    这样 AI 能直接生成列表列定义和格式化逻辑。

四、页面规则说明

编号规则把「页面初始化」「查询规则」等说清楚,并与前文引用一致。

  • 4.1 页面初始化:逐条写“根据哪份配置做哪件事”“根据哪条规则初始化数据”。例如:

    • 根据《系统参数配置.md》对“页面提示区域”进行初始展示。
    • 根据“xx数据查询规则”初始化展示页面数据。
  • 4.2 缴费数据查询规则

    • 先写查询条件转换
    • 再写调用哪个接口。
      AI 可根据规则生成查询参数组装和接口调用。

2.3 我们为什么规定这样写需求文档?

维度 写法 好处
结构化 功能概述 → 流程 → 页面分析 → 规则说明;接口概述 → 入参/出参 → 接口规则 层次清晰,AI 和人都能按章节定位;先“做什么”再“怎么做”,减少遗漏。
表格化 控件表、入参表、出参表、代码映射表 机器可读性强,AI 可直接用于生成表单字段、请求参数、列表列、常量映射;前后端对齐同一张表,字段名和类型一致。
引用与编号 “引用 4.1 规则”“引用xxxz组件”“引用xx数据查询规则”“根据系统参数配置.md” 需求内可追溯,实现时按编号找到对应规则及组件;提示区、查询逻辑等不写死,便于多地区配置。
数据元与取数口径 数据元标识符、字段名、取数口径、格式备注 与标准数据元一致,利于对接;取数口径让前端知道哪些字段是后端计算、如何展示。
流程 + 规则 Mermaid 流程图 + 规则编号 先有时序再有条文,AI 能生成正确的调用顺序和初始化逻辑;规则可单独实现、单独测试。
配置与代码分离 提示内容取自系统参数配置、若未配置则不显示 文案和开关可配置,AI 生成“读配置再展示”的代码,避免硬编码,便于多环境。

整体上,需求文档按“概述 → 流程 → 页面/接口表格 → 规则说明”来写,并用表格和编号把控件、字段、映射、规则、组件编号固定下来,既能让人一眼看懂,也能让 AI 按表生成代码、按规则生成逻辑,减少歧义和返工,也方便后续的1—N迭代。


2.4 需求文档中建议包含的要素

要素 说明 对 AI 的用途
功能概述 一两句话说清功能做什么、谁用 确定模块边界、路由/菜单命名
流程图 用 Mermaid 等画清主流程 生成顺序逻辑、调用关系
页面分区 导航栏、提示区、查询区、列表区等 对应 template 区块与组件
控件表 控件名称、类型、必录、可见性、初始值、操作规则、数据元 生成表单项、校验、字段绑定
数据元 中文名、标识符、字段名、类型、长度 与接口字段、DTO 对齐
页面规则 如「4.1 页面初始化」「4.2 xx数据查询规则」 生成 mounted/created 逻辑与查询条件转换

接口需求文档中则建议包含:

  • 接口概述、入参/出参表(含数据元与检索策略);
  • 入参校验、涉及表、关键取值规则。

这样 AI 生成的 API 封装、请求参数和 DTO 才能与后端约定一致。

2.5 系统参数配置的用法(可选)

「系统参数配置.md」中按模块列出「主页面提示信息参数配置」等,并说明使用场景、配置项、默认值。
前端应:从配置读取提示语等,而不是在页面里写死。这样 AI 生成的代码会去读 state.configs 或等价配置,便于多地区/多环境差异化。


三、输出规范:用项目规则约束 AI 的代码

有了清晰输入,还需要统一输出:目录、命名、组件结构、API 与状态管理方式都要和现有项目一致。本项目中通过 xxx-xxx-h5-rules 承担这一角色。

3.1 规则体系概览(xxx-xxx-h5-rules)

规则文件 作用 对 AI 的约束
project-structure.mdc 项目结构、入口、路由位置 新模块放在哪、如何拆目录
vue-coding-standards.mdc Vue 组件顺序、JS 规范、路由/Vuex/API 约定 组件结构、命名、mapState/mapMutations 用法
component-development.mdc 通用/业务组件、标准父/子页面结构 页面用 xxx + keep-alive、子页面 name 与 class 命名
api-integration.mdc HTTP 封装、接口组织、错误与加密 api 模块写法、http 使用方式
business-modules.mdc 业务模块列表、新增模块步骤 路由/store/api 注册顺序与命名
build-deployment.mdc 构建、环境、部署 不随意改构建与公共路径

这些规则应被 Cursor 等工具识别(如通过 .cursor/rules 或说明中引用),让 AI 在生成代码时默认遵循。

3.2 部分 mdc 文档说明

规则文件不只有代码,而是「说明 + 列表 + 目录树 + 代码块」组合。下面按文件摘录部分原文,供大家参考。


README.mdc(总览)

---
alwaysApply: true
description: xxx-xxx-h5 项目规则总览
---

# xxx-xxx-h5 项目规则总览

## 规则文件说明

本目录包含 xxx-xxx-h5 项目的所有 Cursor 规则文件,用于指导 AI 助手更好地理解和开发该项目。

### 规则文件列表

1. **project-structure.mdc** - 项目结构指南
   - 项目概述和核心目录结构
   - 模块化架构特点说明
   - 主要入口文件和配置文件位置

2. **vue-coding-standards.mdc** - Vue 2.x 编码规范
   - Vue 组件开发规范
   - JavaScript 编码标准
   - 路由和状态管理规范
   ...
  • frontmatter 声明 alwaysApply: truedescription,便于 Cursor 按需加载或常驻。
  • 编号列表 + 子项 说清每个规则文件负责什么,AI 能快速判断该查哪个文件。

project-structure.mdc(结构说明)

# xxx-xxx-h5 项目结构指南

## 项目概述
xxx-xxx-h5 是一个基于 Vue 2.x 的 H5 应用,采用模块化架构设计。

## 核心目录结构

### 主要入口文件
- main.js - 应用主入口,配置 Vue 实例和全局插件
- App.vue - 根组件
- vue.config.js - Vue CLI 配置文件

### 路由系统
- router/index.js - 主路由配置,包含所有模块路由
- router/ - 各功能模块的路由配置
...

## 模块化架构特点
项目采用功能模块化设计,每个业务功能都有对应的:
- 路由配置 (router/)
- 状态管理 (store/)
- API 接口 (api/)
- 页面组件 (views/)

这种设计便于维护和扩展新功能。
  • 先概述再拆目录AI 能建立「模块 = router + store + api + views」的心智模型
  • 无代码也可约束「新模块该建哪些目录、放在哪」。

vue-coding-standards.mdc

---
globs: *.vue,*.js
description: Vue 2.x 编码规范和最佳实践
---

### 组件命名
- 组件名使用 PascalCase
- 文件名与组件名保持一致

### 路由命名
- 路由名称使用 camelCase
- 路径使用 kebab-case
- 模块路由统一导入到主路由
- 使用常量导出路由名称,便于维护
  • globs 限定只在 *.vue,*.js 下生效,避免在无关文件中误用。
  • 条文式约定(命名、路径、常量)无需代码也能约束生成结果。

同一文件内再配「获取系统配置」的说明 + 注意事项 + 代码:规则里先写「在组件中获取系统配置必须使用以下方式」,再贴代码;接着用注意事项列出四条(用户信息走 getters、系统配置走 state、箭头函数、避免在 methods 里访问 store)。示例代码:

// vue-coding-standards.mdc 中规定的方式
export default {
  computed: {
    sbfConfigs: (vm) => {
      return vm.$store.state.configs.xxx
    }
  }
}
  • 先写「必须使用以下方式」再贴代码,AI 会优先采用该写法。
  • 注意事项 能减少「能跑但不符合项目习惯」的生成(如把 config 放在 methods 里)。

component-development.mdc

### 组件设计原则
- 单一职责:每个组件只负责一个功能
- 可复用性:组件应该可以在不同场景下复用
- 可配置性:通过 props 提供灵活的配置选项
- 可扩展性:支持插槽和事件扩展

### 标准页面组件结构
- 页面组件放在 `views/` 目录
- 按功能模块组织子目录
- 组件名使用 `moduleName.pageName` 格式
- 父级组件使用 `xxTransition``keep-alive`
  • 设计原则 是纯文字,但能引导 AI 不写「大而全」的单文件页面。
  • 标准页面组件结构 四条直接对应「放哪、怎么命名、用什么包裹」,和后面的代码块一致。

api-integration.mdc

### 模块化 API 结构
api/
├── index.js              # API 主入口
├── account.js            # 账户相关接口
├── apply.js              # 申请相关接口
├── basecode.js           # 基础代码接口
├── pay.js                # 支付相关接口
└── ...                   # 其他业务模块接口

### 核心 HTTP 配置
- core/http.js - 基础 HTTP 配置
- core/httpEncrypt.js - 加密 HTTP 配置
- core/BusinessError.js - 业务错误处理
  • 目录树 让 AI 知道新模块的 api 文件应放在哪、如何与 index 挂载。
  • 核心 HTTP 配置 列出文件与职责,生成请求代码时会联想到用 @/core/http、BusinessError。

3.3 这样写 mdc 规则的好处

写法 好处
frontmatter(alwaysApply / globs / description) 工具可按路径或全局决定何时注入规则,减少无关上下文;description 便于人和 AI 快速理解文件用途。
分文件(结构 / Vue / 组件 / API / 业务 / 构建) 单文件短小、职责清晰,AI 检索时容易命中「路由」「组件」「API」等关键词;后续增删规则不影响其他领域。
说明 + 列表 + 代码一起写 既有「必须」「应」等约束性文字,又有可复制的模板代码,AI 既知道「为什么这样」又知道「长什么样」,生成时更少偏离。
注意事项 / 命名规范等条文 不依赖代码也能约束命名、数据来源(如 config 只从 computed 取)、错误处理方式,避免「风格正确但细节不符合项目习惯」。
目录树与入口说明 明确新模块该建哪些目录、在 index 里如何挂载,AI 生成的目录和 import 关系更一致。
标准模式 + 复杂模式并存 简单业务用标准模式(reset/set、queryList),复杂业务用 actions/getters,AI 能按需求选模板,而不是自己发明一套。

整体上,**把规则写成「可执行的说明 + 可粘贴的代码」**,并配合 frontmatter 与分文件组织,能让 AI 在正确时机、正确范围内套用约定,减少反复修改和风格漂移,也便于新人或后续 AI 继承同一套规范。

四、总结

  1. 新功能先写清需求与接口
    至少具备:功能需求(含流程与页面分析)、接口需求(入参/出参/规则)、与前端相关的系统参数配置。

  2. 把项目规则显式化
    像 xxx-xxx-h5-rules 一样,把结构、Vue、组件、API、业务模块、构建拆成独立规则文件,并让 AI 在上下文中能读到这些规则。

  3. 模块命名与注册统一
    业务模块用拼音首字母;新增模块严格按 router → store → api → views 的顺序注册,并在主入口中挂载。

  4. 配置与数据元不写死
    提示语、开关等走系统参数配置;字段名、数据元与接口文档一致,便于前后端联调与 AI 二次修改。

  5. 用现有模块当模板
    让 AI 参考历史模块的 router/store/api/views 实现方式,再结合需求与接口文档生成新模块,可减少风格偏差。


五、结语

AI 要在现有工程里稳定落地,不能只靠「说人话」提需求,而要:

  • 输入侧把需求、接口、配置写成结构化、可检索的文档,后期可引入向量库编排
  • 输出侧用项目规则(如 xxx-xxx-h5-rules)约束目录、命名、组件、API 和状态管理

AI 落地规范是一个系统化工程,本文仅介绍了需求侧和代码生成侧部分,还有很多地方没有提到,谨供大家参考。

昨天以前首页

BFF 与 Next.js 的联系与组合

作者 codingWhat
2026年2月5日 01:13

最近在写一个Next.js的应用,其中API Routes被我当做轻量BFF,去做一些数据加工与处理,那么BFF 与 Next.js到底有什么样的联系呢?

一、概念先厘清

1. BFF(Backend for Frontend)

BFF 是为前端专门提供的一层后端,职责包括:

  • 接口聚合:把多个后端/微服务的调用合并成前端需要的少数接口。
  • 数据裁剪与适配:把后端 DTO 转成前端需要的结构,减少字段、改名、扁平化等。
  • 鉴权与会话:在 BFF 统一做登录态校验、权限校验,前端只关心「调一个接口」。
  • 协议与形态统一:前端只和 BFF 用 REST/JSON 等约定好的方式通信,不直接面对内部 RPC、消息队列等。

BFF 不负责 HTML 渲染,只负责「给前端提供数据接口」。

2. Next.js 与 SSR

Next.js 是一个 React 全栈框架,除了做前端 SPA,还提供:

  • 服务端渲染(SSR):在服务器上执行 React,生成 HTML 再返回给浏览器,首屏即完整内容,利于 SEO 和首屏性能。
  • API Routes:在 pages/api 下写接口,运行在 Node 里,对外提供 HTTP API,可当「小后端」用。

所以:

  • SSR 解决的是「谁在哪儿把页面渲染出来」的问题(服务器渲染 vs 浏览器渲染)。
  • BFF 解决的是「谁给前端提供接口、怎么聚合与适配」的问题。

二者一个偏「渲染与页面」,一个偏「接口与数据」,可以单独用,也可以一起用。


二、A项目中的 BFF 层:

在我们工作中,前端使用的接口由JAVA编写的BFF分层应用提供,在分层应用中去调用真正的内网接口。

2.1 架构概览

  • Web 层:Controller 暴露 HTTP 接口,路径多为 /xxx/api
  • Service 层:Facade 封装对下游业务服务的调用。

前端/移动端只认 BFF 的 URL,不直接调业务中台。

2.2 Controller:面向前端的接口与鉴权

以xxx申报记录查询为例(MobilexxxController):

@Controller
@RequestMapping("/xxx/xxxx/api")
public class MobilexxxController extends MobilexxxBaseController {

    @RequestMapping(value = "/query", method = RequestMethod.POST)
    @ResponseBody
    public MobilexxxDTO<Object> query(@RequestBody MobileuxxDTO<ZxxxRequestDTO> requestDTO) {
        // 1. 获取用户账户信息
        AccountDTO accountDTO = getAccount();
        // 2. 验证用户会话(BFF 统一鉴权)
        validateHelper.accountSessionCheck(accountDTO, ...);
        // 3. 从统一入参中取出业务数据,并注入用户信息
        ZxxxRequestDTO request = requestDTO.getData();
        request.setXm(accountDTO.getAccount().getName());
        // ...
        // 4. 调用 Facade,再统一封装返回格式
        Response<List<ZxxxResponseDTO>> response = mobilexxxFacade.query(request);
        return ResponseHandler.procResponseResult(response);
    }
}

可以看到 BFF 在这里做了:

  • 统一入参/出参MobilexxxDTO / MobileuxxDTO,前端只面对一种请求/响应形态。
  • 会话与鉴权getAccount()validateHelper.accountSessionCheck() 在 BFF 完成,前端无感知。
  • 数据注入:把当前用户姓名、证件号等写入请求再交给下游,前端不用传敏感信息。

2.3 Facade:聚合下游服务

Facade 不实现业务,只做「转发 + 聚合」:

@Service
public class MobilexxxFacade {
    @Autowired
    private RestfulClient restfulClient;
    @Autowired
    private RestfulUrlHelper restfulUrlHelper;

    public Response<List<ZxxResponseDTO>> query(ZxxRequestDTO request) {
        return restfulClient.postForJson(
            restfulUrlHelper.getxxxnwUrl("xxxx/query"),
            request, Response.class, List.class, ZxxResponseDTO.class);
    }
}

其他 Facade同理:BFF 通过 RestfulClient 调远端服务,对前端只暴露「一个接口、一种协议」。

小结:app 这一层是典型的 BFF——独立部署的 Java 服务,只提供 API,不负责页面渲染;职责是鉴权、聚合、适配,方便前端/移动端调用。


三、B项目中的 Next.js 与 SSR:

xxx-Next 是 Next.js 应用,同时用到了 SSRAPI Routes(充当轻量 BFF)

3.1 SSR:首屏服务端取数与渲染

使用 getServerSideProps 每次请求时在服务端拉取数据并渲染:

export default function GetData(props: PropsType) {
  const { err, data, msg = '' } = props
  // ...
  return (
    // ...
  )
}

export async function getServerSideProps(context: any) {
  const { id = '' } = context.params
  const data = await getDataById(id)  // 服务端请求接口
  return { props: data }
}

流程是:

  1. 用户请求 /data/xxx;
  2. Next 在服务器执行 getServerSideProps,调用 getDataById(id);
  3. 拿到数据后,在服务器上渲染 React,得到 HTML;
  4. 把 HTML 和序列化好的 props 一起返回给浏览器;

这样首屏就是「带数据的完整 HTML」,无需等客户端再发请求才出内容,这就是 SSR。这里 Next.js 的角色是「渲染层 + 在服务端发起数据请求」,数据来源也可以换成调用上面那套 Java BFF。

3.2 API Routes:"薄" BFF

点击提交按钮时,指向的是 Next 自己的接口:

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  // 数据加工
  const data = genData(req.body)  // 把表单 body 转成后端需要的结构
  const resData = await postData(data)
  // ...
}

这里 Next.js 的 API Route 做的是:

  • 接收: 传入的数据,如表单数据;
  • 转换genData(req.body) 把表单字段整理成接口需要的结构;
  • 转发postData(data) 再请求真正的后端。

也就是说,API Route 在这里扮演了一层薄 BFF:对前端暴露简单的一个 POST /api/data,内部做参数适配和转发,前端不直接调外部后端。

3.3 数据流小结

  • :浏览器 → Next 服务端 → getServerSidePropsgetDataById → 外部接口→ 服务端渲染 HTML → 返回。
  • :浏览器 POST 到 Next 的 /api/data → API Route 整理参数并 postData() → 再请求后端→ 根据结果处理。

SSR 解决「从哪拿数据、在哪渲染」;API Route 解决「提交时谁来做参数转换和转发」。两者都在 Next 里,但职责不同。


四、BFF 与 Next.js(SSR)的区别

维度 BFF(如 app 中的 Java 层) Next.js(SSR + API Routes)
主要职责 为前端提供聚合/适配后的 API 渲染 HTML 页面 + 可选的 API 端点
是否渲染 不渲染,只返回 JSON 等数据 服务端执行 React,输出 HTML
典型部署 独立服务(如 Java 进程、容器) Node 进程,同一应用里既有页面也有 API
技术栈 任意后端语言(本项目为 Java) Node + React(Next.js)
鉴权位置 常在 BFF 统一做 可在 API Route 或 getServerSideProps 里做
适用场景 多端复用同一套接口、多后端聚合 需要 SEO、首屏性能的前端应用

简言之:BFF 是「专门给前端的接口层」;Next.js SSR 是「在服务器上把页面渲染出来的方式」。一个偏数据与接口,一个偏页面与渲染。


五、BFF 与 Next.js 的联系与组合

  1. Next.js API Routes 本身可以当作轻量 BFF
    如 xxx-Next 的 /api/data:接收整理参数,再调后端。适合逻辑简单、不需要多语言/多后端聚合的场景。

  2. Next.js 的 SSR 可以消费 BFF
    getServerSidePropsgetStaticProps 里,用 getDataById 这类函数去请求「真正的 BFF」(例如 app 里的 xxx/xxx/api),而不是直接调业务中台。这样:

    • 鉴权、聚合在 BFF 完成;
    • Next 只负责「要什么数据、怎么渲染」,职责清晰。
  3. 组合方式示例

    • 把 xxx-Next 里 ajax.tsHOSTgetDataById 的 URL 指向 app 的 BFF 地址,数据就从 Java BFF 来。
    • 提交仍可先到 Next 的 /api/data,再由 API Route 调 BFF 的提交接口,这样前端只和 Next 打交道,BFF 由 Next 在服务端/API 里调用。

于是可以形成:浏览器 ↔ Next.js(SSR + API Routes)↔ BFF(app)↔ 业务服务。BFF 管「接口与数据」,Next 管「页面与一次转发/适配」。


六、总结

  • BFF:面向前端的接口层,做聚合、适配、鉴权,不负责渲染;本项目中 app 的 Controller + Facade 是典型实现。
  • Next.js SSR:在服务端按请求拉数据并渲染 React 成 HTML,首屏快、利于 SEO;xxx-NextgetServerSideProps 就是 SSR。
  • 区别:BFF 是「接口层」,Next.js(SSR)是「渲染方式」;BFF 可独立于前端技术栈部署,Next 是前端框架自带服务端能力。
  • 联系:Next 的 API Routes 可当薄 BFF 用;SSR 的数据来源可以、也适合来自 BFF;二者组合能同时获得「清晰的数据接口层」和「更好的首屏与 SEO」。

结合项目:app 负责「给前端/移动端提供统一、安全、好用的 API」;xxx-Next 负责「用 Next.js 把页面做出来(SSR),并用 API Route 做提交时的薄 BFF」。理解这一点,就能在架构上分清 BFF 与 Next.js(SSR)各自解决什么问题、如何配合使用。

❌
❌