Backstage 后端系统命名规范:从插件、模块到扩展点与服务的统一命名模式指南
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
本篇技术指南系统讲解 Backstage 后端系统(Backend System)中的命名模式(Naming Patterns)。这些模式统一了跨包的导出命名方式,让开发者仅凭名称即可判断导出物的类型、归属与用途——例如看到xxxPlugin即可确认是后端插件实例,看到xxxExtensionPoint即知是扩展点接口或引用。读完本文,你将掌握插件、模块、扩展点、服务四类核心构件的标准命名格式与 ID 规则,并能在自己的 Backstage 插件与模块开发中直接套用。
命名总原则:camelCase 与 kebab-case 的分工
Backstage 后端系统对命名有一条总体规则:
所有名称(导出标识符)一律使用camelCase;唯一例外是插件与模块的ID,它们使用kebab-case(连字符分隔的小写形式)。
这一规则的目的在于保持各包导出的一致性,降低理解成本。当你在代码库中看到形如catalogModuleGithubEntityProvider的标识符时,可以立即推断出这是一个属于catalog插件的、名为github-entity-provider的模块;而catalogProcessingExtensionPoint则表明它是 catalog 插件的 processing 扩展点引用。
ID 的字符集约束为:仅允许字母、数字与连字符(dash),且必须以字母开头。例如'user-settings'、'github-entity-provider'、'catalog.processing'均合法,而'2fa'、'-foo'、'foo_bar'这类形式不合规。
下文将按四类核心构件逐一展开,每类都给出格式表、命名示例与仓库中的真实实现佐证。
插件(Plugins):<camelId>Plugin与<kebab-id>
命名格式
| 描述 | 模式 | 示例 | 备注 |
|---|---|---|---|
| 导出名 | <camelId>Plugin | catalogPlugin、userSettingsPlugin | — |
| 插件 ID | '<kebab-id>' | 'catalog'、'user-settings' | 仅含字母、数字与连字符,且以字母开头 |
标准写法
export const userSettingsPlugin = createBackendPlugin({ pluginId: 'user-settings', ... })后端插件的导出常量以Plugin结尾,插件 ID 使用 kebab-case 并在pluginId字段中声明。
仓库中的真实实现
在 plugins/catalog-backend/src/service/CatalogPlugin.ts 中,catalog 插件正是以catalogPlugin = createBackendPlugin(...)的方式导出;plugins/auth-backend/src/authPlugin.ts 与 plugins/app-backend/src/service/appPlugin.ts 也严格遵循<camelId>Plugin模式。这些常量随后通过包的公共入口统一向外导出,保证了消费方命名体验的一致。
值得留意的是,前端插件的命名遵循相同逻辑。例如 plugins/user-settings/src/plugin.ts 中userSettingsPlugin = createPlugin({ id: 'user-settings', ... }),并在 plugins/user-settings/src/index.ts 中作为userSettingsPlugin导出。这说明<camelId>Plugin+ kebab-case ID 的组合是整个 Backstage(前端与后端)共享的心智模型。
模块(Modules):<pluginId>Module<ModuleId>与<module-id>
命名格式
| 描述 | 模式 | 示例 | 备注 |
|---|---|---|---|
| 导出名 | <pluginId>Module<ModuleId> | catalogModuleGithubEntityProvider | — |
| 模块 ID | '<module-id>' | 'github-entity-provider' | 仅含字母、数字与连字符,且以字母开头 |
标准写法
export const catalogModuleGithubEntityProvider = createBackendModule({ pluginId: 'catalog', moduleId: 'github-entity-provider', ... })模块导出名由「插件 ID 的 camelCase 化 +Module+ 模块自身的 camelCase ID」三段拼接而成。这一命名天然携带了模块归属关系:catalogModuleGithubEntityProvider一眼可知它属于 catalog 插件,服务于 GitHub 实体提供者场景。
仓库中的真实实现
auth 系列模块是这一模式最集中的体现。在 plugins/auth-backend-module-atlassian-provider/src/module.ts 中,模块被命名为authModuleAtlassianProvider = createBackendModule(...);同理还有authModuleAuth0Provider(auth-backend-module-auth0-provider/src/module.ts)、authModuleAwsAlbProvider(auth-backend-module-aws-alb-provider/src/module.ts)等。它们统一遵循<pluginId>Module<ModuleId>规则,全部归属于 auth 插件。
关于createBackendModule的底层契约(如registerExtensionPoint如何将模块与扩展点绑定),可进一步阅读 packages/backend-plugin-api/src/wiring/createBackendModule.ts。
扩展点(Extension Points):Interface 与 Reference 的成对命名
命名格式
| 描述 | 模式 | 示例 |
|---|---|---|
| 接口(Interface) | <PluginId><Name>ExtensionPoint | CatalogProcessingExtensionPoint |
| 引用(Reference) | <pluginId><Name>ExtensionPoint | catalogProcessingExtensionPoint |
| 扩展点 ID | '<pluginId>.<name>' | 'catalog.processing'、'foo.barBaz' |
扩展点的三个命名要素:接口类型使用 PascalCase(<PluginId><Name>ExtensionPoint),引用常量使用 camelCase(<pluginId><Name>ExtensionPoint),ID使用'<pluginId>.<name>'点分形式。注意 ID 中<name>部分允许 camelCase(如'foo.barBaz'),这是扩展点 ID 与插件/模块 ID 的一个差异点。
标准写法
export interface CatalogProcessingExtensionPoint { ... } export const catalogProcessingExtensionPoint = createExtensionPoint<CatalogProcessingExtensionPoint>({ id: 'catalog.processing', ... })接口与引用成对出现:接口描述扩展点对外暴露的 API 形状,引用则是模块在注册时用于「指向」该扩展点的令牌。
仓库中的真实实现
catalog 插件完整实现了这一命名:在 plugins/catalog-backend/src/service/CatalogPlugin.ts 中导入了catalogProcessingExtensionPoint,并在插件内部通过extensionPoint: catalogProcessingExtensionPoint完成注册(见同文件 L121)。扩展点的定义来源是@backstage/plugin-catalog-node包,性能测试中也直接引用了该扩展点(见 plugins/catalog-backend/src/tests/performance/getEntitiesPerformance.test.ts)。
createExtensionPoint本身的实现与类型定义可参考 packages/backend-plugin-api/src/wiring/createExtensionPoint.ts,其测试用例 packages/backend-plugin-api/src/wiring/createBackendModule.test.ts 也展示了createExtensionPoint<string>({ id: 'point' })的用法。
服务(Services):Service / ServiceRef / ServiceFactory 的三件套
命名格式
| 描述 | 模式 | 示例 |
|---|---|---|
| 接口(Interface) | <Name>Service | LoggerService、DatabaseService |
| 引用(Reference) | <name>ServiceRef | loggerServiceRef、databaseServiceRef |
| 服务 ID | <pluginId>.<name> | 'core.rootHttpRouter'、'catalog.catalogClient' |
| 工厂(Factory) | <name>ServiceFactory | loggerServiceFactory、databaseServiceFactory |
服务是后端系统中组件间解耦的核心机制,其命名由四件套构成:接口(<Name>Service)、引用(<name>ServiceRef)、ID(<pluginId>.<name>)与工厂(<name>ServiceFactory)。
标准写法
export interface CatalogClientService { ... } export const catalogClientServiceRef = createServiceRef<CatalogClientService>({ id: 'catalog.catalogClient', ... }) export const catalogClientServiceFactory = createServiceFactory({ service: catalogClientServiceRef, ... })三者的职责边界:CatalogClientService定义服务能力;catalogClientServiceRef是服务在依赖注入系统中的唯一令牌;catalogClientServiceFactory则负责实例化该服务并在插件启动时按需装配。
核心服务(Core Services)的例外:coreServices 与 mockServices
上述「每个服务引用都导出为独立xxxServiceRef常量」的模式对核心服务存在一个例外:
@backstage/backend-plugin-api将所有核心服务的引用统一收纳进单个coreServices集合;@backstage/backend-test-utils将所有 mock 服务实现统一收纳进单个mockServices集合。
因此在真实代码中,loggerServiceRef与databaseServiceRef并不存在,取而代之的是coreServices.logger与coreServices.database。文档建议:除非你需要导出的服务数量非常多,否则普通插件应避免逐个导出xxxServiceRef的模式,优先复用coreServices。
仓库证据清晰可见:packages/backend-plugin-api/src/services/definitions/coreServices.ts 中export namespace coreServices { ... }收纳了auth、userInfo、cache、rootConfig等引用(例如auth = createServiceRef<AuthService>({ id: 'core.auth' })),服务 ID 全部采用'core.<name>'点分形式;packages/backend-test-utils/src/services/mockServices.ts 中同样以export namespace mockServices { ... }形式导出全部 mock 实现,供测试场景直接注入。
此外,@backstage/backend-plugin-api的 alpha 入口 packages/backend-plugin-api/src/alpha/refs.ts 中actionsServiceRef、metricsServiceRef、tracingServiceRef等仍是逐个导出的形态,可作为「插件自有服务引用」命名方式的参考样例。
Root 前缀:推荐而非强制
对于 root 作用域(scope: 'root')的服务,通常推荐在接口命名上以Root作为前缀,但并非强制要求:
- 遵循该模式的例子:
RootHttpRouterService、RootLifecycleService; - 反例:
ConfigService同样是 root 作用域服务(在 coreServices.ts 中可见其引用rootConfig = createServiceRef<RootConfigService>({ id: 'core.rootConfig', scope: 'root' })),却没有Root前缀。
从命名一致性的角度,新服务建议优先考虑Root前缀以快速识别生命周期语义;但这不是硬性约束,接口的实际命名可结合服务定位与既有约定权衡。
命名模式速查与实战建议
| 构件 | 导出名 | ID | 示例 | | ---- | ------ | -- | ---- | | 插件 |<camelId>Plugin|'<kebab-id>'|catalogPlugin/'catalog'| | 模块 |<pluginId>Module<ModuleId>|'<module-id>'|catalogModuleGithubEntityProvider/'github-entity-provider'| | 扩展点接口 |<PluginId><Name>ExtensionPoint| — |CatalogProcessingExtensionPoint| | 扩展点引用 |<pluginId><Name>ExtensionPoint|'<pluginId>.<name>'|catalogProcessingExtensionPoint/'catalog.processing'| | 服务接口 |<Name>Service| — |CatalogClientService| | 服务引用 |<name>ServiceRef|'<pluginId>.<name>'|catalogClientServiceRef/'catalog.catalogClient'| | 服务工厂 |<name>ServiceFactory| — |catalogClientServiceFactory|
实战中的三条建议:
- 先查命名,再写代码:在创建新插件、模块或扩展点之前,先对照上表确定导出名与 ID,避免后续重构成本。ID 一经发布即成为面向用户的稳定标识,改动会破坏既有配置与依赖关系。
- 优先复用核心服务:需要日志、数据库、配置等能力时直接通过
coreServices.xxx注入,不要重复导出xxxServiceRef;仅在服务数量庞大或属于插件自有领域时才考虑xxxServiceFactory独立导出。 - 让测试保持同构:测试中注入 mock 服务时使用
mockServices.xxx集合(见 packages/backend-test-utils/src/services/mockServices.ts),与coreServices的命名一一对应,降低读写测试的心理负担。
延伸阅读
命名模式属于 Backstage 后端系统架构文档体系的一部分,建议按顺序结合阅读:后端系统架构总览、后端(Backends)、服务(Services)、插件(Plugins)、扩展点(Extension Points)、模块(Modules) 与功能加载器(Feature Loaders)。本篇命名规范正是这些架构概念在代码层面的「落地语言」——理解它们,你就能更顺畅地阅读和编写符合 Backstage 社区标准的后端代码。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考