An open API service indexing awesome lists of open source software.

https://gitlab.com/cosban/di

dependency injection in golang made easy through code generation
https://gitlab.com/cosban/di

code generation dependency injection go

Last synced: 9 months ago
JSON representation

dependency injection in golang made easy through code generation

Awesome Lists containing this project

README

          

# di

> di (dependency-injection) saves you from writing boiler-plate setup code in your main (and elsewhere) function by
> automating the creation of functions which build the providers you want injected.

## Installation

`go install gitlab.com/cosban/di/v2/cmd/di@latest`

## Usage

1. Create a `provider` file.
2. Add a `go:generate` comment to provider file like so
```golang
//go:generate di generate --source=filename.go --output=filename.gen.go
```

If the `output` flag is not specified, the generated code will be printed to stdout.

### What is a provider file?

At its core, a provider is simply an exported function that delivers a specific output. This output may or may not serve
as a dependency for other providers.

A provider file serves as a central repository for defining providers and their respective prerequisites. It essentially
encapsulates functions that won't be explicitly invoked by your code.

**Examples**
More detailed examples are located within the [examples/|examples/] directory

```golang
//go:generate di generate --source=example.go
package example

import "fmt"

func Path() string {
return "localhost"
}

func Port() int {
return 80
}

func Address(host string, port int) string {
return fmt.Sprintf("%s:%d", host, port)
}
```

Using the `go generate` command on `example.go` would produce the following output
```golang
// Code generated by di; DO NOT EDIT.
//
// Source: example.go
// Command: di generate --source=example.go
// Version: gitlab.com/cosban/di/v2/cmd/di (devel)
package example

var singleton *ExampleComponent

func GetExampleComponent() *ExampleComponent {
if singleton == nil {
singleton = &ExampleComponent{}
}
return singleton
}

type ExampleComponent struct {
}

func (c *ExampleComponent) Path() string {
return Path()
}
func (c *ExampleComponent) Port() int {
return Port()
}
func (c *ExampleComponent) Address() string {
return Address(
c.Path(),
c.Port(),
)
}
```
Which could then be used in your `main.go` to retrieve a correctly formatted address without having to write the
boilerplate or determine the order of which providers to create first.
```golang
package main

import "example"

func main() {
component := example.GetExampleComponent()
fmt.Printf(component.Address())
}
```

## Troubleshooting Errors

| Type | Example Message | Mitigation |
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| Missing dependencies | `di error: missing dependency detected: Dependency provider(s) for Foo() Bar are missing with the following identifiers: Baz() Qux` | Create a provider function named `Baz` which returns a `Qux` or rename the dependency to match another provider of `Qux` |
| Circular dependency | `di error: circular dependency detected: Foo() Bar <-> Baz() Qux and Baz() Qux <-> Foo() Bar` | Modify the dependency structure between `Foo` and `Baz` to eliminate any cyclical dependencies. |
| Invalid source | `di error: unable to parse source` | Fix your code. |

## Features

### Singleton Providers

Singleton providers are providers which are only created once and then cached for future use. They're singletons.

To create a singleton, insert a `// @singleton` comment above the provider function.

```golang
// @singleton
func Bar() string {
return "bar"
}
```

### Component Embedding

Components, the generated structures that utilize providers to acquire their dependencies, can be integrated within
provider files. This integration enables the reuse and modification of providers not only for testing purposes but also
to eliminate redundant boilerplate code and facilitate the management of complex configurations.

To embed a component, insert `// @component` comment above the provider function responsible for returning a
pointer to the component.

```golang
// @component
func ExampleComponent() *example.Component {
return example.GetExampleComponent()
}
```

Key points to note:
* All providers from the embedded component will be available to the newly generated component.
* Provider definitions within components that share identical identifiers (name and return type) with those defined in
the provider file will be overridden by the latter.
* Multiple components may be embedded within the same provider file but errors will occur if two embedded components
share providers with identical identifiers if that provider is not overridden by the current provider file.
* The component generation process supports nested components, allowing the usage of components which themselves have
embedded other components without any additional configuration.