Application Lifecycle
This page explains what happens between vef.Run(...) and a live HTTP server.
Boot order
This is the canonical statement of the VEF boot pipeline. It is assembled from
bootstrap.go (vef.Run, which wires config and datasource directly) and
internal/bootmodules.Core() (the ordered list of business modules shared by
vef.Run and the internal/apptest test harness, so the two graphs cannot
drift):
config -> datasource -> middleware -> api -> security -> event -> expression -> cqrs -> cron -> redis -> lock -> mold -> storage -> sequence -> outbox -> redis-stream -> inbox -> schema -> monitor -> mcp -> app
datasource is a single step: it connects *sql.DB (via internal/database)
and wraps it into orm.DB (via internal/orm) in one module — there is no
separate database or orm boot step. outbox, redis-stream, and inbox
are the event transport submodules — the outbox transport module, the
redis-stream transport module, and the inbox module — registered after
sequence and before schema.
That order matters because later modules depend on earlier ones:
- config comes before everything
- datasource comes before API handlers that need
orm.DB - security comes before authenticated API requests
- the event transport submodules (outbox, redis-stream, inbox) come after the
core
eventmodule but before schema, monitor, and MCP - storage, monitor, schema, and MCP are registered before the app starts listening
What vef.Run(...) actually does
vef.Run(...) wires the FX app in this order:
- installs the framework FX logger with
fx.WithLogger(newFxLogger) - adds the internal config module
- adds the internal datasource module
- appends every option returned by
bootmodules.Core() - appends the user-provided
options... - appends
fx.Invoke(startApp) - appends
fx.StartTimeout(defaultTimeout) - appends
fx.StopTimeout(defaultTimeout*2) - creates the app with
fx.New(opts...) - runs it with
app.Run()
defaultTimeout is 30 * time.Second, so the default start timeout is 30s
and the default stop timeout is 60s.
Because user options are appended after bootmodules.Core(), application
modules can append group members through helpers such as
vef.ProvideAPIResource(...). Replacing a core-provided singleton usually
requires vef.Decorate(...), vef.Replace(...), or a framework replacement
helper such as vef.SupplyFileACL(...); registering a second plain
vef.Provide(...) for the same service is not an override.
Advanced modules can receive vef.Lifecycle and call Lifecycle.Append(...)
to register an fx.Hook directly; vef.StartHook, vef.StopHook, and
vef.StartStopHook are convenience constructors for those hooks.
The exact Lifecycle.Append signature is tracked in the public API index.
The internal startApp invoke appends the HTTP server lifecycle hook after the
module graph has been constructed. Its OnStart waits for
application.Start() or the start context timeout, and its OnStop calls
application.Stop().
Minimal boot example:
func main() {
vef.Run(
ivef.Module,
auth.Module,
sys.Module,
web.Module,
)
}
App startup
The application module creates a Fiber app, applies “before” middleware, mounts the API engine, then applies “after” middleware.
That is why middleware ordering in VEF has two layers:
- app-level middleware order around the whole Fiber app
- API-level middleware order inside the API engine
App-level middleware order
From the current middleware module, the common order is:
- compression
- headers
- CORS
- content type checks
- request ID
- request logger binding
- panic recovery
- request record logging
- API routes
- MCP endpoint middleware
- storage proxy routes
- SPA fallback middleware
The important consequence is that app middleware can run even for requests that never reach the API engine.
API-level middleware order
Inside the API engine, the request chain is sorted by middleware order and currently runs as:
- auth
- contextual setup
- data permission resolution
- rate limit
- audit
- handler
This order is what gives handlers access to:
- the authenticated principal
- request-scoped
orm.DB - request-scoped logger
- resolved data permission applier
Startup hooks from built-in modules
Some modules also use lifecycle hooks:
- datasource pings the primary connection and logs the database version
- event bus starts its in-memory dispatcher
- storage initializes providers that need startup work
- app starts the HTTP server and registers a stop hook
So a successful boot means more than “Fiber started”. It means the runtime dependencies were initialized too.
Why this matters when you debug
If a VEF app fails to start, the problem is often one of these:
- config file not found
- invalid database config
- unsupported provider configuration
- constructor registration error in FX
- handler factory resolution failure
Looking at the boot sequence tells you which layer probably failed first.
Next step
Continue to Routing to see how registered resources become actual HTTP endpoints.