Foundation · The wiring of the Web-Stack

Inversion

A dependency injection (IoC) container for Vala — three lifecycles, field injection, multi-registration and scope-local registrations. It is how an Astrologue web application wires itself together at startup.

Version
0.2
Licence
LGPL-3.0-or-later
Package
usm install inversion
Language
Vala

Overview

Why it exists

Inversion is a dependency injection (IoC) container for Vala. Applications declare what they need; the container decides what concrete thing fills each need, and when a new instance is made versus shared. Nothing in the stack reaches out and constructs its own collaborators — the wiring lives in one place, and that place is testable.

The Web-Stack is built around it. Spry's users and permissions, Invercargill-Sql's connections and sessions, an application's own services — all are registered in a container, resolved in scopes, and injected by field. The companion library Invercargill-Sql-Inversion exists purely to make this container and the database layer one motion.

Features

What it hands you

  • Three lifecycles. Transient — a new instance each time; Scoped — one per scope; Singleton — one per container.
  • Field injection. Dependencies resolve via inject<T>() in field initializers; no constructor plumbing.
  • Multi-registration. Register several implementations for one service type and resolve them all at once.
  • Scope-local registrations. Add registrations that only exist within a scope.
  • Fluent API. Chained registration methods keep configuration readable.
  • Registration metadata. Registrations carry typed metadata that other services can query — how the type provider of the data layer finds its mappers.

Installation

Getting the library

usm install inversion
USM resolves invercargill-1 itself.

From source:

git clone <the Inversion repository>
cd Inversion
meson setup build
ninja -C build
ninja -C build install
GLib, GObject and Invercargill are the whole dependency set.
valac --pkg inversion-0.1 my-program.vala
The binding pulls in its .deps chain automatically.

Usage & documentation

Quick start, from the README

Define a service, register an implementation, resolve it through a scope:

using Inversion;

// Define your service
public interface Logger : Object {
    public abstract void log(string message);
}

public class ConsoleLogger : Object, Logger {
    public void log(string message) {
        stdout.printf("[LOG] %s\n", message);
    }
}

// Register and resolve
var container = new Container();
container.register_singleton<ConsoleLogger>((scope) => new ConsoleLogger())
    .as_type(typeof(Logger));

var scope = container.create_scope();
var logger = scope.resolve<Logger>();
logger.log("Hello!");
Verbatim from the README's Quick Start.

Lifecycles

One registration per lifetime policy:

container.register_transient<MyService>((s) => new MyService());
container.register_scoped<MyService>((s) => new MyService());
container.register_singleton<MyService>((s) => new MyService());
Transient: new instance every resolve. Scoped: one per scope. Singleton: one per container.

Field injection

Dependencies are injected using the inject<T>() function in field initializers — resolved automatically when the owning class is created:

public class UserService : Object {
    private Logger logger = inject<Logger>();

    public void greet(string name) {
        this.logger.log(@"Hello, $name!");
    }
}

var userService = scope.resolve<UserService>();
And inject_all<T>() brings in every implementation: private Lot<Logger> loggers = inject_all<Logger>();

Multiple implementations

container.register_singleton<FileLogger>((s) => new FileLogger())
    .as_type(typeof(Logger));
container.register_singleton<ConsoleLogger>((s) => new ConsoleLogger())
    .as_type(typeof(Logger));

// Get all implementations
var allLoggers = scope.resolve_all<Logger>();
From the README.

Scopes with their own registrations

var scope = container.create_scope();

// Add a registration only available in this scope
scope.register_singleton<RequestContext>((s) => new RequestContext())
    .as_type(typeof(Context));
Scopes manage scoped instance lifetime and can carry local registrations.

The repository's examples/ directory walks each feature end to end — BasicUsage.vala, LifecycleDemo.vala, InjectionDemo.vala, MultiRegistration.vala and ScopeRegistrationDemo.vala — and the library's test suite runs through meson test and USM's test exec.

Package & source

The ledger

Version
0.2 (from MANIFEST.usm)
Licence
LGPL-3.0-or-later — free to run, study, share and improve; the LGPL keeps it free to link from any program.
Package
usm install inversion
Provides
libinversion-0.1.so, inversion-0.1.h, inversion-0.1.vapi + .deps, inversion-0.1.gir, inversion-0.1.typelib, inversion-0.1.pc
Needs at runtime
GLib, GObject, and libinvercargill-1.so.
Source
The repository Tilo15/Inversion on the Astrologue forge (git over ssh, port 222). Free software: the licence guarantees your right to the source.