You can group previous options into single variable by using
di.Options()
:
// account module
account := di.Options(
di.Provide(NewAccountController),
di.Provide(NewAccountRepository),
)
// auth module
auth := di.Options(
di.Provide(NewAuthController),
di.Provide(NewAuthRepository),
)
// build container
container, err := di.New(
account,
auth,
)
if err != nil {
// handle error
}
If you have more than one instances of same type, you can specify alias. For example two instances of database: leader - for writing, follower - for reading.
// Leader provides write database access.
type Leader struct {
*Database
}
// Follower provides read database access.
type Follower struct {
*Database
}
// provide leader database
di.Provide(NewLeader, di.WithName("leader"))
// provide follower database
di.Provide(NewFollower, di.WithName("follower"))
If you need to resolve it from the container use di.Name()
resolve
option.
var db *Database
container.Resolve(&db, di.Name("leader"))
If you need to provide named definition in another constructor embed
di.Inject
.
// Parameters
type Parameters struct {
di.Inject
// use `di` tag for the container to know that field need to be injected.
Leader *Database `di:"leader"`
Follower *Database `di:"follower"`
}
// NewService creates new service with provided parameters.
func NewService(parameters Parameters) *Service {
return &Service{
Leader: parameters.Leader,
Follower: parameters.Leader,
}
}
Also, di.Inject
with tag optional
provide ability to skip dependency
if it not exists in the container.
// ServiceParameter
type ServiceParameter struct {
di.Inject
Logger *Logger `di:"" optional:"true"`
}
Constructors that declare dependencies as optional must handle the case of those dependencies being absent.
You can use naming and optional together.
// ServiceParameter
type ServiceParameter struct {
di.Inject
StdOutLogger *Logger `di:"stdout"`
FileLogger *Logger `di:"file" optional:"true"`
}
To avoid constant constructor changes, you can use di.Inject
. Only
struct pointers are supported as constructing result. And only
di
-taged fields will be injected. Such a constructor will work with
using di
only.
// Controller has some endpoints.
type Controller struct {
di.Inject // enables struct field injection
// fields must be public and have tag di
// tag lets to specify fields need to be injected
Users UserService `di:""`
Friends FriendsService `di:""`
}
// NewController creates controller.
func NewController() *Controller {
return &Controller{}
}
d
Use di.Prototype()
option to create new instance for each resolve.
di.Provide(NewRequestContext, di.Prototype())
If the constructor creates a value that needs to be cleaned up, then it can return a closure to clean up the resource.
func NewFile(log Logger, path Path) (*os.File, func(), error) {
f, err := os.Open(string(path))
if err != nil {
return nil, nil, err
}
cleanup := func() {
if err := f.Close(); err != nil {
log.Log(err)
}
}
return f, cleanup, nil
}
After container.Cleanup()
call, it iterates over instances and calls
cleanup function if it exists.
container, err := di.New(
// ...
di.Provide(NewFile),
)
if err != nil {
// handle error
}
// do something
container.Cleanup() // file was closed