个人对业务应用后端的一个构想:

  1. 后端应用核心在于业务逻辑,通过 API 与下游提供数据交换
  2. 业务逻辑:囊括业务所需的一切逻辑,负责与数据库或上游服务进行数据交换
  3. OPEN API:为下游应用提供数据交换,常使用 OAuth、Basic Auth 等鉴权方式
  4. WEB API:为前端网页提供数据交换,常通过 Cookie 中携带的 JWT 信息作为鉴权依据

业务逻辑可以独立存在。没有 API,业务逻辑也能被调用和测试,可以基于业务逻辑运行无 API 的「纯后端服务」,如异步任务、定时任务

API 调用业务逻辑。API 只能调用业务逻辑而非实现业务逻辑,因为 API 中实现的业务逻辑很难被复用或者调用测试

前端展示内容。前端网页 / App 用于展示数据内容,我倾向于技术架构上将前后端分离

API 与鉴权分离。网页 / App 的数据需求基本一致,但鉴权方式可能不同(App 的网络请求没必要依赖 Cookie)。一套 API 逻辑,只需将入口鉴权进行差异化即可为多个类型的调用者提供服务


只开发 1 个应用,无需过多考虑代码和功能的复用,资源限制的情况下(如己方只有 1 人)开发 100 个应用不得不解决复用效率问题。

能否只写一份代码?多个应用的相同功能能否只写一份代码,前后端某些相同逻辑能否只写一份?monorepo、npm 为多个项目提供了共享空间,只要抽象到位,理论上核心代码写一份即可。

能否一键为应用注入能力?一个功能模块可能会在程序生命周期中多个阶段发挥作用,如应用启动时健康检查、API 调用时提供相应能力,能否通过一行代码向应用注入能力而不是根据模块说明在多个地方调用相关代码以避免对模块注入的割裂?此时,需要定义一个后端应用的构成。


参考一个成熟应用 (iOS / Android App) 的生命周期设计

实际上后端应用的生命周期比客户端应用要简单得多,可以根据需要设计一个最简的应用和插件协议,后续按需完善。

应用加载阶段:

plugins.appWillLoad ➡️ app.appDidLoad ➡️ plugins.appDidLoad ➡️ app.checkHealth ➡️ plugins.checkHealth

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
export interface AppProtocol {
appDidLoad?: () => Promise<void>
checkHealth?: () => Promise<void>
plugins: AppPluginProtocol[]
}

export interface AppPluginProtocol {
appWillLoad?: (app: AppProtocol) => void | Promise<void>
appDidLoad: (app: AppProtocol) => void | Promise<void>
checkHealth?: () => void | Promise<void>
}

export class FangchaApp {
public protocol: AppProtocol

public constructor(protocol: AppProtocol) {
this.protocol = protocol
}

public async launch() {
const plugins = this.protocol.plugins || []
for (const plugin of plugins) {
if (plugin.appWillLoad) {
await plugin.appWillLoad(this.protocol)
}
}

const appDidLoad = this.protocol.appDidLoad || (async () => {})
await appDidLoad().catch((err) => {
console.error(err)
throw err
})

for (const plugin of plugins) {
await plugin.appDidLoad(this.protocol)
}

if (this.protocol.checkHealth) {
await this.protocol.checkHealth()
}
for (const plugin of plugins) {
if (plugin.checkHealth) {
await plugin.checkHealth()
}
}
}
}

在基本结构上,实现路由插件 RouterPlugin

基于 FangchaApp 和路由插件,可以实现典型的 WebApp

更多常用插件