Android-CleanArchitecture-Kotlin
Android-CleanArchitecture-Kotlin copied to clipboard
Returning LiveData from domain layer
What do you think about returning LiveData instances directly from domain Layer? I'm asking it because the one of the best features of Room framework is the hability to observe changes in DB directly.
So the LiveData instances should be returned directly from domain (usecase and repositories)?
LiveData is part of the framework so i would not use it in the domain layer.
AFAIK livedata can work even in unit tests?
@St4B I don't think is a good aproach lose Room features of update LiveData just because its part of framework. This rule is very important to make the unit tests easier, but we can test livedata using Junit without any problem, just like another project class.
hmmm ... I was affected by the old project (with rxJava) which had different models per layers and mappers. Now that we use the same model in every layer it does not seem wrong to me. Basically I started to like it! : p
@Zhuinden yes. You have to add dependencies of livedata to unit test
android.arch.core:core-testing:$arch_version
and than add the rule InstantTaskExecutorRule
into unit test file
Totally agree here with the above comments.
LiveData
belongs to the Android Framework which is something you wanna avoid as much as you can, at least at domain level.
In my opinion, it clearly belong to the UI layer, being a key part of MVVM.
If you use RxJava in your project, the solution might be to return the Observable from the domain layer and use the reactive streams to convert it to the LiveData in the UI layer.
If you use RxJava in your project, the solution might be to return the Observable from the domain layer and use the reactive streams to convert it to the LiveData in the UI layer.
Is this available already in the source code? This would be a big help since I want to take advantage of Room -> LIve Data interaction by using this clean arch repository.
If you use RxJava in your project, the solution might be to return the Observable from the domain layer and use the reactive streams to convert it to the LiveData in the UI layer.
Is this available already in the source code? This would be a big help since I want to take advantage of Room -> LIve Data interaction by using this clean arch repository.
No, this sample app is not using RxJava.
I agree that the domain layer must not have a dependency on the platform-specific frameworks/libraries, but based on YAGNI principle we're just adding an extra level of abstraction without gain.
So I think passing LiveData
in domain layer give us some advantages like less boilerplate for some features like Room.
I am kind a new to the Clean Approach, but there was always something that made my crazy in the begging when I tried to understand it. Everywhere, like in here, everybody are saying - just plain Java! No Android, not this, no that, but every time RxJava is the solution of everything. But RxJava is not pure Java!! I swear, I have never seen an article saying - "Here we are using RxJava, and we are breaking the principle, but there are more pros than cons, so it is OK!". So saying this I don't see why RxJava is ok, but LiveData not.
@mitevyav Well, RxJava is not something that is Android specific, it can be used everywhere (web, mobile, desktop, etc..), on the other side, LiveData
is Android specfic, that's why it's "acceptable" to return something like Observable
from the domain layer, and for LiveData
it is not. It's just that RxJava is so popular, widely accepted and some will say it feels almost like a part of the language.
IMO, you don't need to strictly follow clean "rules", just pick what suits your use-case the best and as long as your app is testable and maintainable you are good.
@Zhuinden yes. You have to add dependencies of livedata to unit test
android.arch.core:core-testing:$arch_version
and than add the ruleInstantTaskExecutorRule
into unit test file
After adding the core-testing dependency
testImplementation "androidx.arch.core:core-testing:2.0.0"
it throws an error that has do with with Powermockito.
java.lang.AbstractMethodError: org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.isTypeMockable(Ljava/lang/Class;)Lorg/mockito/plugins/MockMaker$TypeMockability;
at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29)
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:232)
at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:64)
at org.mockito.Mockito.mock(Mockito.java:1871)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:36)
at org.mockito.internal.configuration.MockAnnotationProcessor.process(MockAnnotationProcessor.java:16)
at org.mockito.internal.configuration.IndependentAnnotationEngine.createMockFor(IndependentAnnotationEngine.java:38)
at org.mockito.internal.configuration.IndependentAnnotationEngine.process(IndependentAnnotationEngine.java:62)
at org.mockito.internal.configuration.InjectingAnnotationEngine.processIndependentAnnotations(InjectingAnnotationEngine.java:57)
at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:41)
at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:69)
at com.etiennelawlor.pitted.TopSpotsViewModelTest.setUp(TopSpotsViewModelTest.kt:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
This error gets thrown after you call the following function in the setUp function
MockitoAnnotations.initMocks(this)
So i commented out these dependencies
// testImplementation “org.mockito:mockito-all:$mockitoVersion” // testImplementation “org.hamcrest:hamcrest-all:$hamcrestVersion” // testImplementation “org.powermock:powermock-module-junit4:2.0.0-RC.1” // testImplementation “org.powermock:powermock-api-mockito:1.7.4"
and now i just have these depenedencies
testImplementation “junit:junit:$junitVersion” testImplementation “org.mockito:mockito-core:2.21.0"
and there is no longer a conflict.
Why don't you go for Flow instead of LiveData. as Flow is a pure data driven item.
LiveData is part of the framework so i would not use it in the domain layer.
Objectively, if you don't want to use LiveData, then you shouldn't use Room.
Room is also Android-specific.
Honestly, we should just all use Flutter, you can port that to any platform now. 😂
Definitely, at the moment there is only one solution for this problem and it's using Coroutine Flow instead of LiveData.
That's it!
RxJava / Reaktive are technically both valid options
RxJava / Reaktive are technically both valid options
I suppose that it's not necessary to bring huge framework in your project just for gaining data from Room :)
In that case, just use LiveData 😉
In that case, just use LiveData 😉
The question is "how to avoid liveData in domain layer?", so..