Modules & Dependency Injection
VEF is built on Uber FX. The public vef package re-exports the core FX helpers so that most applications can stay inside one consistent API surface. This page, Extension Points, and Application Lifecycle together cover the root vef package's public surface.
The key idea
You do not bootstrap subsystems manually. Instead, your code joins the running application by providing constructors into FX groups, and the framework discovers and wires them in automatically. This is the same mechanism behind API resources, middleware, and CQRS behaviors — see API Resources for the concrete resource syntax; this page covers the DI mechanics underneath it.
vef.Run(
user.Module,
auth.Module,
)
Internally, vef.Run(...) appends your options to the framework’s own module list and starts the FX application.
In a real application, main.go often looks more like this:
vef.Run(
ivef.Module, // framework-facing integration
tools.Module, // custom MCP providers registered through vef.ProvideMCP*
web.Module, // SPA hosting
auth.Module, // auth loaders
sys.Module, // system/admin resources
md.Module, // master data resources
pmr.Module, // business resources
)
That structure reflects an important pattern: VEF apps usually compose several small domain or integration modules rather than one giant app module.
Helpers re-exported by vef
The vef package re-exports the FX primitives you use most often:
vef.Runvef.Modulevef.Providevef.Supplyvef.Annotatevef.Asvef.Fromvef.ParamTagsvef.ResultTagsvef.Selfvef.Invokevef.Decoratevef.Replacevef.Populatevef.Privatevef.OnStartvef.OnStop
It also aliases the common FX marker types:
vef.Invef.Outvef.Lifecyclevef.Hookvef.HookFunc
Lifecycle hook wrappers are available as vef.StartHook, vef.StopHook, and
vef.StartStopHook.
The wrapper also exposes vef.From, vef.Replace, and vef.Populate for
advanced DI scenarios. They are the same FX primitives, kept under the vef
package so framework-facing modules can usually avoid importing go.uber.org/fx
directly.
This keeps most application code from importing fx directly unless you need something more specific.
Group-based extension points
Several framework features are connected through FX groups. These are the most important ones for application developers:
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
The helper functions in di.go exist mainly to register values into those groups safely. The helper name prefix does not always describe the FX mechanism — some Provide* helpers append to a group while others replace a default single service. The authoritative, per-helper table (mechanism, group, contract) lives in Extension Points; this page intentionally does not duplicate it.
API resources
The most common helper is:
vef.ProvideAPIResource(NewUserResource)
It annotates the constructor result into the API resource group. During startup, the API module collects every resource from that group and registers their operations into the engine. See API Resources for how to define the resource itself.
Middleware and other providers
The same pattern is used for other extension points:
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)
Each helper hides the FX group tag so that application code stays easier to read.
Some extension helpers replace framework defaults instead of adding group members:
vef.ProvideEventMetricsRecorder(...)vef.ProvideEventErrorSink(...)vef.SupplyFileACL(...)vef.SupplyURLKeyMapper(...)vef.SupplyBusinessRefProvider(...)vef.SupplyBusinessRefResolver(...)
vef.SupplyMCPServerInfo(...) is different: it supplies a single
mcp.ServerInfo value. vef.SupplySPAConfigs(...) is also different: it
supplies one or more middleware.SPAConfig values into the vef:spa group.
Use vef.NamedLogger(name) when application integration code needs a framework
logx.Logger outside dependency injection.
Optional feature modules
Some framework features are not part of the default boot graph. Enable them only when the application needs them:
vef.Run(
vef.ApprovalModule,
user.Module,
)
vef.ApprovalModule turns on the approval/workflow feature and registers its API
resources, CQRS handlers, engine, business-projection worker, and scanners.
Approval's approval.* events require a transactional route; see
Approval Module for the routing details.
Module roles in a larger app
A production-style VEF app often has a few recurring module types:
- an
internal/vefmodule for build info, shared framework-facing services, and event subscribers - an
internal/authmodule forUserLoader,UserInfoLoader, and auth-specific setup - one or more business-domain modules that register API resources
- optional
webandmcpmodules for SPA and MCP integration
This keeps responsibilities obvious and prevents domain modules from becoming catch-all wiring buckets.
Invoke-driven integration modules
In larger applications, a dedicated integration module often uses vef.Invoke(...) for startup-time wiring that does not belong to any one business resource.
Example:
var Module = vef.Module(
"app:vef",
vef.Supply(BuildInfo),
vef.Provide(NewDataDictLoader, password.NewBcryptEncoder),
vef.Invoke(registerEventSubscribers),
)
This is a good home for build metadata, shared framework-facing services, and event-subscriber registration.
Why resources are discovered automatically
The API engine does not need you to mount routes by hand. Instead it:
- collects resources from the DI container
- collects operation specs from the resource itself and from embedded CRUD providers
- resolves handlers
- adapts handlers into Fiber handlers
- mounts them into the RPC or REST router
That design is why VEF code usually looks like “define resource + register constructor” instead of “declare router + bind handler + wire middleware”.
When to use plain fx
Most apps can stay inside the vef wrapper, but using plain fx is still valid when needed, for example:
- advanced annotations
- optional dependencies
- direct access to lifecycle hooks
- test-only overrides
VEF does not prevent direct FX usage. It just tries to make the common paths shorter.
Next step
Continue to Application Lifecycle to see what happens when vef.Run(...) starts the system.