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 分层依赖与通信方向
四层之间存在严格的单向依赖关系。违反依赖方向会导致循环引用或运行时错误。下图展示了标准的分层拓扑与通信路径:
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 在插件通信中的桥梁作用:
2.5 分层模型对测试验证的指导意义
理解四层架构后,可以得出以下测试策略:
- 单元测试:聚焦 Plugin 层与 Backend 层内部逻辑,通过 Mock ApiRef 与 Mock Router 隔离外部依赖。
- 集成测试:验证 Frontend 层 + Backend 层的通信链路,使用
TestBackend与TestApiRegistry模拟真实环境。 - 端到端测试:覆盖 App 层 + 所有 Plugin 层 + Backend 层的完整拓扑,通常使用 Playwright 或 Cypress。
- 契约测试:验证 ApiRef 定义与实际 Backend 路由实现是否一致,防止接口漂移。