library.inversion.overview.md 3.5 KB

Inversion IoC Container

Overview

Inversion is a dependency injection (IoC) container for Vala that provides inversion of control through component registration, lifecycle management, and field injection. It supports transient, scoped, and singleton lifecycles with a fluent registration API.

Core Components

Container

The central registry for component registrations. Holds singleton instances and creates scopes for scoped resolution.

Scope

Manages scoped instance lifetime. Created from a container and maintains its own cache of scoped instances.

Registration

Represents a component registration with implementation type, service aliases, lifecycle, and factory.

Lifecycle

Enum defining instance lifetime: TRANSIENT, SCOPED, SINGLETON.

Factory

Interface for creating instances. Implementations: DelegateFactory, InstanceFactory.

Dependencies

  • GLib 2.0
  • GObject 2.0
  • Invercargill-1

Quick Start

using Inversion;

// Define service interface and implementation
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);
    }
}

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

// Resolve via scope
var scope = container.create_scope();
var logger = scope.resolve<Logger>();
logger.log("Hello!");

Registration Patterns

Basic Registration

// Register with factory delegate
container.register_singleton<MyService>((scope) => new MyService());

// Register as interface
container.register_singleton<MyImplementation>((scope) => new MyImplementation())
    .as_type(typeof(IMyService));

Multiple Interfaces

container.register<MyImplementation>((scope) => new MyImplementation())
    .as_type(typeof(IServiceA))
    .as_type(typeof(IServiceB))
    .with_lifecycle(Lifecycle.SCOPED);

Multiple Implementations

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

// Resolve all implementations
var all_loggers = scope.resolve_all<Logger>();

Field Injection

Dependencies can be injected via field initializers using inject<T>():

public class UserService : Object {
    private Logger logger = inject<Logger>();
    private Lot<IHandler> handlers = inject_all<IHandler>();
    
    public void greet(string name) {
        this.logger.log(@"Hello, $name!");
    }
}

The injection context is automatically established during container resolution. Field initializers execute within the active scope, allowing dependencies to be resolved.

Error Handling

ContainerError

  • NOT_REGISTERED: Requested type not registered
  • CYCLE_DETECTED: Circular dependency found
  • ILLEGAL_LIFECYCLE_COMBINATION: Invalid lifecycle for scope context

    try {
    var service = scope.resolve<IMyService>();
    } catch (ContainerError.NOT_REGISTERED e) {
    // Handle missing registration
    } catch (ContainerError.CYCLE_DETECTED e) {
    // Handle circular dependency
    } catch (Error e) {
    // Handle other errors
    }
    

Inversion is a Vala IoC container providing dependency injection through component registration with transient, scoped, and singleton lifecycles, field injection via inject<T>(), multi-registration support, and scope-local registrations.