app

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package app holds the application assembly layer of the SDK. It provides the reusable pieces of the "build & run" pattern — a deps/provider/server assembly layout — so an application does not re-implement them.

The pattern: a Deps container of lazy dim.Provider fields, an ordered slice of Initializers (infrastructure -> core -> handlers -> workers), and InitDeps which runs each initializer and registers its cleanup with the global closer for LIFO shutdown. Deps itself is application-specific; the SDK supplies the generic InitDeps over it.

Usage

// deps.go (in your application)
var Initializers = []app.Initializer[Deps]{
	initTracer, initStore, initBroker, // infrastructure
	initWidgetCore,                    // core
	initWidgetHandler,                 // handlers
}

// serve command
d := &Deps{Opts: cfg, Logger: log}
if err := app.InitDeps(d, Initializers); err != nil {
	return err
}
defer func() { _ = closer.CloseSync() }() // LIFO release of every cleanup

Each initializer typically assigns a dim.Provider built from a provider factory (value, cleanup, error) and returns that cleanup, so deps declares WHAT to build and provider declares HOW.

Commands and partial dependency sets

The runnable is not always a server (or group of servers): a CLI command — e.g. connect to the DB, run a business function, publish to a queue — also needs dependencies, sometimes only a subset. Two facts make this easy:

  • dim providers are LAZY: an initializer only wires a provider and registers its (no-op-until-built) cleanup; the resource is constructed on first d.X(ctx). So you can run the FULL Initializers slice and only the dependencies the command actually touches get built (and cleaned up) — handlers, consumers and idle infra never connect.

  • Initializers is just data. A command that must NOT touch some infrastructure (or wants to guarantee only certain resources connect) passes a SUBSET slice instead. Compose subsets from named groups with the standard library:

    var ( Infra = []app.Initializer[Deps]{initTracer, initStore, initQueue} Core = []app.Initializer[Deps]{initWidgetCore} Handlers = []app.Initializer[Deps]{initWidgetHandler, initWidgetGRPC} ) // serve: everything. _ = app.InitDeps(d, slices.Concat(Infra, Core, Handlers)) // a "drain queue" command: just store + queue + core, no handlers. _ = app.InitDeps(d, slices.Concat(Infra, Core))

API

  • Initializer[D]: func(*D) (dim.CleanupFunc, error) — builds one dependency.
  • InitDeps[D]: run a []Initializer[D], registering cleanups with the global closer; stops at the first error.
  • Run: supervise runnables (a server.Set) until a signal or failure, then graceful shutdown + closer release. The serve-command entry point.
  • RunCommand[D]: bootstrap a one-shot CLI command — init a subset of deps, run fn with a signal-cancelable context, release resources after.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InitDeps

func InitDeps[D any](d *D, inits []Initializer[D]) error

InitDeps runs each initializer in order against d, registering every non-nil cleanup with the global closer so resources are released in LIFO order on shutdown (pair it with a deferred closer.CloseSync in your serve command). It stops at the first error and returns it; cleanups already registered still run when the caller closes.

This is the reusable form of the per-app InitDeps loop: declare a []app.Initializer[Deps] and call app.InitDeps(&deps, inits).

func Run

func Run(ctx context.Context, cfg RunConfig, runnables ...worker.Runnable) error

Run supervises the given runnables until a shutdown signal arrives or one of them fails, then performs a graceful shutdown and releases every resource registered with the global closer (LIFO). It owns the signal context and the closer lifecycle, so a serve command shrinks to: build deps, build the server set, call Run.

It returns nil on a clean, signal-driven shutdown and the triggering error when a runnable fails on its own.

func RunCommand

func RunCommand[D any](ctx context.Context, cfg CommandConfig, d *D, inits []Initializer[D], fn func(ctx context.Context, d *D) error) error

RunCommand bootstraps a one-shot command: it initializes the given (possibly partial) set of dependencies on d — registering their cleanups with the global closer — runs fn with a signal-cancelable context, then releases every resource via the closer (LIFO), even when fn fails.

This is the CLI counterpart of Run: a command (e.g. connect to the DB, run a business function, publish to a queue) assembles only the dependencies it needs by passing a subset of the application's initializers (compose named groups with slices.Concat), or the full set — dim is lazy, so only the dependencies fn actually touches are built.

Types

type CommandConfig

type CommandConfig struct {
	// Signals override the cancellation signals (default SIGINT, SIGTERM); the
	// context passed to fn is canceled when one arrives, so a long command can
	// abort cleanly.
	Signals []os.Signal
}

CommandConfig configures RunCommand.

type Initializer

type Initializer[D any] func(*D) (dim.CleanupFunc, error)

Initializer builds and assigns one dependency onto the container D, returning an optional cleanup. It is the unit of the assembly pattern: a Deps container of lazy dim.Provider fields plus an ordered slice of Initializers. A typical initializer assigns a provider and returns the resource's cleanup:

func initStore(d *Deps) (dim.CleanupFunc, error) {
	d.DB = dim.NewResource("DB", provider.Postgres(&d.Opts, d.Logger))
	return nil, nil // resource cleanup is returned by NewResource via the provider
}

D is the application's own dependency container — the SDK stays agnostic of its shape.

type RunConfig

type RunConfig struct {
	// Logger is passed to the worker.Group for supervision logging (may be nil).
	Logger *slog.Logger
	// ShutdownTimeout bounds how long the group waits for runnables to Stop
	// (0 means no bound).
	ShutdownTimeout time.Duration
	// Signals override the shutdown signals (default SIGINT, SIGTERM).
	Signals []os.Signal
}

RunConfig configures the long-lived run loop.

Jump to

Keyboard shortcuts

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