全栈开发应该更容易简化堆栈联络结论

2025-06-10

全栈开发应该更容易

简化堆栈

联络

结论

一开始,我们只有全栈开发人员。我们用一些PHPRuby On Rails在后端实现了所有功能,然后在前端运行一些jQuery ,这样就完成了。

但时代变了。现代 Web 应用需要丰富的用户界面,而这些界面已无法再在后端渲染。

因此,我们切换到“单页应用程序”模型,其中前端完全管理用户界面。

至于后端,它被简化了,因为它只需要管理领域模型和业务逻辑。

问题是我们现在必须连接前端和后端,而这正是事情变得复杂的地方。

我们构建了一个 Web API(RESTGraphQL等),这显著增加了我们的代码大小并导致重复我们的域模型。

实际上,这就像构建两个应用程序而不是一个。

因此,我们增加了开发人员的数量,由于整体复杂性,我们将他们分为前端开发人员和后端开发人员。

如果你是半栈开发人员,你只能做一半的工作,并且要花很多时间与负责另一半的人沟通。

如果你是一名全栈开发者,那你就是真正的英雄。你可以用更高效、更令人满意的方式,从头到尾实现一个功能。

划分前端和后端开发人员会降低生产力并破坏所有的乐趣。

但说实话,如今成为一名全栈开发人员实在太难了

理想情况下,我们应该像最初那样,全部采用全栈架构。但要做到这一点,我们需要大幅简化整个架构。

简化堆栈

对于最简单的项目,可以使用“无后端”解决方案,例如ParseFirebaseAmplify。但是,当业务逻辑超出 CRUD 操作范围时,效果就不太好了。

八年前,一个名为Meteor的项目问世了(这在软件工程领域可谓是永恒)。它的主要理念是简化前后端之间的通信,在当时颇具革命性。可惜的是,这个项目已经过时了,已经不再适合如今的环境。

最近,两个项目引起了热议——RedwoodJSBlitz.js 它们都致力于简化前后端之间的通信,但方法有所不同。

RedwoodJS 简化了 GraphQL API 的实现,并将ReactPrisma整合在一个自以为是的框架中。

Blitz.js 也是一个使用 React 和 Prisma 的框架,但它基于Next.js构建,并致力于消除对 Web API 的需求。

这些项目正朝着正确的方向发展——简化全栈应用程序的开发——我希望它们能够成功。

但让我介绍一下我在这个领域的尝试——一个名为Liaison的项目,我已经做了一年半了。

联络

我创建 Liaison 时怀着一种执念 — — 尽可能地扁平化堆栈。

典型的堆栈由六层组成:数据访问、后端模型、API 服务器、API 客户端、前端模型和用户界面。

通过 Liaison,堆栈可以被视为重新统一前端和后端的单一逻辑层。

典型堆栈与联络堆栈

问题在于,每一层都会导致更多的代码分散、知识重复、样板和意外的复杂性。

Liaison 通过允许您在单个逻辑层中组装应用程序来解决此问题。

跨层继承

使用 Liaison,可以从后端继承前端。它的工作方式类似于常规的类继承,但跨越了在不同环境中运行的层。

从物理上讲,我们仍然有单独的前端和后端,但从逻辑上讲,我们得到了一个统一整个应用程序的单层。

当从继承自后端类的前端类调用方法时,执行发生在方法实现的位置——在前端还是后端——对于方法的使用者来说并不重要。执行环境被抽象了。

让我们看看使用 Liaison 后,全栈“Hello, World!”会是什么样子。

首先,这是后端:

import {Component, attribute, method, expose} from '@liaison/component';
import {ComponentHTTPServer} from '@liaison/component-http-server';

class Greeter extends Component {
  @expose({set: true}) @attribute() name = 'World';

  @expose({call: true}) @method() async hello() {
    return `Hello, ${this.name}!`;
  }
}

const server = new ComponentHTTPServer(Greeter, {port: 3210});

server.start();
Enter fullscreen mode Exit fullscreen mode

这是前端:

import {ComponentHTTPClient} from '@liaison/component-http-client';

const client = new ComponentHTTPClient('http://localhost:3210');

const Greeter = await client.getComponent();

const greeter = new Greeter({name: 'Steve'});

console.log(await greeter.hello());
Enter fullscreen mode Exit fullscreen mode

注意:本文重点介绍堆栈的架构方面,因此如果您不完全理解代码,请不要担心。

在前端运行时,hello()会调用该方法,而在后端执行则可以看作是一个实现细节。

前端的类Greeter的行为与常规 JavaScript 类类似,并且可以扩展。例如,我们可以hello()像这样重写该方法:

class ExtendedGreeter extends Greeter {
  async hello() {
    return (await super.hello()).toUpperCase();
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,当hello()方法被调用时,执行会在前端和后端同时发生。但同样,执行发生的位置可以看作是一个实现细节。

从开发人员的角度来看,前端和后端是一个整体,这使得一切变得更容易。

数据持久性

大多数应用程序都需要存储数据,而这里的事情也可以大大简化。

这个想法并不新鲜,数据库可以用ORM抽象出来,而这正是 Liaison 所遵循的方法。

简而言之,Storable()mixin 为你的数据带来了持久性。例如,以下类:

import {Component} from '@liaison/component';
import {Storable, primaryIdentifier, attribute} from '@liaison/storable';

class Movie extends Storable(Component) {
  @primaryIdentifier() id;
  @attribute() title;
}
Enter fullscreen mode Exit fullscreen mode

要创建并保存Movie,您可以执行以下操作:

const movie = new Movie({title: 'Inception');
await movie.save();
Enter fullscreen mode Exit fullscreen mode

要检索现有的Movie,您可以执行以下操作:

const movie = await Movie.get({id: 'abc123'});
Enter fullscreen mode Exit fullscreen mode

再次强调,这里没有什么新东西,它类似于任何使用活动记录模式的 ORM 。

Liaison 的不同之处在于,ORM 不再局限于后端。跨层继承机制使得 ORM 也可以在前端使用。

因此,从概念上讲,我们仍然有一个结合前端、后端和数据库的单一逻辑层。

用户界面

在典型的应用程序中,用户界面和领域模型是完全分离的。几年前,这样做是有充分理由的,因为用户界面本质上是由命令式代码构成的。但现在我们有了一些函数式 UI 库(例如带有 hooks 的 React),将用户界面和领域模型结合起来成为可能。

Liaison 允许您将路线和视图作为模型的方法来实现。

以下是如何定义路线的示例:

import {Component} from '@liaison/component';
import {Routable, route} from '@liaison/routable';

class Movie extends Routable(Component) {
  @route('/movies') static List() {
    // Display all the movies...
  }

  @route('/movies/:id') static Item({id}) {
    // Display a specific movie...
  }
}
Enter fullscreen mode Exit fullscreen mode

如您所见,路由只是与模型方法相关联的 URL。

以下是如何实现视图:

import {Component, attribute} from '@liaison/component';
import React from 'react';
import {view} from '@liaison/react-integration';

class Movie extends Component {
  @attribute() title;
  @attribute() year;
  @attribute() country;

  @view() Home() {
    return (
      <div>
        <this.Heading />
        <this.Details />
      </div>
    );
  }

  @view() Heading() {
    return (
      <h3>
        {this.title} ({this.year})
      </h3>
    );
  }

  @view() Details() {
    return <div>Country: {this.country}</div>;
  }
}
Enter fullscreen mode Exit fullscreen mode

视图只是一种根据模型的属性返回某些内容的方法,返回用户界面元素根本不是问题。

由于视图已绑定到模型,因此您可能不需要使用状态管理器(例如ReduxMobX)@view()装饰器确保在属性值更改时自动重新渲染视图。

因此,我们将用户界面封装在域模型中,这样就少了一层需要担心的事情。

结论

我坚信扁平化堆栈对于使全栈开发更容易实现至关重要。

Liaison 允许您在两个物理层(前端和后端)中构建全栈应用程序,它们集中在一个逻辑层中。

使用尽可能少的层来启动一个项目会更容易,但这并不意味着您必须以这种方式构建所有项目。

对于某些项目来说,将应用程序分解成更多层可能是一个好主意。例如,将数据访问与后端模型分离,或者将用户界面与前端模型分离可能会很有用。

不用担心。跨层继承机制允许您在保持单个逻辑层的同时增加物理层。

如果你不擅长面向对象编程,Liaison 肯定不会让你喜欢。但请不要因为觉得面向对象编程的组合模型不好而拒绝它。JavaScript 类可以函数式定义(例如mixins),因此具有极强的组合性。

查看Liaison 文档,开始构建一些东西,然后让我知道您的想法。

本文最初发表于Hacker Noon

鏂囩珷鏉ユ簮锛�https://dev.to/mvila/full-stack-development-should-be-easier-2i2p
PREV
系统设计面试(适合初学者)
NEXT
我看到了网络的未来,那就是以太坊