2

疯了吧!这帮人居然用 Go 写“前端”?(一)

 3 years ago
source link: https://my.oschina.net/erdateam/blog/5121283
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

疯了吧!这帮人居然用 Go 写“前端”?(一)

作者 | 郑嘉涛(羣青) 来源 | 尔达 Erda 公众号

无一例外,谈到前后端分离“必定”是 RESTful API,算是定式了。但我们知道 REST 在资源划分上的设计总是与 UI 大相径庭,大量专用、特异、古怪的接口就像永远拾不尽的菌菇,你费力铲除它们,但一场雷雨便又枯树复披。另一方面接口越来越通用,最后却只剩下 CRUD,美其名曰后端只考虑稳定和性能,大量业务逻辑却全权“丢”给了前端,不禁让人怀疑,这真的是前后端分离了吗?

Erda 作为企业一站式云原生 PaaS 平台,也存在着大量面向使用交互的布局各色的界面:从个人后台到部署总览再到项目设置;从集群管理到监控大盘再到成员管理。我们从 REST 开始做起,但也逐渐发生变化。本文将从头讲述我们如何从确实问题切入,逐步建设和完善 Erda 的前后端分离框架。 ​ 由于整个框架牵涉到太多内容,所以我计划以系列文章的形式来进行详细解读。本文主要介绍其产生的缘由以及设计思路,更多相关的细节会在后续文章中进行展开,包括但不限于: ​

  • 框架实现细节
  • 使用框架后测试如何展开?
  • 稳定性和工程管理
  • ······

简要介绍一下本文的主要结构: ​

  • 朴素的想法
  • 交互 vs 业务
  • 组件及协议

前面三个部分主要介绍了我们建设框架的背景以及方案分析,“组件及协议”部分则整体阐述了框架的核心设计理念。如果你赶时间,建议直接阅读“组件及协议”部分。 ​

朴素的想法

所有软件公司都会遇到的困境,那就是分工。老派开发者会信奉单打独斗全栈的能力,但软件变得复杂之后,分工是不可避免的,而最为显著的就是前后端的分工。

Erda 的前端资源一直是紧缺的,最夸张的时候前后端比例可以达到 1:8,大部分情况下,工程的瓶颈在于前端。我们很朴素的认为,改变这个局面需要后端承担更多的工作,以释放前端的人力。 ​

交互 vs 业务

所谓前后端的分工,从根本上来说是交互和业务的划分。

下图是最通用的场景,前端关注视觉、体验等,后端关注 CRUD、安全、稳定等。而业务流程、权限等则是散落在前端和后端,我们很难说清楚一个具体的业务逻辑究竟是前端实现的,还是后端实现的。

由上文描述可以预见,这样的划分会导致一些问题: ​

  • 重复性工作:进而系统开发会在某些位置产生疏忽(比如前端实现了鉴权,导致后端的鉴权逻辑得不到很好的验证)。
  • 前后端对接面积大:带来沟通成本,往往这个成本由前端同学承担,这也是前端资源短缺的原因之一。

试想一下,你是否经常会看到这样的场景:后端写完接口后拍拍屁股走人,独留下前端默默猜测接口的参数意义。包括后来测试出的 BUG,也“总”被认为是前端的问题。

面对上面的问题,业界也存在很多种解决方法,比如采用 NodeJS:

如上图所示,通过 js 实现来囊括业务流程、权限等逻辑。使得这些逻辑事实上全部纳入前端范畴,而后端则被空心化,也就只剩下大家熟悉的 CRUD。

这种做法在某种程度上是高效的,也符合前端界“大一统”的思潮,但对于前端资源不足的现状来讲,该举措无疑是雪上加霜。

同时,也有激进派会采用低代码平台“一劳永逸”,干脆不需要前端开发,基于平台的配置和少量的后端流程代码仿佛呈现了一个可能的未来:

不过现有的低代码平台大多仍处于发展阶段,完全摒弃前端则会导致交互或者 UI 呈现比较死板和固定,简单来讲就是不够“炫酷”。这也是为什么目前低代码平台大多支撑的是中后台系统。

我们看到业界也有走“复古”路线的,比如 hey.com 就是如此(https://www.hey.com/how-it-works/):

所谓“复古”,指的是页面逻辑大量运用后端渲染,前端只用少量简单的代码(css,js)实现交互细节。就好比古早的 php 以及 jsp 等开发模式,基本上所有的业务逻辑都实现在后端。可以说这种实践是当下对前端框架越来越庞大、nodejs 盛行现状的一种“反叛”。

不过我们也欣喜的看到,即使是所谓“过时”的开发模式,开发出的仍然是包含当代审美的“炫酷”产品、仍有着十分卓越的交互体验。

全栈的可取之处

hey.com 所用的开发模式,从某种角度上看也可以认为是全栈。

一个人开发前端和后端,这件事情有很大的诱惑力。这也意味着: ​

  • 前后端的沟通成本大量减少,即使前后端都需要熟知业务细节。
  • 接口调试成本大量减少,即使接口设计经过充分的评审。
  • 放到更长远的角度,功能变更和迭代的成本也趋于减少。

但是,全栈的要求过高,个人很难做到前后端都精通。不过这种一个人负责到底的思想,倒是非常可取。 ​

Web 的基础是 HTTP。HTTP 请求的目标(target)是资源(resource)。我们访问的所有页面都是资源,页面的链接则是资源定位符(URI)。 ​ 如下图所示,整个流程是这样的: ​

  1. 浏览器发起请求,当请求得到处理,服务端传输数据给浏览器以呈现界面(这个数据一般为 HTML,一种描述呈现方式的语言)。
  2. 以表单为例,在呈现为表单的界面上进行操作,按照协议和浏览器的实现,则会发起一次新的资源请求(HTTP)。
  3. 这次请求需要服务器进行资源更改,成功后则会重新返回数据给浏览器以呈现修改后的界面。
<form action="/users/1">
  <label>Name:</label>
  <input type="text" name="name" value="Bob">
  <input type="submit" value="Submit">
</form>

传统的 web 只有最低限度的弹窗、确认、scroll 等浏览器定义的交互元素,提交(submit)则是交互的终结。不过,随着 web 和 js 的发展,交互有了更多的可能,同时 html5、css3 也为交互提供了更大的基础。

现在,我们最直观的感受就是网站越来越“炫酷”了,同时也越来越“重”了。站在开发者的角度,前端 js 工程的打包速度甚至比后端工程还要更慢。

有没有一种可能,让我们所有的技术都回归到经典 web,回归到面向资源(resource)进行操作,回归到上个世纪重新思考交互?我们进而可以扩充浏览器的交互元素,而不用通过 js 框架实现复杂交互;扩充 HTTP 请求使其更适应浏览器呈现和交互。

举个例子,我们正要开发一个工作看板,如果浏览器已经提供了一个通用的看板交互元素,我们是不是就可以像使用表单 form 一样,只需要利用这个交互元素再“配置”上业务信息,就能够在不使用额外 js 的情况下,完成这个功能呢?

基于以上,我们得出了这样的构思:

  1. 沉淀出一个通用的组件库(来扩充浏览器交互元素),并且这个组件库数量是固定的,我们将其称之为“通用组件”。
  2. 利用通用组件填充进业务数据后可以配置呈现出业务功能,比如登录表单、项目创建表单等;通用组件复合业务属性后,我们称之为“业务组件”,业务组件个数会随着业务增长而膨胀。
  3. 需要有一个协议来响应交互,达成上述 form 表单的交互流程,但是这个协议需要足够强大不仅能支持组件库所有组件的交互流程,还需要支持多组件联动的复杂场景;这个协议运行在 HTTP 之上,但是与 RESTful 的区别在于,REST 只关注数据且它是无状态的,而我们设想的协议需要感知界面呈现以及支持完整的交互流程。

如上图所示,通过组件库和协议,可以承载前端一半以上的工作,同时将部分减少后端与前端重叠的工作,并且前后端仍保有其自身的灵活性。

组件及协议

综上,落实我们得出的三点构思,需要达成三个层次的事情: ​

  • 丰富的通用组件库
  • 组件渲染能力,将业务组件渲染成通用组件
  • 协议渲染能力,以处理复杂交互

我们设计了这样一个渲染框架,分两个部分(上图绿色部分): ​

  • 业务组件渲染器:核心是处理业务数据到通用组件的转化,以及通用组件操作(operation)的处理(handle)。
  • 场景协议渲染器:核心是将一堆组件编排成为一个场景(可以理解为一个页面,如上图右侧的页面结构)以及场景中组件之间的数据绑定,在有交互产生操作(operation)时,负责决策下派以及操作生效后的重新渲染。

上图中间的通用组件库,则是由前端进行定义和维护的,前端的关注点在于要丰富这个通用组件库,以及将每个组件的交互和 UI 做到极致(后面会讲到,这些工作即是前端呈现器)。

这样的设计初衷旨在大量减少前端工作,尤其是前后端对接方面,甚至可以认为对接是“反转”的,体现在两个层面:接口定义的反转和开发时序的变化。

接口定义的反转

接口的定义上,传统的由后端主导转向为前端主导。

传统(或者说现在主流)的后端实现为 RESTful API,而前端直接对接这些散落的 API,并且理解接口内结构体的含义。但在组件化的背景下,所谓“前后端对接”被拆成了两部分:渲染和呈现。

前面所说的渲染框架(后端)实现了业务到通用组件的渲染,前端定义维护了通用组件库,并实现通用组件的“呈现器”,它将通用组件的数据呈现为可视化的形式。

在渲染和呈现之间的是标准化的通用组件结构(Component Data),我们可以认为通用组件即为前端主导定义的接口,后端由原先甩手一个 RESTful API,演变成要“被迫”理解通用组件的数据结构以实现业务逻辑,整个形势发生了反转。

有趣的是,由于通用组件的数量是确定的,我们可以将通用组件开发移植到其他不同呈现介质,甚至可以开发出 CLI 界面的呈现器,且后端代码无需修改。

开发时序的变化

由传统的后端开发完成后前端联调,转向为前端先完成开发而后端配合联调。

“呈现器”使得前端只需要关注通用组件的开发,业务逻辑可以彻底归为后端。如此职责的划分让前端跳出对后端的依赖,可以独立完成设计、开发和调试(只需要 mock 组件数据)。组件的高度复用,前端甚至可以摇身一变成为设计师,跳过高保真设计稿,直接交付实物,而这个时候的后端可能还在与表结构设计苦苦挣扎。

更重要的不仅仅是前端的开发时间缩短。

我们可以类比 RESTful API 形式的前后端分离,同样都是通过“接口”来标准化对接以实现解耦。REST 接口显然是会随着业务而膨胀的,通用组件的真正优势在于彻底剥除业务,使得它的数量相对恒定且极少。我们都知道少即是美(simple is better),实践证明组件越少越能保证每个组件的质量,而前端通过这些高质量组件完成的功能,几乎可以认为,调试的工作都在后端了。

  • 业务组件真的能隔离业务吗?交互怎么办?
  • 协议渲染真的能替换 REST 吗?我只能二选一吗?

在下篇文章中,我们将会详细地介绍组件渲染和协议渲染的运行逻辑,以及我们是如何做到让前端彻底不关心业务的? ​

欢迎参与开源

Erda 作为开源的一站式云原生 PaaS 平台,具备 DevOps、微服务观测治理、多云管理以及快数据治理等平台级能力。点击下方链接即可参与开源,和众多开发者一起探讨、交流,共建开源社区。欢迎大家关注、贡献代码和 Star!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK