Documentation
¶
Overview ¶
Package app is lagodev's application bootstrap and module layer. It ties the standalone packages — the DI container, the web layer, config and migrations — into a single orchestrated lifecycle so a real program reads
app.New(opts...).
Register(UsersModule, BillingModule).
Run(ctx)
instead of wiring every dependency by hand.
The design follows Laravel's service providers and NestJS's modules, adapted to Go:
- A Provider contributes services to the container in two phases — Register (bind only) then Boot (resolve and use). Boot runs after every provider has registered, so a provider may safely depend on bindings owned by any other provider.
- A Module groups related providers, routes, migrations and commands under one name so a feature is wired with a single Register call.
The App owns a *container.Container, the config-derived state, a *web.App for HTTP, a *migrations.Registry and the ordered provider set. It does not modify any of those packages; it composes them.
Index ¶
- type App
- func (a *App) Boot() error
- func (a *App) Booted() bool
- func (a *App) Commands() []Command
- func (a *App) Container() *container.Container
- func (a *App) Migrations() *migrations.Registry
- func (a *App) Register(providers ...Provider) *App
- func (a *App) Run(ctx context.Context) error
- func (a *App) Scope() *container.Container
- func (a *App) Test() error
- func (a *App) Web() *web.App
- type Booter
- type Command
- type Module
- type ModuleOption
- func WithCommands(fn func() []Command) ModuleOption
- func WithModuleBoot(fn func(c *container.Container) error) ModuleOption
- func WithModuleMigrations(fn func(reg *migrations.Registry)) ModuleOption
- func WithModuleRegister(fn func(c *container.Container) error) ModuleOption
- func WithProviders(p ...Provider) ModuleOption
- func WithRoutes(fn func(r *web.Router)) ModuleOption
- type Named
- type Option
- type Provider
- type RegisterFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the top-level application: it owns the DI container, the HTTP layer, the migration registry and the ordered set of providers/modules, and drives the two-phase boot lifecycle.
Typical use:
a := app.New(app.WithAddr(":8080"))
a.Register(UsersModule, BillingModule)
if err := a.Run(context.Background()); err != nil {
log.Fatal(err)
}
App is not safe for concurrent Register/Boot; configure and boot it from a single goroutine, then serve. The container it owns is itself concurrency safe.
func New ¶
New constructs an App. Defaults: a fresh container, a fresh web.App, and the process-wide migrations.Default registry. The application's own singletons (the container, web app and migration registry) are bound into the container so providers can resolve them.
func (*App) Boot ¶
Boot runs the full lifecycle: every provider's Register phase (in registration order), then every provider's Boot phase, then each module's route and migration callbacks. It is idempotent — calling Boot twice is a no-op after the first success.
Errors are wrapped with the offending provider/module name so failures are actionable.
func (*App) Commands ¶
Commands collects every CLI command contributed by registered modules, in registration order. The app layer does not dispatch commands; the host program (or the cli package) consumes this list.
func (*App) Migrations ¶
func (a *App) Migrations() *migrations.Registry
Migrations returns the application's migration registry.
func (*App) Register ¶
Register adds providers and/or modules in order. It accepts any value that is a Provider (including *Module) so callers may mix both:
a.Register(UsersModule, app.RegisterFunc(bindClock), &MetricsProvider{})
Registration only records order; nothing runs until Boot (or Run/Test). Register returns the App for chaining.
func (*App) Run ¶
Run boots the application and starts the HTTP server, delegating to web.App.Run so its graceful-shutdown semantics are preserved. It blocks until the server stops (signal or error).
The provided context is honoured for the boot phase; cancelling it before boot completes aborts startup. The web server's own signal-based shutdown governs runtime termination.
func (*App) Scope ¶
Scope returns a request-scoped child container derived from the application container. Handlers (or middleware) call it once per request to obtain per-request singletons that inherit application-wide bindings:
rc := a.Scope() defer ... // request lifetime svc := container.MustMake[*RequestSvc](rc)
Per-request scoping is exposed as this hook rather than wired into web.Context, because the web layer threads its own *web.Context and binding the scope into it would require editing web. Middleware that wants a scope per request can capture rc in a closure; see the package tests for the pattern.
type Booter ¶
type Booter interface {
// Boot runs after every provider has registered. It may resolve
// bindings owned by other providers.
Boot(c *container.Container) error
}
Booter is the optional second phase of a Provider. A Provider that also implements Booter has its Boot method called once, after all providers have completed Register, in registration order.
type Command ¶
type Command interface {
// Name is the command's invocation name, e.g. "users:prune".
Name() string
}
Command is an opaque CLI command contributed by a module. The app layer only collects commands; the host program (or the cli package) dispatches them. Kept as an interface so app does not depend on a concrete CLI package.
type Module ¶
type Module struct {
// Name identifies the module in diagnostics and ordering errors.
Name string
// Providers are registered (and booted) in slice order before the
// module's own Register/Boot callbacks.
Providers []Provider
// Register, if non-nil, runs as a register-phase callback after the
// module's providers have registered. Bind module-level services here.
RegisterFn func(c *container.Container) error
// Boot, if non-nil, runs in the boot phase after the module's providers
// have booted. Resolve services and perform module-level wiring here.
BootFn func(c *container.Container) error
// Routes, if non-nil, runs in the boot phase against the application's
// web router. Declare the feature's HTTP routes here.
Routes func(r *web.Router)
// Migrations, if non-nil, runs in the boot phase against the
// application's migration registry. Register the feature's migrations
// here.
Migrations func(reg *migrations.Registry)
// Commands, if non-nil, returns the feature's CLI commands. The slice is
// collected by the app and exposed via App.Commands(); the app layer
// does not run a CLI itself, leaving dispatch to the host program.
Commands func() []Command
}
Module groups everything a feature needs — providers, routes, migrations and CLI commands — under a single name, so an entire feature is wired with one app.Register call.
A Module is a Provider itself: registering a module registers all of its providers (in slice order), then registers the module's own Register/Boot callbacks last so they observe the providers' bindings. During the application boot phase the module's route and migration callbacks run, giving the feature access to the web app and migration registry.
Construct a Module with NewModule and the With* options, or assemble the struct literal directly:
var UsersModule = app.NewModule("users",
app.WithProviders(&UserServiceProvider{}),
app.WithRoutes(func(r *web.Router) {
r.Get("/users", listUsers)
}),
app.WithModuleMigrations(func(reg *migrations.Registry) {
reg.Register(createUsersTable{})
}),
)
func NewModule ¶
func NewModule(name string, opts ...ModuleOption) *Module
NewModule builds a named Module from options.
type ModuleOption ¶
type ModuleOption func(*Module)
ModuleOption configures a Module built with NewModule.
func WithCommands ¶
func WithCommands(fn func() []Command) ModuleOption
WithCommands sets the module's CLI command provider.
func WithModuleBoot ¶
func WithModuleBoot(fn func(c *container.Container) error) ModuleOption
WithModuleBoot sets the module's boot-phase callback.
func WithModuleMigrations ¶
func WithModuleMigrations(fn func(reg *migrations.Registry)) ModuleOption
WithModuleMigrations sets the module's migration registration callback, run in the boot phase against the application's migration registry.
func WithModuleRegister ¶
func WithModuleRegister(fn func(c *container.Container) error) ModuleOption
WithModuleRegister sets the module's register-phase callback.
func WithProviders ¶
func WithProviders(p ...Provider) ModuleOption
WithProviders appends providers to the module.
func WithRoutes ¶
func WithRoutes(fn func(r *web.Router)) ModuleOption
WithRoutes sets the module's route registration callback, run in the boot phase against the application's web router.
type Named ¶
type Named interface {
// ProviderName returns a human-readable identifier.
ProviderName() string
}
Named is an optional Provider/Module capability: a stable name used in boot-ordering errors and diagnostics. Providers that do not implement it are reported by their Go type.
type Option ¶
type Option func(*App)
Option configures an App at construction.
func WithContainer ¶
WithContainer supplies a pre-built container instead of a fresh one. Useful when the host has already bound process-wide services.
func WithMigrations ¶
func WithMigrations(reg *migrations.Registry) Option
WithMigrations supplies the migration registry the app exposes to modules. Defaults to migrations.Default.
func WithWebApp ¶
WithWebApp supplies a pre-built *web.App instead of constructing one from WithWebOptions.
func WithWebOptions ¶
WithWebOptions forwards options to the underlying web.App constructed by New. Ignored if WithWebApp supplies a ready app.
type Provider ¶
type Provider interface {
// Register binds this provider's services into the container.
Register(c *container.Container) error
}
Provider contributes services to the application across two phases.
Register binds services into the container. It must NOT resolve other providers' bindings — they may not exist yet. Keep Register pure: declare factories, no side effects that depend on the rest of the app.
Boot runs after every provider has registered. Here a provider may resolve its dependencies, register routes on the web app, attach event hooks, schedule tasks, and so on. Implement Boot via the optional Booter interface; a provider that only binds services can skip it.
Providers boot in registration order, which is deterministic.
type RegisterFunc ¶
RegisterFunc adapts a plain function into a register-only Provider. Use it for small providers that only bind a few services:
app.RegisterFunc(func(c *container.Container) error {
container.Singleton(c, newClock)
return nil
})