PS:本文所针对的场景,都是复杂业务场景下的 Web 应用。简单的 Web 应用不适合复杂的架构模式,它为带来巨大的成本。
原文地址:https://www.phodal.com/blog/kill-frontend-backend/
在接触了领域驱动设计的概念,其中关于核心域的想法让人颇为激动。而在微服务架构中,核心域是一个或者多个服务的域,而位于核心域的核心则是领域模型。简单地来说,对于一个系统来说,它的核心的核心的 ”核心“ 就是:领域模型。
过去,当我们谈及领域模型的时候,我们往往将其置于 Web 后端的领域。当我 GET 了一些基本的理念之后,便尝试整合到前端架构中。去年,我开始了 在项目上的第一次尝试,并在 GitHub 上创建了 clean-frontend 项目,它以 Angular 作为示例,介绍了如何开发一个整洁前端架构的前端应用。
引子 1:模型一致化
我习惯于在公司的项目中引入成熟的框架,从几年前的 Angular.js 到近几年的 Angular,完整的框架能为企业带来更少的维护成本,从而降低软件开发成本。几年前,我们引入了 Angular 之后,便开始大量地 TypeScript 项目实践。作为一个支持静态类型的语言,它非常适合于开发大型应用、企业应用,在这一点上你可以从 React、Vue 等框架建议使用 TypeScript 看到一种趋势。
采纳了 TypeScript 之后,当前端从后端获取到数据之后,那么它需要将 JSON 数据映射到对应的 interface。如此一来,前端的模型也就有了对应的领域模型。而这个数据模型和后端的数据模型应该是完成一致的,但是实际上,它往往是落后的。后端的 API 发生的变更之后,才需要前端同步去修改模型。
而当我作为一个前端的 Tech Lead 来考虑这个问题的时候,我首先想到的是:读取后端的 Java 代码,然后生成对应的前端模型。在没有造 Chapi 的轮子之前,通过 TypeScript Compiler 转换获得对应的类型,是我觉得比较靠谱的方案。在有了 Chapi 之后,我觉得它们都是小问题了。
不过,然后我一想这样做的意义并不大,还不如:套用后端的契约测试,造一个前端的自动化契约测试,即:mest。它通过 API 返回数据和 TypeScript 的 Interface 来完成对于契约的测试。一个简单的测试数据如下:
-
url,
interface
-
https:
//phodal.github.io/mest-test/error.json,mock/IError.ts
通过 HTTP 请求获取对应的测试数据,再将其与本地的模型进行比较。
不过呢,我们还要使用 JavaScript 语言重写部分 Java 代码,那么我们为什么不要 JavaScript / TypeScript 重写一切呢?
引子 2:JavaScript / TypeScript 重写一切
遗憾的是,使用 JavaScript 编写后端应用(BFF、胶水层除外),在大部分的大公司是比较难的。内部的生态链和运维影响了技术决策,使用 C++ 不香吗,使用 JavaScript 会存在无人在背后支持。
纯 Node.js 后端应用
在过去的几年里,我建议:小公司的后端应用不要使用 JavaScript / TypeScript 编写,因为运维、监控、APM 等生态尚不够完善。小公司应该优先投资于业务领域,在基础设施的投入见效比较短,除非能控制好开发人员的流动性。
不过,不管怎样,已经有大量的小公司因为人力成本的原因,已经使用上了 Node.js 来开发后端应用。
Serverless 应用
而随着 JavaScript 系生态的完善,基础设施已经不是会成为小公司的负担,我便觉得这是一个好的时机。不过,我指的是采用 Serverlesss 架构,而非自建 JavaScript 系生态。Serverless 不仅帮助小企业解决了基础设施的问题,还能为小企业降低软件运维成本。一旦企业做大之后,也可以自建采用 OpenFaas 等开源方案解决 Serverless 的供应商锁定问题。
不论是 Node.js 后端应用还是 Serverless 应用,因为使用的是同一种语言,我们可以轻松地在前后端之间共享代码,可以是 Git submodule、NPM 包、远程等等的方式。
引子 3:纯编译到 JavaScript
市面上有各种各样支持 compile to js 的语言、框架,诸如于 Kotlin.js、Scala.js、Python.js 等等。
对于小前端的应用来说,这种架构非常的不错。它相当于是渐进式的系统架构方案,当前采用了主流的前端框架,而非传统的后端渲染机制,并统一了技术栈,降低了组织内部的学习成本。不过,它带来额外的调试因素,毕竟每多一层封装,系统的复杂度就需要 * 2。
而为了让框架的使用者支持不同的框架 React、Angular、Vue,这个框架还需要提供这些框架的 bind 或者是 wrapper,以提升框架使用者的幸福感。
但是,我们的挑战依然是复杂的前端应用,以及它难以消除交互的复杂度。
引子 4:共享领域模型/模式库
开始之前,我不得不强调一,领域模型是一种包含数据和行为的抽血模型。
编译到 JavaScript 是一种轻前端的方案,而对于重前端的项目来说,它们完成可以采用一种新的模式:共享领域模型。即将领域模型作为模式库,提供给前后端一起使用。即,我们只需要编译所需要的部分。
这在我使用了 Kotlin 的多平台技术(multiplatform)重写了 Chapi 的 domain 层之后,我意识到了这是一个非常迷人的方案。我即使用在前端代码中使用 Chapi 的领域模型,我还需要在后端的代码中使用这套模型。原先,我需要手动翻译一行行代码,现在我并不需要这样的一个步骤。只需要在 pom.xml、build.gradle 或者是 package.json 中引入依赖及其对应的版本即可。
在引入了这部分的代码之后,我们再关注于 UI 交互部分即可。
引子 5:领域模型编译到 WASM
考虑到并非所有的语言都能支持 compile to JavaScript,一种颇为有效的方式就是使用 WASM。
WebAssembly 或称 wasm 是一个实验性的低端编程语言,应用于浏览器内的客户端。WebAssembly 将让开发者能运用自己熟悉的编程语言编译,再藉虚拟机引擎在浏览器内运行。—— 维基百科
我尝试将使用 Go 编写的 Coca 编译成 WASM,但是遇到一系列的问题(我已经忘了),体积似乎是个问题,所以我尝试使用 Rust 去构建另外一种可能性。Rust 官方提供了 Rust Webpack Template,因此我可以将其集成到我现有的前端应用中。只是呢,似乎还没有人会使用 Rust 去编写后端应用。
但是 WASM 提供了一种更友好的方式,即我们不需要重写现有的代码,而是只需要添加一些代码,便可以将现有的后端模型代码提到给前端使用。并且,与混淆后的 JavaScript 相比,它看上去更加安全 —— 学习成本更高一些。
引子 6:ComponentLess
在研究 Serverless 和微前端的期间,我突然有了一点想法,我对于客户端领域的 Serverless 式架构有了一些基本的构想,叫:ComponentLess。尽管有了一些基础的理念,但是还缺乏一个真实可用的 Demo,所以我并没有定义出什么是 ComponentLess。
起先,我以为无代码编程是一个 ComponentLess 方向,但是一研究发现并不是。无代码编程倾向于可视化编程,而 ComponentLess 倾向于使用 DSL 编程。就这一点来说,我便偏向于使用 Web Components + WAM 技术来构建新的前端架构。
ComponentLess
从单体应用转向微服务架构的一大特质是,组件(非单指 UI 组件,可以视为服务)由函数调用转向了 HTTP 调用。而 Serverless 进一步地将微服务的服务级别 HTTP 调用,细化为函数级别的 HTTP 调用。
对于前端领域来说,也是如此。微前端将单体应用拆分一个个的独立运行前端应用,我们可以随意地组合这些应用。进一步地,结合诸如于 Web Component 这样的组件级方案,便可以将拆分细分为到 UI 组件的粒度。我们可以使用 (HTML Imports 已遗弃)script 标签从远程导入:
-
<script
type=
"module" src=
"my-element.js"></script>
-
<my-element></my-element>
因此,在未来,不论是前端开发人员,还是开发人员,都可以通过集成组件的方式来开发应用。也就是说,我们只需要关注于编写核心业务代码即可,剩下的部分可以通过一些特殊的方式来实现。
DSL 抽象化代码
ComponentLess + Serverless 是代码即基础设施开始的一个标志。当代码开始作为基础设施的一部分时,代码便需要以某种方式才能组合到一起。在 Serverless Framework 中,开发人员通过配置接入服务端所使用的基础信息。而 YAML 配置本身也是 DSL 的一种,缺乏灵活度,但是使用非常简单。
事实上,这是两个选择:
配置 + 编程语言。上手容易,迁移难
DSL。上手复杂,易于迁移
但是,无论如何我还是如何 DSL,它听上去有着更丰富的 KPI。至于如何抽象化基础设计代码,可以参考《云研发:研发即代码》一文。
模型复用
不论是 ComponentLess 还是 Serverless,它们都是由函数、UI 组件变为一个独立可运行的单元。为了与别人交互,它需要包含输入和输出。而输入和输出本身是需要一个数据模型作为支撑的,以此才能完成整个系统的稳定性。
而这个话题已经回到我们开头所讨论的内容里。
前后端分离将死?
在所有的引子里, 我们已经准备了所有的论据,所以只需要:
使用可以跨前后端的语言,构建领域模型
将后端服务、前端设施细化为更小的组件
设计 DSL 将领域模型转换到特定平台的代码
你就可以杀死前后端分离,就是这么简单。
前后端分离将死,不是现在,但是可能在五年后开始。
你说呢?
其它
转载:https://blog.csdn.net/gmszone/article/details/104912578