Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(depinject): clarify depinject API #12214

Merged
merged 4 commits into from
Jul 19, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion depinject/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ to simplify the definition of a blockchain by replacing most of app.go's boilerp

## Usage

### `depinject` example
`depinject` includes an expressive and composable [Configuration API](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/depinject#Config).
A core configuration is `Provide`, for example this code snippet

```go
package main
Expand Down Expand Up @@ -39,6 +40,93 @@ func main() {
}
```

demonstrates the registration of free **provider functions** via the `Provide` API. Provider functions form the basis of the
dependency tree, they are introspected then their inputs identified as dependencies and outputs as dependants, either for
another provider function or state stored outside the DI container, as is the case of `&x` and `&y` above.

### Interface type resolution

`depinject` supports interface types as inputs to provider functions. In the SDK's case this pattern is used to decouple
`Keeper` dependencies between modules. For example `x/bank` expects an [AccountKeeper](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/x/bank/types#AccountKeeper) interface as [input to provideModule](https://github.com/cosmos/cosmos-sdk/blob/de343d458aa68c19630177807d6f0e2e6deaf7a9/x/bank/module.go#L224).
Concretely `SimApp` uses the implementation in `x/auth`, but this design allows for this loose coupling to change.

Given the following types

```golang
package duck

type Duck interface {
quack()
}

type AlsoDuck interface {
quack()
}

type Mallard struct{}
type Canvasback struct{}

func (duck Mallard) quack() {}
func (duck Canvasback) quack() {}

type Pond struct {
Duck AlsoDuck
}
```

This usage

```golang
var pond Pond

depinject.Inject(
depinject.Provide(
func() Mallard { return Mallard{} },
func(duck Duck) Pond {
return Pond{Duck: duck}
}),
&pond)
```

results in an *implicit* binding of `Duck` to `Mallard`. This works because there is only one implementation of `Duck`
in the container. However, adding a second provider of `Duck` will result in an error:

```golang
var pond Pond

depinject.Inject(
depinject.Provide(
func() Mallard { return Mallard{} },
func() Canvasback { return Canvasback{} },
func(duck Duck) Pond {
return Pond{Duck: duck}
}),
&pond)
```

A specific binding preference for `Duck` is required.

#### `BindInterface` API

In the above situation registering a binding for a given interface binding may look like

```golang
depinject.Inject(
depinject.Configs(
depinject.BindInterface(
"duck.Duck",
"duck.Mallard"),
depinject.Provide(
func() Mallard { return Mallard{} },
func() Canvasback { return Canvasback{} },
func(duck Duck) APond {
return Pond{Duck: duck}
})),
&pond)
```

Now `depinject` has enough information to provide `Mallard` as an input to `APond`.

### Full example in real app

```go
Expand Down