通用 API 并不能很好地服务于前端
2021-11-05
来源:CSDN
为什么不行?
在设计通用 API 时,你会遇到一系列闹心的问题:
如何预测和支持所有可能的工作流程?
如何避免某些蹩脚的工作流程中的 N+1 问题?
如何测试每个可能出现的请求的功能、性能和安全性?
如何在不破坏现有工作流程的情况下修改某个 API?
如何根据内部和社区的需求,划分修改 API 的优先级?
如何完善文档,以方便各方顺利完成工作?
从前端的角度来看,还有更多问题需要考虑:
如何收集渲染页面所需的所有数据?
如何优化发往多个端点的多个请求?
如何避免以预期之外的方式使用 API 数据字段?
如何权衡新功能与构建新 API 的成本?
如果只是为了前端而构建后端,你需要考虑这么多问题吗?你需要考虑每一种可能的工作流程,避免 N+1 请求问题,测试每个请求配置?还是应该拒绝某些功能,因为你非常清楚每个页面需要呈现什么?看到这里,你可能明白我想说什么了。
建议
我建议不要将前端视为某个通用 API 的客户端,应将其视为应用的一半。
假设你可以把整个页面所需的 JSON 全部发送给前端。那么只需要创建一个端点/page/a,然后渲染/page/a的整个JSON就可以了。而且,每个页面都应该采用相同的做法。不要强迫前端开发人员发送一堆单独的请求来渲染复杂的页面。不要人为地制造的限制。
这个 JSON 需要负责渲染整个页面。不要渲染抽象模型和集合,而是应该渲染具体的方框、小节、段落、列表等。渲染可视化页面结构。
{
“section1”: {
“topBoxTitle”: “Foo”,
“leftBoxTitle”: “Bar”,
“linkToClose”: “https://…”
},
“section2”: {
…
}
}
这与服务器驱动 UI 类似,但不完全相同。我们可以称之为服务器通知 UI。
哪种方法更好?
看到上面那些繁杂的考虑事项了吗?设计前端专用的 API 就不会为这些问题闹心了。
你可以自由决定:我想要一个页面A。然后只需在后端和前端实现页面A。非常简单。
我们无需再考虑:必须引入哪些 API 工作流程,才能成功地渲染这个页面?页面 A 的实现非常简单,只需要实现页面本身的功能。你可以完整地测试页面A,检查 Bug、安全和性能问题。你甚至可以通过一个大型 SQL 查询语句,获取页面 A 所需的所有数据。你可以将页面 A 的整个 JSON 放入缓存。
前端非常清楚页面 A 中每个字段的用途。这些字段没有歧义,它们准确地代表了前端的需求。
当需要修改页面 A 时,你只需径直打开页面 A,完成修改就行了,无需花大把时间开会讨论如何修改后端 API 才能实现前端的变更。这个 API 只服务于页面 A,不需要精心设计服务多个请求。只有这样,我们才能摆脱自我强加的限制。
此外,业务逻辑可以全部交由后端负责,无需在前端和后端之间的分工上浪费精力。前端可以专心呈现页面,而后端则可以专心实现前端所需的内容。目标明确,不是吗?
如何实践?
我曾在多个生产项目中尝试过这种做法。其中有一个是个人的项目,还有一个是在公司现有项目的基础之上进行的重构。我们整个团队都参与了那个项目,而且效果很好。我们遇到的唯一问题就是,前端的工作越来越无聊,因为几乎所有的业务逻辑都由后端负责。同时,后端团队也没有感觉到太大压力。而且大多数时候,我们谈论的都是业务相关的内容,而不是代码。
当然,很多人不太赞同这种做法,常见的反对意见如下。
我希望前端自由(或者,我希望前端解耦)!
不要自欺欺人了,通用 API 并没有赋予前端真正的自由。为了渲染一个页面,需要发送 7 个请求,这不是自由。这是为了满足基本的要求而作茧自缚。一旦需求发生变化,后端也必然需要变更。这样的自由都是偶然的,而且大多是发生在错误的地方。
如果真的想让前端团队自由,直接在 Postgres 上安装一个 GraphQL 包装器,就可以了。
我们本来就需要通用API,这样不是一箭双雕吗?
不,其实你没有必要公开这些 API。真正到了发布的时候,你可能会想:“也许我不应该公开这些API”。通用 API 与前端专用API 的变更有非常大的不同。公开 API 需要支持客户端的工作流程。而前端专用 API 可以随意变更。没有困难,就不要制造困难了。
如果给每个页面构建JSON,那么逻辑代码该如何重用?现在的 CRUD 控制器中大量的逻辑代码都是重用的!
如果编程语言允许,完全可以重用逻辑。你可以使用 mixins、组合、继承,以及其他任何方式。如果能建立良好的抽象,就像乐高积木一样,只需要将积木搭建起来就可以了。
通用 API 可以在移动应用中重用!
通常移动应用都有不同的页面,其中包含的信息和结构不同,变更的原因也不同。专门针对各个页面构建一个后端,可以节省很多时间。
如果页面需要部分XHR 更新怎么办?始终返回整个页面吗?
创建一个只返回特定数据的端点也是没问题的。你完全可以针对页面特定部分的数据建立一个端点。在页面最初加载的时候渲染 React 组件,然后通过调用这些端点的 XHR 更新各个组件。但是,只有某些页面有这样的需求时,才有必要引入这些端点。这是特殊的处理,一般不需要实现。
我的前端是一个SPA,只需要一部分数据,不需要整个页面。
即便是部分数据,也可以作为部分页面结构提供给前端,不需要作为通用资源。只要后端能够满足前端的需求即可。
我正在构建一个站点构建器,所以前端实际上只是站点构建器 API 的测试。
这是一种正确的用法,加油!
你有数据支持这种做法吗?
我希望能够收集到这样的数据,然而,我们很难准确地衡量。谁会同时针对一个软件长期维护两个架构,然后比较二者的效果?本文表达的只是我个人的经历。