2. Backstage 核心架构回顾:App、Plugin、Backend、Frontend 的分层模型

在深入插件通信与 IDP 拓扑验证之前,必须重新梳理 Backstage 的四层架构模型。这四层——App 层、Plugin 层、Backend 层与Frontend 层——构成了所有插件通信的物理边界与逻辑契约。理解每一层的职责、依赖方向以及数据流约束,是后续编写测试用例与构建拓扑验证的基础。

2.1 四层架构总览

Backstage 采用“前端聚合 + 后端微服务”的混合架构。前端部分运行在浏览器中,后端部分运行在 Node.js 进程或 Kubernetes Pod 中。下表总结了各层的核心职责与典型技术栈:

层级 运行环境 核心职责 典型技术栈
App 层 浏览器 / 前端 应用组装、路由注册、主题注入、全局配置 React、React Router、MUI
Plugin 层(前端) 浏览器 / 前端 业务功能卡片、页面组件、前端 API 调用 React、Backstage 插件 SDK
Backend 层 Node.js / 服务端 数据聚合、鉴权、代理、事件分发 Express、Backstage Backend SDK
Frontend 层(狭义) 浏览器 / 前端 底层 HTTP 客户端、Auth 令牌管理、API 网关 fetch、@backstage/core-app-api
关键区分:“Frontend 层”在 Backstage 语境中常指代前端基础设施(如 API 客户端、路由守卫),而“Plugin 层(前端)”指代业务组件。两者在代码包中分属 @backstage/core-app-api 与 @backstage/plugin-* 两个命名空间。

2.2 分层依赖与通信方向

四层之间存在严格的单向依赖关系。违反依赖方向会导致循环引用或运行时错误。下图展示了标准的分层拓扑与通信路径:

Backstage 四层架构与通信方向 App 层(应用组装) Plugin 层(前端业务插件) Frontend 层(API 客户端 / Auth) Backend 层(服务端 / 插件后端) 注册 / 挂载 调用 API 客户端 HTTP / gRPC 请求 说明: 箭头方向表示依赖/调用方向。Backend 层不直接依赖前端三层,前端三层也不直接依赖后端实现细节。 所有跨层通信均通过接口抽象(如 ApiRef、Router)进行。

2.3 各层内部结构详解

2.3.1 App 层:组装与配置中心

App 层是 Backstage 应用的入口。它负责:

  • 插件注册:通过 createApp 或 createPlugin 将前端插件挂载到路由树中。
  • 主题与布局:注入 MUI 主题、侧边栏、顶部导航等全局 UI 元素。
  • 配置注入:将 app-config.yaml 中的配置项分发到各插件。
  • 路由守卫:统一处理登录态、权限校验与页面重定向。
测试关注点:App 层是集成测试的起点。验证插件是否被正确注册、路由是否冲突、全局配置是否透传至子插件,是 IDP 拓扑验证的第一道关卡。

2.3.2 Plugin 层(前端):业务逻辑的载体

每个前端插件是一个独立的 React 应用模块。典型结构包含:

  • 页面组件:如 EntityPage、CatalogPage。
  • API 引用:通过 createApiRef 声明对外部服务的调用接口。
  • 路由扩展:使用 createRouteRef 定义插件内部路由。
  • 卡片与部件:可嵌入到其他插件页面的 EntityCard 或 Widget。
// 典型插件前端入口示例
import { createPlugin, createApiRef } from '@backstage/core-plugin-api';

export const myPlugin = createPlugin({
  id: 'my-plugin',
  routes: {
    root: createRouteRef({ id: 'my-plugin.root' }),
  },
});

export const myApiRef = createApiRef<MyApi>({
  id: 'plugin.my-plugin.api',
});

2.3.3 Frontend 层:基础设施与通信抽象

Frontend 层(狭义)是 Backstage 前端框架的核心基础设施,包括:

  • API 客户端:FetchApi、IdentityApi、ConfigApi 等内置 API。
  • Auth 管理:OAuth2 令牌刷新、Cookie 同步、匿名访问降级。
  • 路由解析:将 RouteRef 映射为实际 URL 路径。
  • 插件通信总线:通过 ApiHolder 实现插件间的服务发现。
常见误区:不要将业务逻辑直接写在 Frontend 层。Frontend 层只提供抽象接口与通信管道,业务逻辑应封装在 Plugin 层中。违反此原则会导致插件难以独立测试与复用。

2.3.4 Backend 层:服务端能力提供者

Backend 层运行在服务端,每个后端插件通常对应一个 Express 子路由。核心职责:

  • 数据聚合:从多个外部系统(如 GitHub、Jenkins、PagerDuty)拉取数据并格式化。
  • 鉴权与代理:统一处理服务端到服务端的认证,避免前端直接暴露令牌。
  • 事件分发:通过 EventBroker 或 WebSocket 推送实时变更。
  • 数据库操作:使用 DatabaseManager 访问 SQLite/PostgreSQL。
// 典型后端插件路由示例
import { createRouter } from '@backstage/plugin-my-plugin-backend';
import { Router } from 'express';

export default async function createPluginRouter(): Promise<Router> {
  return await createRouter({
    logger: env.logger,
    database: env.database,
    config: env.config,
  });
}

2.4 跨层通信契约:ApiRef 与 Router

四层之间的通信并非直接函数调用,而是通过两个核心抽象:

抽象 所在层 作用 测试替身
ApiRef Frontend 层 定义前端插件调用后端服务的接口签名 mockApiRef / TestApiRegistry
Router Backend 层 定义后端插件的 HTTP 路由路径与请求格式 supertest + mockServices

下图展示了 ApiRef 在插件通信中的桥梁作用:

ApiRef 通信契约示意 Plugin A (前端卡片) ApiRef 接口抽象层 Backend 服务 (插件后端) 测试优势: 通过 Mock ApiRef,可以在不启动后端服务的情况下测试前端插件。 通过 Mock Router,可以在不加载前端插件的情况下测试后端接口。 这种解耦使得单元测试与集成测试可以分层独立执行。

2.5 分层模型对测试验证的指导意义

理解四层架构后,可以得出以下测试策略:

  • 单元测试:聚焦 Plugin 层与 Backend 层内部逻辑,通过 Mock ApiRef 与 Mock Router 隔离外部依赖。
  • 集成测试:验证 Frontend 层 + Backend 层的通信链路,使用 TestBackend 与 TestApiRegistry 模拟真实环境。
  • 端到端测试:覆盖 App 层 + 所有 Plugin 层 + Backend 层的完整拓扑,通常使用 Playwright 或 Cypress。
  • 契约测试:验证 ApiRef 定义与实际 Backend 路由实现是否一致,防止接口漂移。