tree: c5fea609444f524da9fd5ec1f4a28777d2509769 [path history] [tgz]
  1. BUILD.gn
  2. inject.go
  3. inject_test.go
  4. README.md
src/lib/inject/README.md

inject

The inject library simplifies the scaffolding required to start and stop applications, and in particular, command line tools.

Use

With the inject library, the key pieces of an application which carry global state -- think flags, parsed configuration -- are represented as individual pieces dubbed modules.

Each module is responsible for its little piece of the world, and its scope should be small. When creating a module, think about the principle of least knowledge to scope it down to its essence. For instance, in a code generation application, a “templates” module could be responsible for providing the global *template.Template which is used to start the rendering.

Modules can depend on each other, and are allowed to form a directed acyclic graph (DAG). Conceptually, a module will depend on another if it uses the functionality of the child module in order to provide its functionality. In this case, it is common for all dependencies on the child to go through the parent, i.e. the parent provides an abstraction layer over the child. Continuing the example above, a “generator” module which emits code to a io.Writer provided an intermediate representation, would depend on the “templates” module describe above.

An application startup process starts at a single root module, often called the “app” module, then instantiates and starts all dependent module in reverse depth-first traversal order. For instance, with the very simple module graph:

┌───────────┐   ┌───────────┐   ┌───────────┐
│ app       │───▷ generator │───▷ templates │
└───────────┘   └───────────┘   └───────────┘

The application startup would first start the “templates” module, then the “generator” module, and lastly the “app” module.

Defining modules

Concretely, to define a module, create a struct type:

type app struct {
    ...
}

The module itself does not need to be exported, though package structure will often push you to export it.

Each dependency that this module has on other modules is represented as a member annotated with the `inject:""` tag:

type app struct {
    Generator *codegen.Generator `inject:""`
}

When a dependency is listed, this instructs the inject library to ensure the child modules are instantiated, and started before the parent module is itself started. (And vice-versa with stopping, where the parent will be stopped before the child is stopped.)

A module can request to be started by implementing the inject.Start interface, or request to be stopped by implementing the inject.Stop interface.

References

The inject library is a dependency injection framework. The most widely used framework is Guice. This library solves a much smaller problem, that of starting and stopping applications, not of supporting dynamic injection at runtime. Such dynamic injection brings about much complexity: concurrency model, lifetimes, scoping. All of that is purposefully out of scope.

This library is heavily influenced by Facebook's inject and startstop.

Develop

Configure

fx set core.x64 --with //src/lib/inject:tests

And run the tests

fx test inject_test