告别 Web API
在构建单页应用程序或移动应用程序时,我们通常需要实现 Web API(REST、GraphQL 等)来连接前端和后端。从技术上讲,这并不难,但却会带来一些不良后果。
想象一下两个星球。“前端”星球使用 JavaScript,“后端”星球也使用 JavaScript 或其他高级语言。
现在假设这些行星需要广泛协作,形成一个整体,称为“应用程序”。
不幸的是,这些行星无法使用它们的母语直接相互交流,它们必须依赖一种名为“Web API”的第三方,这种第三方使用的语言要简单得多。
事实上,大多数 Web API 的语言仅限于 URL、一些 HTTP 动词(GET、POST、DELETE 等)和一些 JSON 的组合。
使用 GraphQL 的 Web API 更为先进,但它们仍然远远落后于 JavaScript 等编程语言的可能性:
- 编程范式是过程式或功能式的(没有面向对象编程)。
- 仅支持最基本的类型(忘记日期、地图、设置等)。
- 缺少引用的概念(只能通过值传递对象)。
在前端和后端之间放置一种基本的语言会增加很多样板并破坏开发体验。
另一个问题是,Web API 需要额外考虑。它必须经过设计、实现、测试、记录等等。坦白说,这一切都很麻烦。
但最糟糕的是,构建 Web API 通常会迫使你降低代码库的质量。事实上,当前端和后端被 Web API 分隔时,保持代码的 DRY 原则和内聚性是相当具有挑战性的。
现在想象一下,我们可以摆脱 Web API。想象一下,前端可以使用其原生语言直接与后端通信。那不是很棒吗?
好消息是,如今借助一组名为Layr的库,这一点已经成为可能。
你好,Layr!
使用Layr,前端和后端在物理上是分离的(它们在不同的环境中运行),但在逻辑上重新统一(就好像它们在同一个环境中一样)。
它是如何工作的?
- 后端由一个或多个类组成,这些类的一些属性和方法明确地暴露给前端。
- 前端为后端类生成一些代理,并且可以像常规 JavaScript 类一样使用这些代理。
Layr 的底层依赖于RPC机制。因此,从表面上看,它可以被看作类似于CORBA、Java RMI或.NET CWF。
但 Layr 却截然不同:
- 它不是一个分布式对象系统。Layr 后端是无状态的,因此整个堆栈中没有共享对象。
- 它不涉及任何样板代码、生成的代码、配置文件或工件。
- 它使用简单但功能强大的序列化协议(Deepr),可实现链式调用、自动批处理或部分执行等独特功能。
Layr 从 JavaScript/TypeScript 开始,但它解决的问题是通用的,并且可以移植到任何面向对象的语言。
例子
让我们实现经典的“Counter”示例,看看使用 Layer 构建全栈应用程序是什么样子。
首先,我们在后端实现“数据模型”和“业务逻辑”:
// backend.js
import {
Component,
primaryIdentifier,
attribute,
method,
expose
} from '@layr/component';
import {ComponentHTTPServer} from '@layr/component-http-server';
class Counter extends Component {
// We need a primary identifier so a Counter instance
// can be transported between the frontend and the backend
// while keeping it's identity
@expose({get: true, set: true}) @primaryIdentifier() id;
// The counter value is exposed to the frontend
@expose({get: true, set: true}) @attribute() value = 0;
// And the "business logic" is exposed as well
@expose({call: true}) @method() increment() {
this.value++;
}
}
// Lastly, we serve the Counter class through an HTTP server
const server = new ComponentHTTPServer(Counter, {port: 3210});
server.start();
天哪!这么多代码就为了一个简单的“计数器”示例?当然,这看起来有点儿夸张,但我们实际上实现了一个功能齐全的后端,它包含数据模型、一些业务逻辑,以及一个暴露所有功能的 HTTP 服务器。
现在我们有了后端,我们可以从前端使用它:
// frontend.js
import {ComponentHTTPClient} from '@layr/component-http-client';
(async () => {
// We create a client to connect to the backend server
const client = new ComponentHTTPClient('http://localhost:3210');
// We get a proxy to the Counter backend class
const Counter = await client.getComponent();
// Lastly, we consume the Counter
const counter = new Counter();
console.log(counter.value); // => 0
await counter.increment();
console.log(counter.value); // => 1
await counter.increment();
console.log(counter.value); // => 2
})();
这里发生了什么?通过调用该counter.increment()
方法,计数器值会增加。需要注意的是,该方法并不存在于前端。它是在后端实现的,因此会在此环境中执行。但从前端的角度来看,实际的执行环境并不重要。该方法在远程执行这一事实可以看作是一个实现细节。
前端中的类Counter
可以扩展以实现特定于前端的功能。以下示例展示了如何重写increment()
方法,以便在计数器达到特定值时显示一条消息:
class ExtendedCounter extends Counter {
async increment() {
// We call the `increment()` method in the backend
await super.increment();
// We execute some additional code in the frontend
if (this.value === 3)
console.log('The counter value is 3');
}
}
}
这就是前端和后端重新整合后的样子。很酷吧?
有什么陷阱?
为什么我们不需要 Web API 而每个人都要构建它们呢?
实现 Web API 的一个好理由是,当您想要通过 REST 等既定协议将后端暴露给一些外部开发者时。但说实话,绝大多数应用程序并没有这个需求。如果您确实需要 Web API,也可以事后再添加,同时继续使用“无 API”方法满足所有内部需求。
另一个原因是,如果您开发的是拥有数百万用户的大型应用程序。事实上,Layr 提供的便利并非没有代价,因此,如果您想要尽可能优化的应用程序,最好选择较低级别的解决方案。
最后,如果您想用 JavaScript 以外的语言实现前端或后端,您仍然可以在堆栈的一侧使用 Layr,但您必须在堆栈的另一端实现可以使用Deepr协议的 API 客户端或服务器。
结论
删除 Web API 可让您更快地构建全栈应用程序,同时提高代码库的质量。
通过在包括一些生产项目在内的多个项目中使用Layr ,我平均能够减少 50% 的代码量并大大提高我的工作效率。
另一个重要方面是开发体验。由于前端和后端不再由 Web API 分隔,您将获得类似于开发独立应用程序的感觉,并且更加有趣。
文章来源:https://dev.to/mvila/good-bye-web-apis-2bel