Questions about Dependency Injection: at runtime via Guice, versus Scala at compile time?

451 views
Skip to first unread message

Kaj Magnus Lindberg

unread,
Jul 18, 2017, 6:35:40 AM7/18/17
to Play Framework
Hi,

I want to upgrade to Play 2.6, and have some questions about dependency injection (DI). I'm new to DI.

Are there any benefits with using Guice DI, instead of Scala compile time DI?
Are things one can do, with Guice, that cannot be done using the compile-time approach?

(With Scala compile time DI, I suppose I mean this: https://github.com/playframework/play-scala-compile-di-example.
I'm fine with passing "many" constructor arguments and wiring things myself; I don't consider not-having-to-write such code a major benefit.)

What about performance: Are there any differences between the compile-time and Guice-runtime approaches?
For app server startup time?
And later, when the app has been running for a while already — are the compile-time and Guice-runtime approaches then equally fast?

Which alternative (Guice-runtime-DI or compile-time-DI) do you think offers the lowest risk that Play Framework will deprecate it and replace it with something else, in the future? (forcing me to relearn and rewrite/update my Play related stuff)

... I'm thinking that Guice might be a bit higher risk? Because it's been broken out from core recently. And, there are alternatives to Guice, like Spring, MacWire, ... and Dagger 2 — Dagger 2 seems to be more popular than Guice nowadays (newer, but still has a lot more GitHub stars). So perhaps in the future, the default recommended DI will change from Guice to something else? (since there's other more popular stuff out there already)

... But there are no "competing" implementations of compile-time DI? So perhaps the compile-time method is more "stable", then?

What about Scala Native, https://github.com/scala-native/scala-native — I suppose Scala Native won't work with Guice and runtime-DI? But compile-time-DI works fine with Scala Native, right?

Thanks & regards,
KajMagnus

Justin du coeur

unread,
Jul 18, 2017, 10:06:15 AM7/18/17
to play-fr...@googlegroups.com
A few personal opinions.  I know Guice moderately well, but my own Play application (which long predates the introduction of Guice to Play) uses its own homebrew DI solution, named Ecology.

(More precisely, it eschews DI per se in favor of a more old-fashioned runtime-dependencies model that uses a more explicit "god object".  I've been using essentially this same approach in five different languages over the past 18 years: it's not fancy, but works consistently and well.  The point is, there are *many* options for managing your dependencies in Play.)

On Tue, Jul 18, 2017 at 6:35 AM, Kaj Magnus Lindberg <kajma...@gmail.com> wrote:
I want to upgrade to Play 2.6, and have some questions about dependency injection (DI). I'm new to DI.

Are there any benefits with using Guice DI, instead of Scala compile time DI?
Are things one can do, with Guice, that cannot be done using the compile-time approach?

Probably not?  They're both approaches for decomposing your application into decoupled pieces, and wiring those pieces together so they can access each other.

The only thing that's a bit easier with runtime dependency, in my experience, is managing initialization order.  I don't know offhand how Play's example compile-time DI deals with mutual dependencies, which tends to get many naive DI solutions into trouble as they try to scale up.  (Guice handles this with the "Provider" mechanism.)  Whether this is an issue or not depends on how you code -- in principle, it's a non-issue for truly functional code, but I've never managed to get a *complex* application to production without initialization order becoming an issue somewhere along the line.

(With Scala compile time DI, I suppose I mean this: https://github.com/playframework/play-scala-compile-di-example.
I'm fine with passing "many" constructor arguments and wiring things myself; I don't consider not-having-to-write such code a major benefit.)

That's fair, and a reasonable decision.  Just keep in mind that dependencies tend to grow -- at this point, my server has ~70 components (just the ones *I* have added, separate from the ones built into Play), with each one depending on an average of ~10 others, and the number is still rising fairly rapidly.  You should think about the overall complexity of your problem, and how many inter-related subsystems you are likely to need in the long run.
 
What about performance: Are there any differences between the compile-time and Guice-runtime approaches?
For app server startup time?
And later, when the app has been running for a while already — are the compile-time and Guice-runtime approaches then equally fast?

I suspect you'd have to benchmark to get any real answers here, but my suspicion is that any difference between the two is basically noise.
 
Which alternative (Guice-runtime-DI or compile-time-DI) do you think offers the lowest risk that Play Framework will deprecate it and replace it with something else, in the future? (forcing me to relearn and rewrite/update my Play related stuff)

... I'm thinking that Guice might be a bit higher risk? Because it's been broken out from core recently. And, there are alternatives to Guice, like Spring, MacWire, ... and Dagger 2 — Dagger 2 seems to be more popular than Guice nowadays (newer, but still has a lot more GitHub stars). So perhaps in the future, the default recommended DI will change from Guice to something else? (since there's other more popular stuff out there already)

... But there are no "competing" implementations of compile-time DI? So perhaps the compile-time method is more "stable", then?

No -- I'd say almost the opposite, frankly.  There are many runtime-DI systems because it's an old and well-worn concept that has had lots of time to mature, much of that in the Java ecosystem.

By contrast, I've been seeing a lot of discussion in the FP communities about how best to deal with dependency management, and I haven't gotten the sense that there is anything resembling a best-practice consensus yet.  (Although personally, I think the Eff approach sounds quite promising -- it's the first statically-typed approach that I've seriously considered adopting, that appears to deal with the real-world complexities well.)

I have no insight into what the Play team might do about changing their default recommendations in the future.  But Guice is by now relatively old, complete, mature and stable, and is what I usually use when coding for clients, on the grounds that it appears to be the safest option going forward.  Changing the default would probably be an considerable hassle for the Play team, so I doubt they would do so casually.  In my experience, the team is very conscious of compatibility breakages, and does so relatively slowly and carefully.

I suspect that compile-time DI will prove to be the *best* option in the long run, but I think folks are still figuring out the best answers there.  There are many different ways you could construct your application with statically-typed components; the Play docs are just giving one straightforward option as an example.  Having not used Play's current recommended approach, I can't speak to it with any confidence; I am curious how well it works when you get to large numbers of inter-dependent application components.

What about Scala Native, https://github.com/scala-native/scala-native — I suppose Scala Native won't work with Guice and runtime-DI? But compile-time-DI works fine with Scala Native, right?

Almost certainly correct, at least that Guice per se (or anything else dependent on reflection) won't work.  Some forms of runtime-dependency-management would work just fine, so long as they aren't reflection-based -- for example, my above-linked Ecology system is already in heavy use on both the JVM and JS platforms, and will probably work fine on Native.

But I don't know if Scala Native is ever likely to run Play, so I'm not sure how relevant that is...

Greg Methvin

unread,
Jul 18, 2017, 7:32:20 PM7/18/17
to play-framework
On Tue, Jul 18, 2017 at 3:35 AM, Kaj Magnus Lindberg <kajma...@gmail.com> wrote:
Hi,

I want to upgrade to Play 2.6, and have some questions about dependency injection (DI). I'm new to DI.

Are there any benefits with using Guice DI, instead of Scala compile time DI?
Are things one can do, with Guice, that cannot be done using the compile-time approach?

Guice does offer a bit more flexibility and is a bit "smarter" than simply using the compiler. For example, Guice will check your object graph to make sure there are no cycles; in a typical compile-time approach, such a circular dependency will most likely result in a StackOverflowError at runtime. Guice also has "modes" which allows you to eagerly instantiate all your singletons in production mode, while doing so lazily in dev mode; that's hard to do with plain Scala code. Guice also has some other nice features like automatically generating code for factories (see https://github.com/google/guice/wiki/AssistedInject).

Another "nice" thing is that because the bindings are defined at runtime, you can programmatically define them. This is how Play is able to have settings like play.http.requestHandler = foo, and set the implementation at runtime.

In my experience, these things are not all that important for a small to medium sized application, but I've found the Guice features helpful with more complex situations.
 

(With Scala compile time DI, I suppose I mean this: https://github.com/playframework/play-scala-compile-di-example.
I'm fine with passing "many" constructor arguments and wiring things myself; I don't consider not-having-to-write such code a major benefit.)

You can also use Macwire, which just writes that code for you: https://github.com/playframework/play-scala-macwire-di-example 

What about performance: Are there any differences between the compile-time and Guice-runtime approaches?
For app server startup time?
And later, when the app has been running for a while already — are the compile-time and Guice-runtime approaches then equally fast?

Technically Guice is going to be a bit slower because it's doing runtime reflection to instantiate classes. But in practice that has relatively small overhead compared to the rest of the work your application is doing. I have never run into a case where Guice was the bottleneck.
 

Which alternative (Guice-runtime-DI or compile-time-DI) do you think offers the lowest risk that Play Framework will deprecate it and replace it with something else, in the future? (forcing me to relearn and rewrite/update my Play related stuff) 

... I'm thinking that Guice might be a bit higher risk? Because it's been broken out from core recently. And, there are alternatives to Guice, like Spring, MacWire, ... and Dagger 2 — Dagger 2 seems to be more popular than Guice nowadays (newer, but still has a lot more GitHub stars). So perhaps in the future, the default recommended DI will change from Guice to something else? (since there's other more popular stuff out there already)

What exactly are you worried about? If you're concerned about Play removing the play-guice module, I don't think that is likely. Even if it were removed, you'd still be able to wire the components together manually. The Guice module is just wiring together components provided by Play. The reason there is no core dependency is there doesn't need to be. There's no reason for other DI users to have the extra dependency if they don't need it.

Dagger 2 is meant for Java, and Play also supports that: https://github.com/playframework/play-java-dagger2-example
 

... But there are no "competing" implementations of compile-time DI? So perhaps the compile-time method is more "stable", then?

There are competing strategies for compile-time DI as well. There's the cake pattern, the thin cake pattern (what Play promotes), Scala implicits, the Reader monad, etc. All these things are possible to integrate with Play's approach. I don't think it's fair to say that compile-time DI in Scala is settled.

Looking at it in terms of how you design your components, the approaches that promote using constructor injection are probably the most "stable" and the most interchangeable. It should be relatively trivial to migrate a Guice app using constructor injection to the thin cake pattern or vice versa, since your components should look exactly the same. Your integration tests (anything using GuiceApplicationBuilder or the injector) would probably need to change though.
 

What about Scala Native, https://github.com/scala-native/scala-native — I suppose Scala Native won't work with Guice and runtime-DI? But compile-time-DI works fine with Scala Native, right?

I would assume so, but I doubt Play will support Scala Native anytime soon. It's still a relatively experimental project.
 

Thanks & regards,
KajMagnus

--
You received this message because you are subscribed to the Google Groups "Play Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/e40c8021-207a-4585-b28c-48265f5fc011%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Tech Lead - Play Framework

William Narmontas

unread,
Jul 18, 2017, 9:53:42 PM7/18/17
to Play Framework
> instantiate all your singletons in production mode, while doing so lazily in dev mode; that's hard to do with plain Scala code

Really? I'm getting ready to migrate to compile-time DI and thought this would not be particularly hard to do. I am using quite a few singletons there.

So for the time being, should I stick to runtime or compile-time DI?

I'm finding that in my app [https://github.com/ScalaWilliam/ActionFPS] runtime DI is a bit fragile, though it could be just me misusing it.

Thanks & regards,
KajMagnus

To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

Greg Methvin

unread,
Jul 18, 2017, 11:22:21 PM7/18/17
to play-framework
On Tue, Jul 18, 2017 at 6:53 PM, William Narmontas <con...@vynar.com> wrote:
> instantiate all your singletons in production mode, while doing so lazily in dev mode; that's hard to do with plain Scala code

Really? I'm getting ready to migrate to compile-time DI and thought this would not be particularly hard to do. I am using quite a few singletons there.

Hard to do is an overstatement, but there's not an obvious straightforward way to do it without writing custom code.

Assuming you're using the thin cake pattern Play advocates, there are basically two ways of handling eager singletons. You can use "val" instead of the traditional "lazy val" to instantiate eagerly, but have to make sure you don't accidentally forward-reference that val. Or you can simply make everything a lazy val, and access the lazy val when you start your application.

It would be pretty simple to write your own mechanism to execute code when the app starts, similar to Play's ApplicationLifecycle. Then you could selectively choose to eagerly instantiate those singletons depending on the app's mode or other configuration.

Like most of the situations where Guice has some extra feature, it's usually not a big deal unless your application is rather complex.


So for the time being, should I stick to runtime or compile-time DI?

I think compile-time DI is fine for most situations, especially if you use a library like Macwire to eliminate boilerplate. I don't think it actually matters that much. It's more a matter of preference and whether you're actually interested in taking advantage of specific features Guice has.
 

I'm finding that in my app [https://github.com/ScalaWilliam/ActionFPS] runtime DI is a bit fragile, though it could be just me misusing it.

What did you find makes it fragile? What are you hoping to get out of compile-time DI?

I did notice you have some logic in your module code, which is generally not a good idea. For example: https://github.com/ScalaWilliam/ActionFPS/blob/master/web/app/modules/GamesProviderDeciderModule.scala#L29. As a general rule, your modules should only contain bindings. And I think you should be using the Guice API directly instead of the play.api.inject one. The Play APIs are intended to be lowest common denominator APIs, for Play modules that want to support multiple DI frameworks.
 
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/181bac95-56c5-483e-882b-ef205c3fe956%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Kaj Magnus Lindberg

unread,
Aug 23, 2017, 2:08:52 AM8/23/17
to Play Framework
Hi Justin! Thanks for your reply & all info. Replies inline below.


On Tuesday, July 18, 2017 at 4:06:15 PM UTC+2, Justin du coeur wrote:
A few personal opinions.  I know Guice moderately well, but my own Play application (which long predates the introduction of Guice to Play) uses its own homebrew DI solution, named Ecology.

(More precisely, it eschews DI per se in favor of a more old-fashioned runtime-dependencies model that uses a more explicit "god object".  I've been using essentially this same approach in five different languages over the past 18 years: it's not fancy, but works consistently and well.  The point is, there are *many* options for managing your dependencies in Play.)

I had a look at Ecology. I'm not good enough at Scala to understand the implementation :- P
Interesting to know that one can write one's own DI lib though and that it's not that super much code :- )

 
On Tue, Jul 18, 2017 at 6:35 AM, Kaj Magnus Lindberg <kajma...@gmail.com> wrote:
I want to upgrade to Play 2.6, and have some questions about dependency injection (DI). I'm new to DI.

Are there any benefits with using Guice DI, instead of Scala compile time DI?
Are things one can do, with Guice, that cannot be done using the compile-time approach?

Probably not?  They're both approaches for decomposing your application into decoupled pieces, and wiring those pieces together so they can access each other.

The only thing that's a bit easier with runtime dependency, in my experience, is managing initialization order.  I don't know offhand how Play's example compile-time DI deals with mutual dependencies, which tends to get many naive DI solutions into trouble as they try to scale up.  (Guice handles this with the "Provider" mechanism.)  Whether this is an issue or not depends on how you code -- in principle, it's a non-issue for truly functional code, but I've never managed to get a *complex* application to production without initialization order becoming an issue somewhere along the line.

Ok yes I've run into some bugs related to init order, in my app, already, and have had to spend some time debugging. (Mostly forward-referencing non-lazy vals that were still 'null'.)


(With Scala compile time DI, I suppose I mean this: https://github.com/playframework/play-scala-compile-di-example.
I'm fine with passing "many" constructor arguments and wiring things myself; I don't consider not-having-to-write such code a major benefit.)

That's fair, and a reasonable decision.  Just keep in mind that dependencies tend to grow -- at this point, my server has ~70 components (just the ones *I* have added, separate from the ones built into Play), with each one depending on an average of ~10 others, and the number is still rising fairly rapidly.  You should think about the overall complexity of your problem, and how many inter-related subsystems you are likely to need in the long run.


70 components is a lot more than what I have :-)  Don't think I'll ever have more than maybe 10 or 20 that needs some special initiaization and depend on each other.

 
What about performance: Are there any differences between the compile-time and Guice-runtime approaches?
For app server startup time?
And later, when the app has been running for a while already — are the compile-time and Guice-runtime approaches then equally fast?

I suspect you'd have to benchmark to get any real answers here, but my suspicion is that any difference between the two is basically noise.
 
Which alternative (Guice-runtime-DI or compile-time-DI) do you think offers the lowest risk that Play Framework will deprecate it and replace it with something else, in the future? (forcing me to relearn and rewrite/update my Play related stuff)

... I'm thinking that Guice might be a bit higher risk? Because it's been broken out from core recently. And, there are alternatives to Guice, like Spring, MacWire, ... and Dagger 2 — Dagger 2 seems to be more popular than Guice nowadays (newer, but still has a lot more GitHub stars). So perhaps in the future, the default recommended DI will change from Guice to something else? (since there's other more popular stuff out there already)

... But there are no "competing" implementations of compile-time DI? So perhaps the compile-time method is more "stable", then?

No -- I'd say almost the opposite, frankly.  There are many runtime-DI systems because it's an old and well-worn concept that has had lots of time to mature, much of that in the Java ecosystem.

By contrast, I've been seeing a lot of discussion in the FP communities about how best to deal with dependency management, and I haven't gotten the sense that there is anything resembling a best-practice consensus yet.  (Although personally, I think the Eff approach sounds quite promising -- it's the first statically-typed approach that I've seriously considered adopting, that appears to deal with the real-world complexities well.)

I have no insight into what the Play team might do about changing their default recommendations in the future.  But Guice is by now relatively old, complete, mature and stable, and is what I usually use when coding for clients, on the grounds that it appears to be the safest option going forward.  Changing the default would probably be an considerable hassle for the Play team, so I doubt they would do so casually.  In my experience, the team is very conscious of compatibility breakages, and does so relatively slowly and carefully.

I suspect that compile-time DI will prove to be the *best* option in the long run, but I think folks are still figuring out the best answers there.  There are many different ways you could construct your application with statically-typed components; the Play docs are just giving one straightforward option as an example.  Having not used Play's current recommended approach, I can't speak to it with any confidence; I am curious how well it works when you get to large numbers of inter-dependent application components.

Ok :- )

I'll probably use constructos, and pass lots of objs and things to the constructors. Even if it's a bit verbose, at least I won't have to learn anything new, which I would forget after a year, and have to re-learn again and again :- P

 
What about Scala Native, https://github.com/scala-native/scala-native — I suppose Scala Native won't work with Guice and runtime-DI? But compile-time-DI works fine with Scala Native, right?

Almost certainly correct, at least that Guice per se (or anything else dependent on reflection) won't work.  Some forms of runtime-dependency-management would work just fine, so long as they aren't reflection-based -- for example, my above-linked Ecology system is already in heavy use on both the JVM and JS platforms, and will probably work fine on Native.

But I don't know if Scala Native is ever likely to run Play, so I'm not sure how relevant that is...

Ok. I was thinking / hoping that maybe maybe after 10 years or so.

Thanks & regards,
KajMagnus
 

Kaj Magnus Lindberg

unread,
Aug 23, 2017, 2:13:57 AM8/23/17
to Play Framework
Hi Greg!

Thanks for explaining how Guice is smarter, e.g. checking for cycles, ... and mentioning that all this doesn't matter so very much.  I think that personally I like compile time DI better, so I'll probably go with that.

More replies inline below ...


On Wednesday, July 19, 2017 at 1:32:20 AM UTC+2, Greg Methvin wrote:


On Tue, Jul 18, 2017 at 3:35 AM, Kaj Magnus Lindberg <kajma...@gmail.com> wrote
[...] 
Which alternative (Guice-runtime-DI or compile-time-DI) do you think offers the lowest risk that Play Framework will deprecate it and replace it with something else, in the future? (forcing me to relearn and rewrite/update my Play related stuff) 

... I'm thinking that Guice might be a bit higher risk? Because it's been broken out from core recently. And, there are alternatives to Guice, like Spring, MacWire, ... and Dagger 2 — Dagger 2 seems to be more popular than Guice nowadays (newer, but still has a lot more GitHub stars). So perhaps in the future, the default recommended DI will change from Guice to something else? (since there's other more popular stuff out there already)

What exactly are you worried about? If you're concerned about Play removing the play-guice module, I don't think that is likely. Even if it were removed, you'd still be able to wire the components together manually. The Guice module is just wiring together components provided by Play. The reason there is no core dependency is there doesn't need to be. There's no reason for other DI users to have the extra dependency if they don't need it.

Dagger 2 is meant for Java, and Play also supports that: https://github.com/playframework/play-java-dagger2-example


I was worried that Play would eventually stop supporting Guice, and recommend another DI-framework instead, and that it wouldn't be backwards compatible, so that I would have had to rewrite lots of stuff.

Ok but now I understand that it's not-well-spent-time to worry about that. :- )


... But there are no "competing" implementations of compile-time DI? So perhaps the compile-time method is more "stable", then?

There are competing strategies for compile-time DI as well. There's the cake pattern, the thin cake pattern (what Play promotes), Scala implicits, the Reader monad, etc. All these things are possible to integrate with Play's approach. I don't think it's fair to say that compile-time DI in Scala is settled.

Looking at it in terms of how you design your components, the approaches that promote using constructor injection are probably the most "stable" and the most interchangeable. It should be relatively trivial to migrate a Guice app using constructor injection to the thin cake pattern or vice versa, since your components should look exactly the same. Your integration tests (anything using GuiceApplicationBuilder or the injector) would probably need to change though.

Ok, yes, constructor injection feels like what I want to try first, now when I soon start porting to 2.6.

Thanks & regards,
KajMagnus

Justin du coeur

unread,
Aug 23, 2017, 9:59:42 AM8/23/17
to play-fr...@googlegroups.com
On Wed, Aug 23, 2017 at 2:08 AM, Kaj Magnus Lindberg <kajma...@gmail.com> wrote:
On Tuesday, July 18, 2017 at 4:06:15 PM UTC+2, Justin du coeur wrote:
A few personal opinions.  I know Guice moderately well, but my own Play application (which long predates the introduction of Guice to Play) uses its own homebrew DI solution, named Ecology.

(More precisely, it eschews DI per se in favor of a more old-fashioned runtime-dependencies model that uses a more explicit "god object".  I've been using essentially this same approach in five different languages over the past 18 years: it's not fancy, but works consistently and well.  The point is, there are *many* options for managing your dependencies in Play.)

I had a look at Ecology. I'm not good enough at Scala to understand the implementation :- P
Interesting to know that one can write one's own DI lib though and that it's not that super much code :- )

Yeah, although I should point out, in the interest of honesty, that that's partly because I'm not trying to do any "injection" magic.  Ecology is really a pretty simple pattern, that doesn't go mucking with macros or reflection or such.
 
Ok yes I've run into some bugs related to init order, in my app, already, and have had to spend some time debugging. (Mostly forward-referencing non-lazy vals that were still 'null'.)

Yeah, that's pretty common.  It's bitten me enough times over the years that I've wound up dealing with it at the architectural level, so I stop wasting time on it.

I'll probably use constructos, and pass lots of objs and things to the constructors. Even if it's a bit verbose, at least I won't have to learn anything new, which I would forget after a year, and have to re-learn again and again :- P

Yeah, and that will likely work just fine.  Keep in mind that I'm speaking from the moderately-complex end of things -- while Querki isn't huge in the grand scheme of things, 50k lines of Scala and 70+ subsystems is still decently big.  For something smaller, manual construction is probably decent.
 
What about Scala Native, https://github.com/scala-native/scala-native — I suppose Scala Native won't work with Guice and runtime-DI? But compile-time-DI works fine with Scala Native, right?

Almost certainly correct, at least that Guice per se (or anything else dependent on reflection) won't work.  Some forms of runtime-dependency-management would work just fine, so long as they aren't reflection-based -- for example, my above-linked Ecology system is already in heavy use on both the JVM and JS platforms, and will probably work fine on Native.

But I don't know if Scala Native is ever likely to run Play, so I'm not sure how relevant that is...

Ok. I was thinking / hoping that maybe maybe after 10 years or so.

Not impossible, but I wouldn't hold my breath.  Scala Native is more likely to wind up focused on embedded applications and suchlike (I suspect), rather than webservers... 
Reply all
Reply to author
Forward
0 new messages