模块与依赖注入
VEF 基于 Uber FX 构建。公开的 vef 包把最常用的 FX helper 重新导出了,所以大多数应用都可以在同一套 API 表面内完成组合。本页、扩展点 和 应用生命周期 三页合起来覆盖了 root vef package 的公开表面。
核心思路
你不需要手动启动每个子系统。你的代码通过向 FX group 提供构造函数来加入正在运行的应用,框架会自动发现并把它们接入依赖图。API 资源、中间件、CQRS behavior 背后都是同一套机制——具体的资源写法见 API 资源;本页讲的是这套机制底层的 DI 原理。
vef.Run(
user.Module,
auth.Module,
)
在内部,vef.Run(...) 会把你的 option 追加到框架自己的模块列表里,再启动整个 FX 应用。
在真实项目中,main.go 常常更像这样:
vef.Run(
ivef.Module, // 框架侧集成
tools.Module, // 通过 vef.ProvideMCP* 注册自定义 MCP provider
web.Module, // SPA 托管
auth.Module, // 认证加载器
sys.Module, // 系统/管理资源
md.Module, // 主数据资源
pmr.Module, // 业务资源
)
这反映出一个重要模式:VEF 应用通常是由多块小模块组合出来的,而不是一个超大的总模块。
vef 重新导出的常用 helper
业务侧最常接触的是这些:
vef.Runvef.Modulevef.Providevef.Supplyvef.Annotatevef.Asvef.Fromvef.ParamTagsvef.ResultTagsvef.Selfvef.Invokevef.Decoratevef.Replacevef.Populatevef.Privatevef.OnStartvef.OnStop
它也重新导出了常用 FX 标记类型:
vef.Invef.Outvef.Lifecyclevef.Hookvef.HookFunc
生命周期 hook 包装函数包括 vef.StartHook、vef.StopHook 和
vef.StartStopHook。
包装层也公开了 vef.From、vef.Replace 和 vef.Populate,用于更高级的 DI
场景。它们就是同名 FX primitive,只是放在 vef 包下,方便框架侧模块通常不
必直接 import go.uber.org/fx。
这样大多数应用代码不需要频繁直接 import fx。
基于 group 的扩展点
很多框架能力都是通过 FX group 串起来的。对应用开发者最重要的是:
vef:api:resourcesvef:api:auth_strategiesvef:app:middlewaresvef:cqrs:behaviorsvef:security:challenge_providersvef:mcp:toolsvef:mcp:resourcesvef:mcp:templatesvef:mcp:promptsvef:event:transportsvef:event:publish-middlewaresvef:event:consume-middlewaresvef:datasource:providersvef:approval:lifecycle_hooks
di.go 里的各种 helper,本质上就是帮你更安全地把值注册进这些 group。helper
名称前缀不一定等于 FX 机制——有的 Provide* 是向 group 追加,有的则是替换默认的单个服务。逐个 helper 的权威表格(机制、group、契约)见扩展点,本页不再重复维护。
API 资源注册
最常用的 helper 是:
vef.ProvideAPIResource(NewUserResource)
它会把构造函数结果打上 API resource group 标签。启动时,API 模块会从这个 group 收集所有资源,并把其中的操作注册进引擎。资源本身怎么写见 API 资源。
其他 provider 也是同一个模式
中间件、CQRS、登录挑战、MCP 也都一样:
vef.ProvideMiddleware(NewAuditTrailMiddleware)
vef.ProvideAuthStrategy(NewAPIKeyStrategy)
vef.ProvideCQRSBehavior(NewTracingBehavior)
vef.ProvideChallengeProvider(NewTOTPChallengeProvider)
vef.ProvideMCPTools(NewToolProvider)
vef.ProvideMCPResources(NewResourceProvider)
vef.ProvideMCPResourceTemplates(NewTemplateProvider)
vef.ProvideMCPPrompts(NewPromptProvider)
vef.ProvideSPAConfig(NewWebConfig)
vef.ProvideEventTransport(NewKafkaTransport)
vef.ProvideEventPublishMiddleware(NewAuditPublishMiddleware)
vef.ProvideEventConsumeMiddleware(NewRecoverConsumeMiddleware)
vef.ProvideDataSourceProvider(NewTenantDataSourceProvider)
这些 helper 的价值不在“增加新能力”,而在于把 FX group tag 隐藏掉,让应用代码更易读。
另一些 helper 是替换框架默认实现,而不是追加 group 成员:
vef.ProvideEventMetricsRecorder(...)vef.ProvideEventErrorSink(...)vef.SupplyFileACL(...)vef.SupplyURLKeyMapper(...)vef.SupplyBusinessRefProvider(...)vef.SupplyBusinessRefResolver(...)
vef.SupplyMCPServerInfo(...) 不同:它 supply 单个 mcp.ServerInfo 值。
vef.SupplySPAConfigs(...) 也不同:它把一个或多个 middleware.SPAConfig 值
supply 到 vef:spa group。
当集成代码需要在 DI 之外拿框架日志接口时,可以使用
vef.NamedLogger(name) 创建 logx.Logger。
可选功能模块
有些框架功能不在默认 boot graph 中。只有应用需要时才显式启用:
vef.Run(
vef.ApprovalModule,
user.Module,
)
vef.ApprovalModule 会开启审批/工作流功能,并注册它的 API resources、
CQRS handlers、engine、业务投影 worker 和 scanners。审批的
approval.* 事件需要 transactional route;路由细节
见审批模块。
大型应用里的模块角色
在更接近生产的 VEF 项目里,通常会出现这些固定角色:
internal/vef:build info、共享框架侧 service、事件订阅器internal/auth:UserLoader、UserInfoLoader、认证相关初始化- 若干业务域模块:注册 API 资源
- 可选的
web和mcp模块:分别负责 SPA 与 MCP 集成
这样职责会比“一个 app 模块包所有东西”清楚得多。
用 vef.Invoke(...) 做集成型模块
在规模更大的应用里,经常会有一个专门的集成模块,通过 vef.Invoke(...) 做启动期 wiring,而不是直接暴露业务资源。
例如:
var Module = vef.Module(
"app:vef",
vef.Supply(BuildInfo),
vef.Provide(NewDataDictLoader, password.NewBcryptEncoder),
vef.Invoke(registerEventSubscribers),
)
这种模块很适合放 build info、共享框架侧 service,以及事件订阅器注册。
为什么资源能自动发现
API engine 不要求你手动挂路由。它会按下面的流程工作:
- 从容器里收集资源
- 从资源本身和嵌入的 CRUD provider 里收集操作
- 解析 handler
- 把 handler 适配成 Fiber handler
- 挂载到 RPC 或 REST router
所以 VEF 的典型开发方式更像“定义资源 + 注册构造函数”,而不是“声明 router + 绑定 handler + 手挂中间件”。
什么时候直接用 fx
大多数应用都可以只用 vef 包装层,但如果你需要这些高级能力,直接用 fx 也完全没问题:
- 更复杂的注解
- 可选依赖
- 直接操作 lifecycle hook
- 测试环境替换实现
VEF 并没有限制你使用 FX,它只是把最常见路径做短了。
下一步
继续看 应用生命周期,理解 vef.Run(...) 从启动到监听端口到底做了什么。