Home

Awesome

Go Version GoDoc Build Status Coverage Status GoReport

Dependency injection for Go 1.18+ using Generics and reflection

Functionalities

Installation

go get github.com/tiendc/autowire

Usage

General usage

    // Suppose you have ServiceA and a creator function
    type ServiceA interface {}
    func NewServiceA(srvB ServiceB, repoX RepoX) ServiceA {
        return <ServiceA-instance>
    }

    // and ServiceB
    type ServiceB interface {}
    func NewServiceB(ctx context.Context, repoX RepoX, repoY RepoY) (ServiceB, error) {
        return <ServiceB-instance>, nil
    }

    // and RepoX
    type RepoX interface {}
    func NewRepoX(s3Client S3Client) (RepoX, error) {
        return <RepoX-instance>, nil
    }

    // and RepoY
    type RepoY interface {}
    func NewRepoY(redisClient RedisClient) RepoY {
        return <RepoY-instance>
    }

    // and a struct provider
    type ProviderStruct struct {
        RedisClient RedisClient
        S3Client    S3Client
    }

    var (
        // init the struct value
        providerStruct = &ProviderStruct{...}

        // create a container with passing providers
        container = MustNewContainer([]any{
            // Service providers
            NewServiceA,
            NewServiceB,
            // Repo providers
            NewRepoX,
            NewRepoY,
            // Struct provider (must be a pointer)
            providerStruct,
        })
    )

    func main() {
        // Update some values of the struct provider
        providerStruct.RedisClient = newRedisClient()
        providerStruct.S3Client = newS3Client()

        // Create ServiceA
        serviceA, err := autowire.BuildWithCtx[ServiceA](ctx, container)
        // Create RepoX
        repoX, err := autowire.Build[RepoX](container)
    }

Non-shared mode

    // Set sharedMode when create a container
    container = MustNewContainer([]any{
        // your providers
    }, SetSharedMode(false))

    // Activate non-shared mode inline for a specific build only
    serviceA, err := Build[ServiceA](container, NonSharedMode())

Overwrite values of specific types

This is convenient in unit testing to overwrite specific types only.

    // In unit testing, you may want to overwrite some `repos` and `clients` with fake instances.
    // NOTE: you may need to use `non-shared mode` to make sure cached objects are not used.
    serviceA, err := Build[ServiceA](container, NonSharedMode(),
            ProviderOverwrite[RepoX](fakeRepoX),
            ProviderOverwrite[RepoY](fakeRepoY))
    serviceA, err := Build[ServiceA](container, NonSharedMode(),
            ProviderOverwrite[RedisClient](fakeRedisClient),
            ProviderOverwrite[S3Client](fakeS3Client))

Reclaim memory after use

Typically, dependency injection is only used at the initialization phase of a program. However, it can take some space in memory which will become wasted after the phase. Use the below trick to reclaim the memory taken by the autowire variables.

    var (
        // create a global container
        container = MustNewContainer(...)
    )

    func autowireCleanUp() {
        // Assign nil to allow the garbage collector to reclaim the taken memory
        container = nil
    }

    func main() {
        // Initialization phase
        // Create services and use them
        serviceA, _ := autowire.Build[ServiceA](container)
        serviceB, _ := autowire.Build[ServiceB](container)

        // Clean up the usage, make sure you won't use the vars any more
        autowireCleanUp()
    }

Contributing

License