app

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 6 Imported by: 0

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

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

func New(opts ...Option) *App

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

func (a *App) Boot() error

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) Booted

func (a *App) Booted() bool

Booted reports whether Boot has completed successfully.

func (*App) Commands

func (a *App) Commands() []Command

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) Container

func (a *App) Container() *container.Container

Container returns the application's DI container.

func (*App) Migrations

func (a *App) Migrations() *migrations.Registry

Migrations returns the application's migration registry.

func (*App) Register

func (a *App) Register(providers ...Provider) *App

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

func (a *App) Run(ctx context.Context) error

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

func (a *App) Scope() *container.Container

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.

func (*App) Test

func (a *App) Test() error

Test boots the application without binding a port, for use in tests. After Test returns, drive HTTP requests through App.Web().Test(req).

func (*App) Web

func (a *App) Web() *web.App

Web returns the underlying HTTP application.

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.

func (*Module) ProviderName

func (m *Module) ProviderName() string

ProviderName implements Named.

func (*Module) Register

func (m *Module) Register(*container.Container) error

Register makes *Module satisfy Provider so it can be passed to App.Register alongside bare providers. It is a no-op: the app flattens a module into its providers and boot hooks at Boot time (see App.Boot), so a module is never registered through this method directly.

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 WithAddr

func WithAddr(addr string) Option

WithAddr sets the HTTP listen address passed to web.App.Run (default ":8080").

func WithContainer

func WithContainer(c *container.Container) Option

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

func WithWebApp(w *web.App) Option

WithWebApp supplies a pre-built *web.App instead of constructing one from WithWebOptions.

func WithWebOptions

func WithWebOptions(opts ...web.Option) Option

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

type RegisterFunc func(c *container.Container) error

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
})

func (RegisterFunc) Register

func (f RegisterFunc) Register(c *container.Container) error

Register implements Provider.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL