Can you provide some hint (or maybe example or sample) how to proper use Koin in project which use modules to divide into layers according to clean arch.
So the dependences between modules (layer) should be:
Also:
Now I pass about data module to appPresenation (it depends also on data) bacause I cannot figure out how to expose *UseCase classes without providing implementations of *repos
Similar question is here but I cannot figure out solution:
https://www.reddit.com/r/androiddev/comments/akmawy/multimodule_clean_architecture_project_make_koin/
Hi,
we recently did this in our project. The trick is to not provide an Implementation of your repo, but rather an Interface of the repository, which is defined in the "domain" layer. In this case, your UseCase remains uncoupled of the actual implementation of the repo and you could easily switch the dataSource.
You can then "provide" the Repo as follows:
single { PoiRepository(get()) as IPoiRepository }
Our UseCase (we named it Interactor) looks like this:
factory { GetPoisInteractor(get(), get(), get()) }
And the class itsself:
class GetPoisInteractor(
subscribeScheduler: ISubscribeScheduler,
postExecutionThread: IObserveScheduler,
private val poiRepository: IPoiRepository
)...
We also have it like this:
We have one "Injection" object in each layer so we have an "AppInjection" object which holds the appModule, a "DomainInjection" object which holds the domainModule and a "DataInjection" object which holds the dataModule.
In the application just start koin like this:
startKoin {
androidContext(this@Application)
modules(listOf(appModule, domainModule, dataMdoule))
}
thank you a lot @SimonHaenle2 for your response.
I already have that trick: domain has repo interfaces which are implemented in data module.
I don't how to answer to that question: If class PoiRepository is in _data_ module then how it is accessible in koin module which is in _appPresentation_ module? or where you put that module?
So it seems that I miss something. I have only 3 modules. (Maybe I should have additional global app module?)
file: _appPresentation/build.gradle_ has:
dependencies {
implementation project(':domain')
// I want to remove below dependency.
implementation project(':data')
}
file: _domain/build.gradle_ has:
dependencies {
// only pure java/kotlin libs
}
file: _data/build.gradle_ has:
dependencies {
implementation project(':domain')
}
And main application class is in _appPresentation_ module and look like this:
class App : Application() {
override fun onCreate() {
super.onCreate()
startKoin{
androidLogger()
androidContext(this@App)
modules(listOf(appModule, useCaseModule, repoModule, networkModule))
}
}
Module with Repo is in _appPresentation_ module and looks like this:
val repoModule = module {
single<FooBarRepo> { FooBarRepoImpl(get(), get()) }
}
but here is the problem: I have compile error here because the definition of FooBarRepoImpl is not known in _appPresentation_ (and that should be and I want to achieve)
Presentation has aceess to Domain AND Data. That's correct and in my opinion does not violate the clean architecture. So FooBarRepoImpl in my opinion belongs to data module and should be there.
Are you sure? when project has such configuration then classes and functions form _data_ module are accessible in _appPresentation_ module. I think it shouldn't happen. _appPresentation_ should not have an idea that _data_ module exists at all.
Maybe should be a extra module which would bind all? Or separated configuration of koin in _data_ module and pass to that module "app start event" which starts koin for _data_ module?
Found a pretty nice tutorial that explains whole project setup here:
https://proandroiddev.com/creating-clean-architecture-multi-project-mvp-app-34d753a187ad
Also found many other tutorials as well, in all of them presentation has access to domain AND data
I do have a similar implementation to the one described above, and everything is working like a charm so far. However, I am finding many issues when dealing with instrumented tests. In my case, _presentation_, _domain_, and _data_ are libraries/modules of the main project (_app_). The popular _app_ module is simply used for general purposes, such as configuring and starting _Koin_, for example. However, it seems Koin is not working well when undertaking instrumented tests (UI tests in my case) on a module, _presentation_ for example.
This is part of my test:
```@RunWith(AndroidJUnit4::class)
@LargeTest
class LoginActivityTest : KoinTest {
@get:Rule
var activityRule = ActivityTestRule(LoginActivity::class.java)
@Before
fun setUp() {
startKoin(listOf(presentationLayerModule))
}
@After
fun tearDown() {
stopKoin()
}
@Test
fun whenActivityStartsLoginIsDisplayed() {
onView(withId(R.id.activity_login_tv_title))
.check(matches(isDisplayed()))
}
}
The above snippet uses version 1.0.2, although a similar problem arises when upgrading to the recent 2.0.1 version. Basically, the error says something like:
```Started running tests
java.lang.IllegalStateException: StandAloneContext Koin instance is null
at org.koin.standalone.StandAloneContext.getKoin(StandAloneContext.kt:68)
at org.koin.android.ext.android.ComponentCallbacksExtKt.getKoin(ComponentCallbacksExt.kt:74)
at org.deafsapps.android.cleanapp.presentationlayer.login.view.ui.LoginActivity$$special$$inlined$inject$1.invoke(ComponentCallbacksExt.kt:146)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at org.deafsapps.android.cleanapp.presentationlayer.login.view.ui.LoginActivity.getLoginPresenter(LoginActivity.kt)
at org.deafsapps.android.cleanapp.presentationlayer.login.view.ui.LoginActivity.onResume(LoginActivity.kt:52)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1257)
at androidx.test.runner.MonitoringInstrumentation.callActivityOnResume(MonitoringInstrumentation.java:734)
at android.app.Activity.performResume(Activity.java:6076)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2975)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3017)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2392)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
Contrary, I am not having that problem on single-module applications (module _app_ only), so it is clear to me where the problem is. Moreover, no error or issue arises when using Koin for Unit tests. What am I missing?
I believe this is also related to issue https://github.com/InsertKoinIO/koin/issues/380#issue-414254115
Thanks!
@luna-vulpo I know this is a bit old, but your clean architecture implementation/concept is a bit off. As some others mentioned here, your Presentation module does need to know of Data and Domain, and that doesn't violate the Clean Architecture injection pattern but enforces it.
Simplifying a bit:
Data and Domain, and it's and Android module.Domain, implements repositories, and it's an Android library module.As for DI, given that dependency injection is an architectural pattern that manifests separation of concerns at application level, construction concern should be confined to the main module in your application. Library modules should not depend on dependency injection framework that you鈥檙e using.
To get a bit more context on this topic, I recommend reading Mark Seemmann鈥檚 post titled DI-Friendly Library. Mark is probably one of the most authoritative experts in the world in context of dependency injection, so you can trust his judgement
Presentation should NOT know about Data.
That final link you gave is a red-herring as it is only about DI not clean architecture layer dependencies.
I suggest looking at what Uncle Bob actually said or the following issue:
https://github.com/android10/Android-CleanArchitecture/issues/136
@luna-vulpo You're right.
The problem here is that architecture deals with theoretical entities, without regarding practical issues. Just like physics homework that starts with "assume no friction", it cannot be implemented directly. The theoretical division into Presentation, Domain and Data is glaringly ignoring the real-world issue: how to tie them all together and present to the operating system as a single app.
In order to do that while staying 100% faithful to the division, (as you've already noticed) you need a fourth module, lets call it app. It will perform the real world chores: OS entry point, inclusion in gradle dependencies and instantiation of the other 3 modules. This lets you maintain hard architectonic barrier between Presentation and Data. The benefit is that nobody can use data classes in ui even by accident.
However, this usually comes at quite high inconvenience costs while benefits are negligible for an experienced team. Therefore, in common practice, the tiny app duties are merged into (comparatively) huge Presentation module. This creates false appearance that tying them all together is inherent duty of Presentation. It's not - but the whole point of designer's job is to know when to sidestep the rules. As long as you maintain some other kind of barrier between ui and data, it's fine. In a perfect world Data should not export any of it's internals - so creating accidental dependency should be impossible anyway.
Are you sure? when project has such configuration then classes and functions form _data_ module are accessible in _appPresentation_ module. I think it shouldn't happen. _appPresentation_ should not have an idea that _data_ module exists at all.
Maybe should be a extra module which would bind all? Or separated configuration of koin in _data_ module and pass to that module "app start event" which starts koin for _data_ module?
I struggled with the same question.
And I as surprised to find that in Fernando Cejas's (android10) implementation of the Clean Architecture,
there is a dependency to the data module in presentation:
https://github.com/android10/Android-CleanArchitecture/blob/master/presentation/build.gradle
Presentation should NOT know about Data.
That final link you gave is a red-herring as it is only about DI not clean architecture layer dependencies.
I suggest looking at what Uncle Bob actually said or the following issue:
android10/Android-CleanArchitecture#136
But it DOES know in this example:
https://github.com/android10/Android-CleanArchitecture/blob/master/presentation/build.gradle
Most helpful comment
@luna-vulpo You're right.
The problem here is that architecture deals with theoretical entities, without regarding practical issues. Just like physics homework that starts with "assume no friction", it cannot be implemented directly. The theoretical division into
Presentation,DomainandDatais glaringly ignoring the real-world issue: how to tie them all together and present to the operating system as a single app.In order to do that while staying 100% faithful to the division, (as you've already noticed) you need a fourth module, lets call it
app. It will perform the real world chores: OS entry point, inclusion in gradle dependencies and instantiation of the other 3 modules. This lets you maintain hard architectonic barrier betweenPresentationandData. The benefit is that nobody can use data classes in ui even by accident.However, this usually comes at quite high inconvenience costs while benefits are negligible for an experienced team. Therefore, in common practice, the tiny
appduties are merged into (comparatively) hugePresentationmodule. This creates false appearance that tying them all together is inherent duty ofPresentation. It's not - but the whole point of designer's job is to know when to sidestep the rules. As long as you maintain some other kind of barrier between ui and data, it's fine. In a perfect worldDatashould not export any of it's internals - so creating accidental dependency should be impossible anyway.