gioc

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2026 License: MIT Imports: 6 Imported by: 0

README

gioc - Go Inversion of Control (IoC) Container

Go Reference Go Report Card License codecov

gioc is a lightweight, type-safe IoC container for Go. It provides automatic constructor injection, interface binding, multiple dependency lifetimes, and build-time dependency graph validation.

Features

  • Automatic Constructor Injection: Constructor parameters are resolved from the container automatically.
  • Interface Binding: Register concrete types against interfaces with ProvideAs[I].
  • Three Lifetimes: Singleton (default), Transient (new each time), Scoped (per-context).
  • Build-time Validation: Call Validate() at startup to catch missing dependencies and cycles before any resolution.
  • Instance-based Container: No global state by default. Create isolated containers with New().
  • Thread-safe: All operations are safe for concurrent use. Scoped resolution uses context.Context.
  • Zero external dependencies: Only the Go standard library.

Installation

go get github.com/mstgnz/gioc

Quick Start

package main

import (
    "fmt"
    "github.com/mstgnz/gioc"
)

type Database struct{ Connection string }
type UserRepo struct{ DB *Database }
type UserService struct{ Repo *UserRepo }

func NewDatabase() *Database             { return &Database{Connection: "localhost:5432"} }
func NewUserRepo(db *Database) *UserRepo { return &UserRepo{DB: db} }
func NewUserService(repo *UserRepo) *UserService { return &UserService{Repo: repo} }

func main() {
    c := gioc.New()

    gioc.Provide(c, NewDatabase)
    gioc.Provide(c, NewUserRepo)
    gioc.Provide(c, NewUserService)

    if err := c.Validate(); err != nil {
        panic(err)
    }

    svc := gioc.Resolve[*UserService](c)
    fmt.Println(svc.Repo.DB.Connection) // localhost:5432
}

API

Container
c := gioc.New()           // create a new container
c.Validate() error        // check for missing deps and cycles
c.Clear()                 // remove all bindings
c.Count() int             // number of registered bindings
c.BeginScope(ctx) context // create a scoped context
Registration
// Register a constructor (parameters auto-resolved)
gioc.Provide(c, NewDatabase)
gioc.Provide(c, NewCache, gioc.AsTransient())
gioc.Provide(c, NewRequestService, gioc.AsScoped())

// Register bound to an interface
gioc.ProvideAs[Logger](c, NewConsoleLogger)

// Register a pre-created instance
gioc.ProvideInstance[*Database](c, db)
gioc.ProvideInstance[Logger](c, myLogger) // register as interface

// Check existence
gioc.Has[*Database](c) // true/false
Resolution
db := gioc.Resolve[*Database](c)

// Scoped resolution (e.g. per HTTP request)
ctx := c.BeginScope(r.Context())
svc := gioc.ResolveFrom[*RequestService](c, ctx)
Global Convenience

A default container is available for simple applications:

gioc.Register(NewDatabase)
gioc.RegisterAs[Logger](NewConsoleLogger)
gioc.RegisterInstance[*Config](cfg)

db := gioc.Get[*Database]()
logger := gioc.Get[Logger]()

ctx := gioc.BeginScope(context.Background())
svc := gioc.GetFrom[*RequestService](ctx)

gioc.Validate()  // validate default container
gioc.Reset()     // clear default container
Scopes
Scope Behaviour
Singleton (default) One instance for the container's lifetime
Transient New instance on every resolution
Scoped One instance per scope context
gioc.Provide(c, NewDatabase)                         // singleton
gioc.Provide(c, NewCache, gioc.AsTransient())        // transient
gioc.Provide(c, NewRequestCtx, gioc.AsScoped())      // scoped

Scoped instances are tied to context.Context, making them safe for concurrent HTTP handlers:

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := container.BeginScope(r.Context())
    svc := gioc.ResolveFrom[*RequestService](container, ctx)
    // svc is unique to this request
}

Examples

See the examples directory:

Contributing

This project is open-source, and contributions are welcome. Feel free to contribute or provide feedback of any kind.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Overview

Package gioc provides a lightweight, type-safe Inversion of Control (IoC) container for Go applications.

It supports three dependency lifetimes (Singleton, Transient, Scoped), automatic constructor injection, interface binding, and build-time dependency graph validation.

Quick start:

c := gioc.New()
gioc.Provide(c, NewDatabase)
gioc.Provide(c, NewUserRepository)
gioc.Provide(c, NewUserService)

if err := c.Validate(); err != nil {
    log.Fatal(err)
}

svc := gioc.Resolve[*UserService](c)

For convenience, a default global container is available:

gioc.Register(NewDatabase)
db := gioc.Get[*Database]()

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BeginScope added in v1.1.0

func BeginScope(ctx context.Context) context.Context

BeginScope creates a scoped context using the default container.

Example:

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := gioc.BeginScope(r.Context())
    svc := gioc.GetFrom[*RequestService](ctx)
}

func Get added in v1.2.0

func Get[T any]() T

Get retrieves an instance of type T from the default container.

Example:

db := gioc.Get[*Database]()

func GetFrom added in v1.2.0

func GetFrom[T any](ctx context.Context) T

GetFrom retrieves an instance of type T from the default container, using the given context for scoped resolution.

Example:

ctx := gioc.BeginScope(r.Context())
svc := gioc.GetFrom[*RequestService](ctx)

func Has added in v1.2.0

func Has[T any](c *Container) bool

Has reports whether a binding for type T exists.

func Provide added in v1.2.0

func Provide(c *Container, constructor any, opts ...Option)

Provide registers a constructor function with the container. The constructor's return type becomes the registration key. Constructor parameters are automatically resolved from the container.

Supported signatures: func() T, func(D1) T, func(D1, D2) T, ...

Example:

gioc.Provide(c, NewDatabase)
gioc.Provide(c, NewCache, gioc.AsTransient())

func ProvideAs added in v1.2.0

func ProvideAs[I any](c *Container, constructor any, opts ...Option)

ProvideAs registers a constructor and binds it to the interface type I.

Example:

gioc.ProvideAs[Logger](c, NewConsoleLogger)
logger := gioc.Resolve[Logger](c) // returns *ConsoleLogger as Logger

func ProvideInstance added in v1.2.0

func ProvideInstance[T any](c *Container, instance T)

ProvideInstance registers a pre-created value as a singleton. The type parameter T determines the registration key, so you can register concrete types or interfaces:

gioc.ProvideInstance[*Database](c, db)       // register as *Database
gioc.ProvideInstance[Logger](c, consoleLog)   // register as Logger interface

func Register added in v1.2.0

func Register(constructor any, opts ...Option)

Register registers a constructor with the default container.

Example:

gioc.Register(NewDatabase)
gioc.Register(NewCache, gioc.AsTransient())

func RegisterAs added in v1.2.0

func RegisterAs[I any](constructor any, opts ...Option)

RegisterAs registers a constructor bound to interface type I with the default container.

Example:

gioc.RegisterAs[Logger](NewConsoleLogger)

func RegisterInstance added in v1.1.0

func RegisterInstance[T any](instance T)

RegisterInstance registers a pre-created instance with the default container.

Example:

gioc.RegisterInstance[*Database](db)
gioc.RegisterInstance[Logger](consoleLogger)

func Reset added in v1.2.0

func Reset()

Reset clears all bindings from the default container. Primarily useful for testing.

func Resolve added in v1.2.0

func Resolve[T any](c *Container) T

Resolve retrieves an instance of type T from the container.

Example:

db := gioc.Resolve[*Database](c)

func ResolveFrom added in v1.2.0

func ResolveFrom[T any](c *Container, ctx context.Context) T

ResolveFrom retrieves an instance of type T, using the given context for scoped dependency resolution.

Example:

ctx := c.BeginScope(r.Context())
svc := gioc.ResolveFrom[*RequestService](c, ctx)

func Validate added in v1.2.0

func Validate() error

Validate checks the default container for missing dependencies and cycles.

Types

type Container added in v1.2.0

type Container struct {
	// contains filtered or unexported fields
}

Container manages dependency bindings and their resolution.

func Default added in v1.2.0

func Default() *Container

Default returns the global default container.

func New added in v1.2.0

func New() *Container

New creates a new, empty Container.

func (*Container) BeginScope added in v1.2.0

func (c *Container) BeginScope(ctx context.Context) context.Context

BeginScope returns a child context that carries a new scope. Scoped dependencies resolved with this context share instances within the same scope.

Example:

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := container.BeginScope(r.Context())
    svc := gioc.ResolveFrom[*RequestService](container, ctx)
}

func (*Container) Clear added in v1.2.0

func (c *Container) Clear()

Clear removes all bindings from the container.

func (*Container) Count added in v1.2.0

func (c *Container) Count() int

Count returns the number of registered bindings.

func (*Container) Validate added in v1.2.0

func (c *Container) Validate() error

Validate checks that every dependency is registered and that the dependency graph contains no cycles. Call this after all registrations are complete (e.g. at application startup) to catch configuration errors early.

type Option added in v1.2.0

type Option func(*bindingConfig)

Option configures how a dependency is registered.

func AsScoped added in v1.2.0

func AsScoped() Option

AsScoped marks the binding as scoped (one instance per scope context).

func AsSingleton added in v1.2.0

func AsSingleton() Option

AsSingleton marks the binding as singleton (default).

func AsTransient added in v1.2.0

func AsTransient() Option

AsTransient marks the binding as transient (new instance every time).

type Scope added in v1.1.0

type Scope int

Scope represents the lifetime of a dependency.

const (
	// Singleton scope (default): one instance for the container's lifetime.
	Singleton Scope = iota
	// Transient scope: new instance on every resolution.
	Transient
	// Scoped scope: one instance per scope context (e.g. per HTTP request).
	Scoped
)

func (Scope) String added in v1.2.0

func (s Scope) String() string

String returns the human-readable name of the scope.

Directories

Path Synopsis
examples
basic command
cycle_detection command
interface_based command
scope_example command

Jump to

Keyboard shortcuts

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