wire: clean up README (google/go-cloud#100)
Summary of changes: - Rewrote introduction (fixes google/go-cloud#81). - Simplified `go generate` explanation by automatically adding the comment line to the output. - Removed mentions of Dagger. Wire stands enough on its own now, but Dagger's influence lives on in our minds. - Moved best practices to bottom and removed provider set grouping guidance.
This commit is contained in:
128
README.md
128
README.md
@@ -1,14 +1,17 @@
|
||||
# Wire: Compile-Time Dependency Injection for Go
|
||||
# Wire: Automated Initialization in Go
|
||||
|
||||
Wire is a static [dependency injection][] framework for Go, inspired by
|
||||
[Dagger][]. It works by using Go code to specify dependencies, then
|
||||
generating code to create those structures, mimicking the code that a user
|
||||
might have hand-written.
|
||||
Wire is a code generation tool that automates connecting components using
|
||||
[dependency injection][]. Dependencies between components are represented in
|
||||
Wire as function parameters, encouraging explicit initialization instead of
|
||||
global variables. Because Wire operates without runtime state or reflection,
|
||||
code written to be used with Wire is useful even for hand-written
|
||||
initialization.
|
||||
|
||||
[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection
|
||||
[Dagger]: https://google.github.io/dagger/
|
||||
|
||||
## Usage Guide
|
||||
## Basics
|
||||
|
||||
Wire has two core concepts: providers and injectors.
|
||||
|
||||
### Defining Providers
|
||||
|
||||
@@ -28,6 +31,9 @@ func ProvideFoo() Foo {
|
||||
}
|
||||
```
|
||||
|
||||
Provider functions must be exported in order to be used from other packages,
|
||||
just like ordinary functions.
|
||||
|
||||
Providers can specify dependencies with parameters:
|
||||
|
||||
```go
|
||||
@@ -70,8 +76,9 @@ func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
|
||||
}
|
||||
```
|
||||
|
||||
Providers can be grouped in **provider sets**. To add these providers to a new
|
||||
set called `SuperSet`, use the `wire.NewSet` function:
|
||||
Providers can be grouped into **provider sets**. This is useful if several
|
||||
providers will frequently be used together. To add these providers to a new set
|
||||
called `SuperSet`, use the `wire.NewSet` function:
|
||||
|
||||
```go
|
||||
package foobarbaz
|
||||
@@ -116,8 +123,7 @@ say that the above providers were defined in a package called
|
||||
|
||||
```go
|
||||
// +build wireinject
|
||||
|
||||
// ^ build tag makes sure the stub is not built in the final build
|
||||
// The build tag makes sure the stub is not built in the final build.
|
||||
|
||||
package main
|
||||
|
||||
@@ -144,26 +150,17 @@ the generated file.
|
||||
|
||||
You can generate the injector by invoking `gowire` in the package directory:
|
||||
|
||||
```
|
||||
```shell
|
||||
gowire
|
||||
```
|
||||
|
||||
Or you can add the line `//go:generate gowire` to another file in your package to
|
||||
use [`go generate`]:
|
||||
|
||||
```
|
||||
go generate
|
||||
```
|
||||
|
||||
(Adding the line to the injection declaration file will be silently ignored by
|
||||
`go generate`.)
|
||||
|
||||
Wire will produce an implementation of the injector in a file called
|
||||
`wire_gen.go` that looks something like this:
|
||||
|
||||
```go
|
||||
// Code generated by gowire. DO NOT EDIT.
|
||||
|
||||
//go:generate gowire
|
||||
//+build !wireinject
|
||||
|
||||
package main
|
||||
@@ -187,55 +184,23 @@ As you can see, the output is very close to what a developer would write
|
||||
themselves. Further, there is little dependency on Wire at runtime: all of the
|
||||
written code is just normal Go code, and can be used without Wire.
|
||||
|
||||
Once `wire_gen.go` is created, you can regenerate it by running [`go generate`].
|
||||
|
||||
[`go generate`]: https://blog.golang.org/generate
|
||||
|
||||
## Best Practices
|
||||
|
||||
Wire is still not mature yet, but guidance that applies to Dagger generally
|
||||
applies to Wire as well. In particular, when thinking about how to group
|
||||
providers into sets, follow the same [guidance](https://google.github.io/dagger/testing.html#organize-modules-for-testability)
|
||||
as Dagger (provider sets are called modules in Dagger/Guice):
|
||||
|
||||
> Some [...] bindings will have reasonable alternatives, especially for
|
||||
> testing, and others will not. For example, there are likely to be
|
||||
> alternative bindings for a type like `AuthManager`: one for testing, others
|
||||
> for different authentication/authorization protocols.
|
||||
>
|
||||
> But on the other hand, if the `AuthManager` interface has a method that
|
||||
> returns the currently logged-in user, you might want to [export a provider of
|
||||
> `User` that simply calls `CurrentUser()`] on the `AuthManager`. That
|
||||
> published binding is unlikely to ever need an alternative.
|
||||
>
|
||||
> Once you’ve classified your bindings into [...] bindings with reasonable
|
||||
> alternatives [and] bindings without reasonable alternatives, consider
|
||||
> arranging them into provider sets like this:
|
||||
>
|
||||
> - One [provider set] for each [...] binding with a reasonable alternative.
|
||||
> (If you are also writing the alternatives, each one gets its own [provider
|
||||
> set].) That [provider set] contains exactly one provider.
|
||||
> - All [...] bindings with no reasonable alternatives go into [provider sets]
|
||||
> organized along functional lines.
|
||||
> - The [provider sets] should each include the no-reasonable-alternative
|
||||
> [provider sets] that require the [...] bindings each provides.
|
||||
|
||||
One Wire-specific practice though: create one-off types where in Java you
|
||||
would use a binding annotation. For example, if you need to pass a string
|
||||
through the dependency graph, you would create a wrapping type:
|
||||
|
||||
```go
|
||||
type MySQLConnectionString string
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
The following features all build on top of the concepts of providers and
|
||||
injectors.
|
||||
|
||||
### Binding Interfaces
|
||||
|
||||
Frequently, dependency injection is used to bind concrete implementations for an
|
||||
interface. Wire matches inputs to outputs via [type identity][], so the
|
||||
Frequently, dependency injection is used to bind a concrete implementation for
|
||||
an interface. Wire matches inputs to outputs via [type identity][], so the
|
||||
inclination might be to create a provider that returns an interface type.
|
||||
However, this would not be idiomatic, since the Go best practice is to [return
|
||||
concrete types][]. Instead, you can declare an interface binding in a
|
||||
provider set:
|
||||
concrete types][]. Instead, you can declare an interface binding in a provider
|
||||
set:
|
||||
|
||||
```go
|
||||
type Fooer interface {
|
||||
@@ -348,7 +313,8 @@ an error if the expression calls any functions.
|
||||
If a provider creates a value that needs to be cleaned up (e.g. closing a file),
|
||||
then it can return a closure to clean up the resource. The injector will use
|
||||
this to either return an aggregated cleanup function to the caller or to clean
|
||||
up the resource if a later provider returns an error.
|
||||
up the resource if a provider called later in the injector's implementation
|
||||
returns an error.
|
||||
|
||||
```go
|
||||
func provideFile(log Logger, path Path) (*os.File, func(), error) {
|
||||
@@ -379,3 +345,37 @@ func injectFoo() Foo {
|
||||
panic(wire.Build(/* ... */))
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
The following are practices we recommend for using Wire. This list will grow
|
||||
over time.
|
||||
|
||||
### Distinguishing Types
|
||||
|
||||
If you need to inject a common type like `string`, create a new string type
|
||||
to avoid conflicts with other providers. For example:
|
||||
|
||||
```go
|
||||
type MySQLConnectionString string
|
||||
```
|
||||
|
||||
### Options Structs
|
||||
|
||||
A provider function that includes many dependencies can pair the function with
|
||||
an options struct.
|
||||
|
||||
```go
|
||||
type Options struct {
|
||||
// Messages is the set of recommended greetings.
|
||||
Messages []Message
|
||||
// Writer is the location to send greetings. nil goes to stdout.
|
||||
Writer io.Writer
|
||||
}
|
||||
|
||||
func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
|
||||
// ...
|
||||
}
|
||||
|
||||
var GreeterSet = wire.NewSet(Options{}, NewGreeter)
|
||||
```
|
||||
|
||||
@@ -168,7 +168,10 @@ func (g *gen) frame() []byte {
|
||||
return nil
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("// Code generated by gowire. DO NOT EDIT.\n\n//+build !wireinject\n\npackage ")
|
||||
buf.WriteString("// Code generated by gowire. DO NOT EDIT.\n\n")
|
||||
buf.WriteString("//go:generate gowire\n")
|
||||
buf.WriteString("//+build !wireinject\n\n")
|
||||
buf.WriteString("package ")
|
||||
buf.WriteString(g.prog.Package(g.currPackage).Pkg.Name())
|
||||
buf.WriteString("\n\n")
|
||||
if len(g.imports) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user