-
Thanks for your support and for maintaining the framework, On a big scale project, how would you handle dependencies / dependency injection? We have been exploring some approaches:
Advantages:
Disadvantages:
Advantages:
Disadvantages:
Advantages:
Disadvantages:
Concrete example 1: Tracking. We need to be able to fire tracking events from almost all screens on the app. We plan on having a With approach 1 we will have a trackingService parameter on almost every component we create. Approaches 2 and 3 seem cleaner. Which would be the preferred way? Concrete example 2: Auth. Some parts of the app UI will have to logout the user. We plan to have an With approach 2/3 all components will have access to it even if not needed. Is a shared service the right approach? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Thanks for such a good question! First of all, personally I'm trying to avoid any DI frameworks. It might be opinionated, but I believe that DI frameworks hide the excessive complexity of the code. When I catch myself willing to add a DI framework, it is a sign for me that the code is coupled and complex. A solution for this is usually to decompose the code by smaller peaces (e.g. modules). This is actually one of the primary purposes of Decompose! So I would certainly go with the approach #1 or #2. I would pass all the dependencies via constructors. Except, probably, the cases with the most commonly used dependencies, like Also as a side note, using a DI framework doesn't necessarily mean not compile time safe. E.g. Dagger is compile time safe. PS: I'm wondering how "Using a DI framework (Koin / Kodein / Service Locator)" would help to have "Unbloated component constructors"? |
Beta Was this translation helpful? Give feedback.
-
Thank you for sharing. With unbloated component constructors what I meant is with approach 1, constructor increases size with every new service that it or its children uses: ComponentImpl(
componentContext,
storeProvider,
trackingService,
authService,
outputCallback,
...
): Component {
private val store = instanceKeeper.getStore {
ComponentStoreProvider(
storeFactory,
trackingService,
authService
).provide()
}
} with approach 3 (eg using Koin DI ), we pull the services from DI: ComponentImpl(
componentContext,
storeProvider
): Component, KoinComponent {
private val store = instanceKeeper.getStore {
ComponentStoreProvider(
storeFactory = storeFactory,
trackingService = get(), // <-- get() retrieves dependency from Koin module
authService = get()
).provide()
}
} |
Beta Was this translation helpful? Give feedback.
Thanks for such a good question! First of all, personally I'm trying to avoid any DI frameworks. It might be opinionated, but I believe that DI frameworks hide the excessive complexity of the code. When I catch myself willing to add a DI framework, it is a sign for me that the code is coupled and complex.
A solution for this is usually to decompose the code by smaller peaces (e.g. modules). This is actually one of the primary purposes of Decompose!
So I would certainly go with the approach #1 or #2. I would pass all the dependencies via constructors. Except, probably, the cases with the most commonly used dependencies, like
Tracker
orHttpClient
. Such common things I would likely add to t…