Is there a better way to create singletons than cr...
# kotlin-inject
d
Is there a better way to create singletons than creating a new scope notation for each component?
e
It's varies by project, but in my experience there shouldn't be as many Components as the documentation makes it seem. @evant maybe that's something that can be improved on? Early on I found myself using
@Component
a lot because the docs showed things like
RealNetworkComponent
. However, structuring your component hierarchy like that means that other parent components of
AppComponent
wouldn't be able to inject anything from
RealNetworkComponent
. My solution to that (and answer to @Daniel Weidensdörfer) has been to use a hierarchy of
AppComponent
,
ActivityComponent
and
ScreenComponent
, with a corresponding scope for each one (obviously an Android solution, but the concept extends to any other system). Instead of having a
NetworkComponent
I declare a
NetworkModule
interface and have the appropriate component implement it (or more recently just use kotlin-inject-anvil and
ContributesTo
).
d
That was my first approach as well, how would you handle it in a multi module project? Say there are
domain
,
data
,
feature1
,
feature2
,
app
modules. With obvious dependencies
data
<- `domain`<-
features
<-
app
. Currently I have a component for each of the gradle modules. Since there is no "Master" component that contains everything, each feature gets its references from its own component FeatureXComponent which in turn reference the DomainComponent (if needed). The DomainComponent cannot be a "Module" interface as two different trees would be generated for each child feature. So with this architecture, I'm creating a new scope XXXSingleton for each component. While I'm asking myself why there is no global Singleton annotation provided by the framework.
m
@eygraber thank you so much, I've been searching all over GitHub to understand how other projects are using kotlin-inject, and I was seeing so many interfaces, when the README hinted me on the
@Component abstract class
path. there are a few drawbacks with using interfaces, especially when `internal`s are involved (to Daniel's point on modularization), but after some time trying your idea out:
a hierarchy of
AppComponent
,
ActivityComponent
and
ScreenComponent
I finally got to a place that feels familiar with other projects, and while boilerplate exists in different forms, it's smaller and easier to handle than the other options, it seems. ------ @Daniel Weidensdörfer thank you for the original post, I had the same question and struggle, and was going the same route of components being 1:1 with gradle modules. I think what Eliezer is suggesting is we wouldn't have `@Component`s per module, but interfaces, and then only a few "master" components that implement them: those 3 he pointed out (where I understand
ScreenComponent
means N of those for N screens, i.e.
HomeScreenComponent
,
SettingsScreenComponent
etc.) by having fewer components, you automatically have fewer scopes, as all the interfaces implemented by 1 component can use that 1 scope
Copy code
@ApplicationScope
@Component
abstract class ApplicationComponent(
  @get:Provides val context: Context
) : DataComponent, NetworkComponent

...

@ActivityScope
@Component
abstract class ActivityComponent(
  @Component val applicationComponent: ApplicationComponent
) : InAppReviewComponent, SplashScreenComponent

...

@HomeScreenScope
@Component
abstract class HomeScreenComponent(
  @Component val activityComponent: ActivityComponent
) : HomeStuffComponent
this is one of the repos that helped shape my ideas. While learning, I like to start simple, so it's been helpful to me that it doesn't use Anvil, which would be another thing for me to learn from zero