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

wire: share dependency graph across injectors in the same package? #21

Closed
drewolson opened this issue Jul 24, 2018 · 6 comments
Closed
Labels
enhancement New feature or request
Milestone

Comments

@drewolson
Copy link

I was testing out wire and created a package with two injectors[1]. I noticed that each injector individually recreates the dependency graph. Other DI containers I've used (dagger, for example) allow sharing of singleton objects between different "entry points" to the graph.

Is this behavior intentional or is the goal to eventually allow "reusing" a graph across many injectors within a package?

[1] - https://github.com/drewolson/wiretest/blob/f34107974e871e45ac0f073c7878ab2d6e93b7c4/container/wire_gen.go

@drewolson
Copy link
Author

FWIW, in the meantime I'm using a technique similar to your Options example to inject a struct with all the values I need. The disadvantage of this is that each value is eagerly injected regardless of when it is needed, but it certainly works for now.

Thanks!

@drewolson
Copy link
Author

For reference, here is a minimal example that shows the behavior I'm describing in a dagger 2 project:

https://github.com/drewolson/dagger_maven_example/tree/singleton_across_providers

This is the test that asserts the behavior.

@zombiezen
Copy link
Collaborator

zombiezen commented Jul 25, 2018

Wire intentionally does not have a notion of subcomponents at the moment. In talking with the Dagger team, we discovered that subcomponents and scopes introduce a fair amount of complexity.

As you say, you can get much the same behavior by returning the singletons from the first injector that creates them and then pass them to later injectors. This has the benefit of making the dataflow explicit, which for the examples we came up with, seemed like a net win. That said, we're very curious to see how folks will use Wire in real-world applications: if this does not scale, we might have to revisit.

EDIT: I realized after looking more closely at your sample that the component itself is stateful (a detail I had forgotten about Dagger). My explanation above is still largely applicable: we want state to be explicit.

@drewolson
Copy link
Author

EDIT: I realized after looking more closely at your sample that the component itself is stateful (a detail I had forgotten about Dagger).

Yes, this is where I was going with this conversation. Perhaps, in the future, there could be some kind of alternative injectors that are attached to a struct where state is tracked. I definitely agree that starting out simple and waiting for real-world experience reports is the right approach.

Thanks again for this great library, I look forward to using it in my projects.

@zombiezen zombiezen transferred this issue from google/go-cloud Nov 28, 2018
@zombiezen zombiezen added this to the Unplanned milestone Nov 28, 2018
@zombiezen zombiezen added the enhancement New feature or request label Nov 28, 2018
@takanuva15
Copy link

takanuva15 commented Oct 26, 2023

@drewolson I found this issue 5 years later while looking up the same thing - how to share dependencies across injectors (eg reusing one DB connection-pool among multiple DAOs). Unfortunately, the links you posted are dead now and I'm not sure what you're referring to when you mention the "Options example".

Could you please share some updated links (or better, sample code) to explain how you managed to share dependencies between injectors? Also, what is the "Options example" you were referring to?

@takanuva15
Copy link

takanuva15 commented Oct 26, 2023

After some doing some more research, I found issue #260 where they basically resolve this issue by declaring an App super-container struct that has all the top-level dependencies for the application. Then there's only a single injector declared in the wire.go file which will naturally instantiate all dependencies once and then reuse them.

Sample code:
wire.go

type App struct {
	*services.AuthService
	*services.FooService
}

var daoSet = wire.NewSet(
	dao.NewAuthDAO,
	dao.NewFooDAO,
)

var svcSet = wire.NewSet(
	services.NewAuthService,
	services.NewFooService,
)

func InitializeApp() *App {
	wire.Build(
		dao.InitDB,
		daoSet,
		svcSet,
		wire.Struct(new(App), "*"),
	)
	return nil
}

Then after generating the wire_gen.go file by invoking wire in the terminal, we can invoke InitializeApp() to get the dependencies we need.

For the Options stuff, I found this doc page about using an Options struct which is what I assume you were referring to: https://github.com/google/wire/blob/main/docs/best-practices.md#options-structs

// 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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants