疯了吧!这帮人居然用 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)。
如下图所示,整个流程是这样的:
- 浏览器发起请求,当请求得到处理,服务端传输数据给浏览器以呈现界面(这个数据一般为 HTML,一种描述呈现方式的语言)。
- 以表单为例,在呈现为表单的界面上进行操作,按照协议和浏览器的实现,则会发起一次新的资源请求(HTTP)。
- 这次请求需要服务器进行资源更改,成功后则会重新返回数据给浏览器以呈现修改后的界面。

<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 的情况下,完成这个功能呢?
基于以上,我们得出了这样的构思:
- 沉淀出一个通用的组件库(来扩充浏览器交互元素),并且这个组件库数量是固定的,我们将其称之为“通用组件”。
- 利用通用组件填充进业务数据后可以配置呈现出业务功能,比如登录表单、项目创建表单等;通用组件复合业务属性后,我们称之为“业务组件”,业务组件个数会随着业务增长而膨胀。
- 需要有一个协议来响应交互,达成上述 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!
- Erda Github 地址:https://github.com/erda-project/erda
- Erda Cloud 官网:https://www.erda.cloud/
疯了吧!这帮人居然用 Go 写“前端”?(一)的更多相关文章
- 疯了吧!这帮人居然用 Go 写“前端”?(二)
作者 | 郑嘉涛(羣青) 来源|尔达 Erda 公众号 前言 上篇我们讲了故事发生的背景,也简单阐述了组件及协议的设想: 一.丰富的通用组件库. 二.组件渲染能力,将业务组件渲染成通用组件 ...
- 一年三篇IF大于7的牛人告诉你怎么写SCI
一年三篇IF大于7的牛人告诉你怎么写SCI 1 研究生必备四本 俗话说好记性不如烂笔头,所以一定要首先养成做笔记的好习惯!作为研究生下面这几个本子是必不可少的: 1.实验记录本(包括试验准备本),这当 ...
- PIC12F629帮我用C语言写个程序,控制三个LED亮灭
http://power.baidu.com/question/240873584599025684.html?entry=browse_difficult PIC12F629帮我用C语言写个程序,控 ...
- 第一章-第六题(帮人抢票,帮人选课这些软件是否合法 你怎么看?)--By梁旭晖
我觉得这些软件是合法的,符合道德规范的. 计算机当初设计的初衷就是简化甚至替代人类的工作.而软件作为计算机硬件的驱动着,其设计就是体现这些原则. 现在互联网上的订票,选课类型的网站还是有很多的,比如: ...
- 大半夜吃饱了撑的帮人调IE玩
那高手的也是IE6,我也是IE6,但是他的IE6就总是进recv,我的IE6就进WSARecv,一点都不科学...擦..不调了.
- 期末人福音——用Python写个自动批改作业系统
一.亮出效果 最近一些软件的搜题.智能批改类的功能要下线. 退1024步讲,要不要自己做一个自动批改的功能啊?万一哪天孩子要用呢! 昨晚我做了一个梦,梦见我实现了这个功能,如下图所示:功能简介:作对了 ...
- sublime text帮你更好的写python
在Google的Python风格指南中,有这样的要求: 用4个空格来缩进代码 但是每次在敲代码的时候,用一个tab确实比敲四次空格方便的多.令人欣慰的是sublime text 2能够把tab转换成4 ...
- 以"小刀会“的成败论当今创业成败
讲起"小刀会",熟悉的人或许非常熟悉,不熟悉的人或许根本不知道清末有这样一个组织. 依据翻查史料,最初的小刀会是在福建成立的,来源有两个.一个是天地会的分支,一个是白莲教分支. 而 ...
- 【ASP.NET Core】Blazor+MiniAPI完成文件下载
今天老周要说的内容比较简单,所以大伙伴们不必紧张,能识字的都能学会. 在开始之前先来一段废话. 许多人都很关心,blazor 用起来如何?其实也没什么,做Web的无非就是后台代码+前台HTML(包含J ...
随机推荐
- 【代码更新】单细胞分析实录(20): 将多个样本的CNV定位到染色体臂,并画热图
之前写过三篇和CNV相关的帖子,如果你做肿瘤单细胞转录组,大概率看过: 单细胞分析实录(11): inferCNV的基本用法 单细胞分析实录(12): 如何推断肿瘤细胞 单细胞分析实录(13): in ...
- yum history使用详解(某次为解决误卸载软件的回退实验)
[root@localhost ~]# yum history list #查看历史 Loaded plugins: fastestmirror ID | Command line | Date an ...
- 如何将声学的spectrogram(声谱图)重新反变换成时域语音信号
最近在研究一些信号分析的事情,感兴趣如何将频谱信号反变换成时域信号.fft 与ifft可以顺畅的转变,但是这个是一帧信号,当时间较长的信号再一起是,通过反变换变成一帧一帧的时域信号,如何把他们拼接起来 ...
- springcloud优雅停止上下线与熔断
SpringCloud 服务优雅上下线 Spring Boot 框架使用"约定大于配置"的特性,优雅流畅的开发过程,应用部署启动方式也很优雅.但是我们通常使用的停止应用的方式是 k ...
- 记一次性能优化的心酸历程【Flask+Gunicorn+pytorch+多进程+线程池,一顿操作猛如虎】
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 本文只是记录我优化的心酸历程.无他,唯记录尔.....小伙伴们可围观,可打call,可以私信与我交流. 干货满满,建议收藏,需要用到时常看看. 小 ...
- 菜鸡的Java笔记 - java 线程常用操作方法
线程常用操作方法 线程的命名操作,线程的休眠,线程的优先级 线程的所有操作方法几乎都在 Thread 类中定义好了 线程的命名和取得 ...
- python实现轮廓发现
目录: (一)轮廓发现的介绍 (二)代码实现 (1)使用直接使用阈值方法threshold方法获取二值化图像来选择轮廓 (2)使用canny边缘检测获取二值化图像 (一)轮廓发现的介绍与API的介绍 ...
- .NET 百万级 大数据插入、更新 ,支持多种数据库
功能介绍 (需要版本5.0.44) 大数据操作ORM性能瓶颈在实体转换上面,并且不能使用常规的Sql去实现 当列越多转换越慢,SqlSugar将转换性能做到极致,并且采用数据库最佳API 操作数据库 ...
- [atAGC051D]C4
考虑将两次移动作为一个整体,两次移动的效果分为:$s-u$.$u-s$和原地不动 对于从$s$回到$s$路径,必然有前两种效果使用次数相同,假设都为$i$(枚举),那么原地不动的次数$j=\frac{ ...
- 关于"丢失的牛"这个题的教学反思
某天上课讲到这样一个题:丢失的牛1~n,乱序排列,告诉从第二个位置到最后一个位置, 每个位置的前面的数字中比它小的数的个数,求每个位置的数字是多少N<=8000 FormatInput第一行给出 ...