docs/best-practices.md
The following are practices we recommend for using Wire. This list will grow over time.
If you need to inject a common type like string, create a new string type to
avoid conflicts with other providers. For example:
type MySQLConnectionString string
A provider function that includes many dependencies can pair the function with an options struct.
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(wire.Struct(new(Options), "*"), NewGreeter)
When creating a provider set for use in a library, the only changes you can make without breaking compatibility are:
All other changes are not safe. This includes:
Instead of making one of these breaking changes, consider adding a new provider set.
As an example, if you have a provider set like this:
var GreeterSet = wire.NewSet(NewStdoutGreeter)
func DefaultGreeter(ctx context.Context) *Greeter {
// ...
}
func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
// ...
}
func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
// ...
}
You may:
DefaultGreeter instead of NewStdoutGreeter in GreeterSet.T and add a provider for T to GreeterSet, as long as
T is introduced in the same commit/release as the provider is added.You may not:
NewGreeter instead of NewStdoutGreeter in GreeterSet. This both
adds an input type (io.Writer) and requires injectors to return an error
where the provider of *Greeter did not require this before.NewStdoutGreeter from GreeterSet. Injectors depending on
*Greeter will be broken.io.Writer to GreeterSet. Injectors might already have
a provider for io.Writer which might conflict with this one.As such, you should pick the output types in a library provider set carefully.
In general, prefer small provider sets in a library. For example, it is common
for a library provider set to contain a single provider function along with a
wire.Bind to the interface the return type implements. Avoiding larger
provider sets reduces the likelihood that applications will encounter conflicts.
To illustrate, imagine your library provides a client for a web service. While
it may be tempting to bundle a provider for *http.Client in a provider set for
your library's client, doing so would cause conflicts if every library did the
same. Instead, the library's provider set should only include the provider for
the API client, and let *http.Client be an input of the provider set.
There are two approaches for creating an injected app with mocked dependencies. Examples of both approaches are shown here.
Create a test-only injector that takes all of the mocks as arguments; the
argument types must be the interface types the mocks are mocking. wire.Build
can't include providers for the mocked dependencies without creating conflicts,
so if you're using provider set(s) you will need to define one that doesn't
include the mocked types.
Create a new struct that includes the app plus all of the dependencies you want
to mock. Create a test-only injector that returns this struct, give it providers
for the concrete mock types, and use wire.Bind to tell Wire that the concrete
mock types should be used to fulfill the appropriate interface.