From 467d72be0cc41ea2ae7cb18a079c34157945f502 Mon Sep 17 00:00:00 2001 From: Matt Kocubinski Date: Tue, 19 Jul 2022 09:00:15 -0500 Subject: [PATCH] docs(depinject): clarify `depinject` API (#12214) --- depinject/README.md | 90 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/depinject/README.md b/depinject/README.md index 0aa0cbd317..3d65fffe81 100644 --- a/depinject/README.md +++ b/depinject/README.md @@ -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 @@ -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