前后端开发的心得体会_基于 Serverless 的前后端一体化微应用开发体验

ad38deeaf75908583cd50c5671058f49.png

背景

Malagu 是基于 TypeScript 的 Serverless First、可扩展和组件化的应用框架。

主要特点:

  1. 「Serverless First」,默认提供适配 Serverless 最佳实践,也支持运行在服务器上
  2. 「支持前后端一体化」,基于 JSON RPC,前端像调用本地方法一样调用后端方法
  3. 前后端「支持 rpc 和 mvc」两种通信形式,mvc 可以满足传统纯后端 rest 风格接口开发需要
  4. 「支持 typeorm」,事务 AOP 和事务传播行为,也可以集成其他的开源 ORM 框架
  5. 提供「类 spring security 的认证和授权」,现在已经集成了「开箱即用的 authing 平台」,实现了 oidc 认证和授权
  6. 「组件化」,框架本身也是基于组件化实现,将复杂大型项目拆解成一个个 Malagu 组件,提高了代码的复用能力、降低了代码维护难度
  7. 「依赖注入和 AOP」,抽象了一套前后端统一的依赖注入,依赖注入的体验与 spring 差不多
  8. 「命令行工具插件化」,默认提供初始化、运行、构建、部署能力,通过插件可以扩展命令行的能力
  9. 「零配置」,提供了一套默认行为,你也可以通过 Malagu 配置文件覆盖默认行为。
  10. 「支持适配任意前端框架」,目前集成了 React 前端框架,另外,UI 框架集成了 antd、md、grommet 等

简介

本文将使用 Malagu 框架介绍基于 Serverless 的前后端一体化微应用开发体验,当然基于传统服务器的前后端一体化微应用开发也是可行的,相较于 Serverless 来说,部署运维成本更高,资源利用率更低。前后单一体化,不仅仅是前后端使用同一种开发语言、同一套 IoC 方案、同一个项目开发等等,这里面其实包含了更多的丰富内涵和创新空间。接下来,我将向大家展示 Malagu 在前后端一体化的开发体验是怎么样的。

一、前期准备

安装 yarn 和 malagu cli 工具,其中 malagu cli 是辅助你开发、调试和发布基于 Malagu 框架开发的项目的命令行工具。因为 malagu 提供的模板,默认使用 yarn 管理依赖,所有也需要安装 yarn 工具。

npm install -g yarn
npm install -g @malagu/cli

二、初始化前后端一体化项目

我们可以使用 malagu init 命令快速基于模板创建初始化项目,命令行提供了一些列不同场景的内置模板,在这里,我们选择 fc-admin-app 这个前后端一体化的模板(支持模糊搜索模板)。

malagu init

初始化后的项目结构如下:

.
├── .github
│   └── workflows
│       └── deploy.yml 当你将代码提交到 Github 上,下次提交代码的时候,自动触发对于环境的 CI/CD
├── .gitignore
├── .malagu             与 Malagu 相关的配置文件、构建编译结果等等文件
│   ├── backend         后端相关的文件
│   │   ├── dist       后端构建编译输出的产物 
│   │   │   ├── index.js
│   │   │   └── index.js.map
│   │   └── malagu.yml 该文件修改无效,框架把所有 Malagu 组件的配置文件进行拓扑排序并合并的通用配置和后端配置的最终配置文件
│   ├── bootstrap      当选择部署到阿里云函数计算平台,默认采用 Custom 运行时,bootstrap 为启动文件
│   └── frontend       前端相关的文件
│       ├── dist       前端构建编译输出的产物
│       │   ├── 0.c6ed55dba53a380668f5.js
│       │   ├── 1.5221182caa277536b162.js
│       │   ├── 10.859a880f04b6d20e3ee2.js
│       │   ├── 11.e4f1cf442ae521a66b25.js
│       │   ├── 2.fa57b5d2f3e9b8cef6fd.js
│       │   ├── 4.9a057e8d19275402be2a.js
│       │   ├── 5.f46d5a366fac8d44e16c.js
│       │   ├── 6.400e58dfb6324d61b6f0.js
│       │   ├── 7.68ede76a9cedea2f3ddf.js
│       │   ├── 8.80d19d1ade1c304f7311.js
│       │   ├── 9.f78098320e6470c6e3e3.js
│       │   ├── favicon.ico
│       │   ├── index.html
│       │   ├── main.104fd814ecc08b3a89d8.js
│       │   ├── service-worker.js
│       │   └── workbox-25caeab4.js
│       └── malagu.yml                该文件修改无效,框架把所有 Malagu 组件的配置文件进行拓扑排序并合并的通用配置和前端配置的最终配置文件
├── .vscode
│   └── launch.json                   启动 vscode 单步调试的配置文件
├── README.md
├── favicon.ico                        网站图标,网站入口文件 index.html 默认提供,你也可以在项目根下面自定义 index.html 文件
├── malagu-database.yml                可选,数据库相关配置
├── malagu-pre.yml                     可选,预发环境配置
├── malagu-release.yml                 可选,线上环境配置
├── malagu-test.yml                    可选,测试环境配置
├── malagu.yml                         可选,通用配置
├── package.json
├── src
│   ├── browser
│   │   ├── application-lifecycle.ts  前端应用生命周期钩子,此示例中主要用于初始化用户登录状态
│   │   ├── module.ts                 前端模块入口文件
│   │   └── user.view.tsx             前端页面实现
│   ├── common
│   │   ├── index.ts
│   │   └── user-protocol.ts          接口定义
│   └── node
│       ├── entity                     数据库实体类定义文件夹
│       │   ├── index.ts
│       │   └── user.ts               用户实体类定义
│       ├── module.ts                  后端模块入口文件
│       └── user-service.ts
├── tsconfig.json
└── yarn.lock

说明:上面相关的 Malagu 配置默认都可以省略,模板默认提供三套环境:测试、预发、线上,供你选择使用,如果你不需要这么多环境删掉就好了。源码的目录组织不一定要按照上面的来,上面只是默认的组织方式而已。

三、本地运行项目

本地运行命令为 malagu serve,默认端口为 3000,可以通过 -p,--port 自定义端口,本地运行会自动带上 local 模式,也就是说默认会加载组件的 malagu-local.yml 文件,如果你有些配置只会在本地运行用到,你可以配置到 malagu-local.yml 文件里。

ed0e362046696e42cf73985235004fde.gif

四、本地调试项目

大部分情况下,我们通过 console.log 就能解决遇到的很多问题。当然,我们也可以选在使用 vscode 自带的单步调试能力,使用 Malagu 自带的应用模板已经内置了 vscode 单步调试配置,你可以直接使用。前端调试也是类似,只不过你需要使用浏览器的调试工具而已,与传统前端项目并没有什么区别。

1933c142466a80e39c8a8484514284af.gif

五、构建与部署项目

Malagu 框架本身是一个跨平台的应用框架,理论上你可以把它部署到任意云平台或者服务器上,目前 Malagu 已经适配了阿里云函数计算、腾讯云函数、国外的 Vercel 平台(以前叫 Zeit 平台)以及服务器。

下面以部署到阿里云函数计算为例。想要部署到阿里云函数计算上,首先你需要添加一个平台适配器组件:@malagu/fc-adapter(如果是腾讯云函数则是 @malagu/scf-adapter)。

如果你是首次使用阿里云或者函数计算,请先注册一个阿里云账号,并开通函数计算服务。

首次执行 malagu deploy 命令会提示输入账号 ID (必须是主账号) 和 AK(可以是主账号的 AK,也可以是子账号的) 等信息,Malagu 会使用你提供的信息进行部署。如果你使用子账号 AK,请确保子账号拥有函数计算的读写权限,以及 RAM 的 PassRole 权限。如果你以前使用过阿里云官方工具 Funcraft,Malagu 会复用 Funcraft 的账号和 AK 配置。账号信息也可以配置在配置文件中,如 malagu.yml 或者 .env。

Malagu 部署策略是一个应用对应着一个函数,对于服务和函数,框架提供了一套默认配置,你也可以通过配置文件自定义配置,后面会详细介绍。

构建与部署默认会带上 remote 默认,与 local 模式恰恰相反(一个是本地,一个是线上),也就是说默认会加载组件的 malagu-remotel.yml 文件,如果你有些配置只会在线上运行用到,你可以配置到 malagu-remote.yml 文件里。

61be2d6bc137dfc9ddabc688acbebf7d.gif

六、组件配置

组件属性通过 yaml 文件来配置,默认在组件项目的根目录下加载 malagu.yml 属性文件,当 malagu.yml 配置了属性 mode: test(支持指定多个 mode) ,则尝试加载根目录的 malagu-test.yml 属性文件,规则是: malagu-[mode].yml

配置方式如下:

  • 公共属性文件:malagu.yml
  • 模式属性文件:malagu-[mode].yml

支持表达式如下:

引用其他属性值

port: 3000
host: localhost
url: 'https://${host}:${port}' 

引用环境变量值

password: ${env.PASSWORD}

默认值设置

password: '${env.PASSWORD?:123456}'

在代码中使用:

@Component()
export class A {
  
  @Value('foo') // 引用组件属性 foo
  protected readonly foo: string;
}

更多详细文档:组件属性。

七、部署配置

正如前面所说,Malagu 框架本身是一个跨平台的应用框架,往往我们会提供一个平台适配组件来适配对应的平台。开发平台适配组件遵循的是零配置原则,我们会提供一套默认的配置策略。当然,你也可以自定义策略来覆盖全部或者部分策略。不同平台的部署策略是不一样的,以函数计算平台为例,部署的时候,默认的服务名为 malagu,默认的函数名称为你 package.json 里面定义得项目名称,默认的运行时采用 Custom 等等。

自定义服务名:

deployConfig:
  service:
    name: xxxxx

自定义内存大小:

deployConfig:
  function:
    memorySize: 512

绑定自定义域名:

deployConfig:
  customDomain:
    name: demo.malagu.cellbang.com # 需要在你的域名解析配置对于的 CNAME 才能正常访问到你的函数,利润 CNAME 记录值:13048890.cn-hangzhou.fc.aliyuncs.com

更多详细文档:@malagu/fc-adapter 文档。

八、数据库操作

在 Malagu 框架中操作数据库很方便,我们已经为你集成了 TypeOrm 框架,能够很方便地操作各种关系型数据库,以及 MongoDB、Redis 等等,你只需要添加组件 @malagu/typeorm,并配置好数据库链接即可。

添加数据库组件

yarn add @malagu/typeorm  # npm i @malagu/typeorm 

配置数据库链接

支持多数据源配置,存在多个数据源的时候,需要为链接指定名称。数据库链接配置推荐使用单独的配置文件,比如 malagu-database.yml,并且不要提交到 git 仓库。当你需要使用 CI/CD 的时候,项目模板已经提供了一个默认的 Github Actions 配置,你只需要在你的 Github 上配置好数据库相关和云厂商 AK 的秘钥即可使用。

# malagu-database.yml
backend: 
  malagu:
    typeorm:
      ormConfig:
        - type: "${ env.DE_TYPE ?: 'mysql'}"
          host: "${ env.DB_HOST ?: 'localhost'}"
          port: "${ env.DB_PORT ?: 3306}"
          synchronize: true
          username: "${ env.DB_USERNAME ?: 'root'}"
          password: "${ env.DB_PASSWORD ?: 'root'}"
          database: "${ env.DB_DATABASE ?: 'test'}"

操作数据库

import { Transactional, OrmContext } from '@malagu/typeorm/lib/node';
import { Rpc } from '@malagu/rpc'
import { User } from './entity';
import { UserService } from '../common';

@Rpc(UserService)
export class UserServiceImpl implements UserService {
    
    @Transactional({ readOnly: true })
    list(): Promise<User[]> {
        const repo = OrmContext.getRepository(User);
        return repo.find();
    }

    @Transactional({ readOnly: true })
    get(id: number): Promise<User | undefined> {
        const repo = OrmContext.getRepository(User);
        return repo.findOne(id);
    }

    @Transactional()
    async remove(id: number): Promise<void> {
        const repo = OrmContext.getRepository(User);
        await repo.delete(id);
    }

    @Transactional()
    async modify(user: User): Promise<void> {
        const repo = OrmContext.getRepository(User);
        await repo.update(user.id, user);
    }

    @Transactional()
    create(user: User): Promise<User> {
        const repo = OrmContext.getRepository(User);
        return repo.save(user);
    }

}

更多详细文档:@malagu/typeorm 文档。

九、前后端一体化开发

前后端一体化开发主要分为三个步骤:定义接口 -> 后端实现接口 -> 前端调用接口(像调用本地方法一样调用后端接口)。

定义接口

// src/common/user-protocol.ts
export const UserService = Symbol('UserService');

export interface UserService {

    list(): Promise<User[]>;

    get(id: number): Promise<User | undefined>;

    remove(id: number): Promise<void>

    modify(user: User): Promise<void>;

    create(user: User): Promise<User>;
}

export interface User {
    
    id: number;

    name: string;

    age: number;
}

后端实现接口

// src/node/user-service.ts
import { Transactional, OrmContext } from '@malagu/typeorm/lib/node';
import { Rpc } from '@malagu/rpc'
import { User } from './entity';
import { UserService } from '../common';

@Rpc(UserService) // 相当于告诉框架,可以让前端通过 JSON RPC 的方式调用
export class UserServiceImpl implements UserService {
    
    @Transactional({ readOnly: true }) // 开启只读事务
    list(): Promise<User[]> {
        const repo = OrmContext.getRepository(User);
        return repo.find();
    }

    @Transactional({ readOnly: true })
    get(id: number): Promise<User | undefined> {
        const repo = OrmContext.getRepository(User);
        return repo.findOne(id);
    }

    @Transactional() // 开启读写事务
    async remove(id: number): Promise<void> {
        const repo = OrmContext.getRepository(User);
        await repo.delete(id);
    }

    @Transactional()
    async modify(user: User): Promise<void> {
        const repo = OrmContext.getRepository(User);
        await repo.update(user.id, user);
    }

    @Transactional()
    create(user: User): Promise<User> {
        const repo = OrmContext.getRepository(User);
        return repo.save(user);
    }

}

前端调用接口

// src/browser/user.view.tsx
import * as React from 'react';
import { View } from '@malagu/react';
import { RpcUtil } from '@malagu/rpc';
import { DataTable } from 'grommet';
import { UserService, User } from '../common';

function Users() {

    const [ data, setData ] = React.useState<User[]>([]);

    React.useEffect(() => {
        // 通过 RpcUtil 获取接口 UserService 的远程调用代理对象
        const userService = RpcUtil.get<UserService>(UserService);
        // 像调用本地方法一样调用后端接口
        userService.list().then(users => setData(users))

    }, [])
    
    return (
        <DataTable
            margin={{ vertical: 'medium' }}
            columns={[
                {
                    property: 'name',
                    header: 'Name',
                    primary: true
                },
                {
                    property: 'age',
                    header: 'Age'
                }
            ]}
            data={data}
        >
        </DataTable>
    );
}

@View({ component: Users})
export default class {}

写在最后

Malagu 是一个 Serverless First 的应用开发框架,它可以用来开发前端应用、后端应用和前后端一体化应用。Malagu 提供了一种组件机制,组件机制具有可扩展、模块化等特性,而 Malagu 框架本身也是由一系列组件组成,你们可以很轻松地开发属于自己业务的组件,让组件可以在多个业务项目之间进行复用。由于文章篇幅有限,上面只介绍了少部分 Malagu 组件,还有更多有意思的组件等待你们来挖掘。

相关链接

  • 细胞矩阵公司官网地址
  • Malagu 框架项目地址
  • Malagu 框架文档地址
  • Malagu 框架部署到腾讯云函数详细文档
  • Malagu 框架部署到阿里云函数计算详细文档
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在基于serverless构建零售创新应用方面,有以下几个主要优点和适用场景。 首先,serverless架构能够极大地简化应用的开发和部署过程。传统的应用开发需要部署和维护自己的服务器,而serverless则完全移除了这一步骤。开发人员只需要编写核心业务逻辑,将其封装为函数,然后上传到serverless平台即可。这种方式能够大大减少开发和运维的工作量,提升开发效率和创新能力。 其次,serverless架构能够节约成本。由于serverless不需要维护自己的服务器,开发人员只需要根据函数的实际执行时间来付费,而不需要为传统的服务器进行预留。这种按需付费的模式能够极大地降低成本,并且能够根据需求进行弹性扩缩容。 此外,serverless还能够提供更好的可伸缩性和弹性。无论应用的流量如何变化,serverless都能够根据需求自动扩缩容,确保应用的稳定性和高可用性。因此,基于serverless构建的零售创新应用可以灵活应对季节性波动、促销活动等不确定的业务需求。 最后,serverless能够与其他云服务进行无缝集成,使得开发人员能够利用丰富的云服务进行创新。例如,可以使用AI服务进行商品推荐、运营分析等功能,也可以使用云存储服务进行数据存储和管理。这些服务可以直接与serverless函数进行集成,提供更强大的功能和用户体验。 总结来说,基于serverless构建零售创新应用能够简化开发和部署、节约成本、提供可伸缩性和弹性,并且能够与其他云服务进行无缝集成,为零售行业带来更多的创新机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值