ViewModel Clean Stylish

Abdullah Balta
3 min readMar 14, 2020

--

Using Architecture Component is strongly recommended by Google. So, Android Developers using any kind of architecture when developing apps. MVVM, MVP, and MVI are the most popular ones. However, the main idea is the separation of UI and business logic.

We end up adding extra layers to existing patterns as we combine CleanArchitecture with them. Let’s think about both the MVVM pattern and CleanArchitecture together. In MVVM pattern initial “V” letter stands for “View” means that your activities, fragments, adapters, custom widgets, etc. On the other side, CleanArchitecture has a presentation layer as we know. This layer includes activities, fragments, adapters, custom widgets and viewmodels.

You noticed that, there is a conflict with viewmodels. How can we separate UI and business logic? The answer is UseCase but what is the use case?

Let’s look at some viewmodel approaches with the following scenario. The app has tutorial screens. If the user skips the tutorial, it shows again when the app restarts. If the user completes the tutorial, it never shows again. The user directly sees the dashboard screen when the app restarts.

First Approach: ViewModel No Clean Way

class TutorialViewModel(
private val tutorialRepository: TutorialRepository
): ViewModel() {
val showDashboardLiveData = LiveData<Unit>()
val showTutorialLiveData = LiveData<Unit>()
fun isTutorialSkipped() {
if (tutorialRepository.isSkipped()) {
showDashboardLiveData.value = Unit
} else {
showTutorialLiveData.value = Unit
}
}
}

This is a simple ViewModel class in MVVM. Like Google recommended app architecture. ViewModel class has some logic and that’s fine.

Second Approach: ViewModel With Clean Architecture

In this approach, viewModel executes useCase and useCase returns if the user skipped the tutorial or not. Still ViewModel class has logic and our architecture has an additional layer that means more classes and more lines of code. So, how can we get rid of this logic?

class TutorialViewModel(
private val tutorialUseCase: TutorialUseCase
): ViewModel() {
val showDashboardLiveData = LiveData<Unit>()
val showTutorialLiveData = LiveData<Unit>()
fun isTutorialSkipped() {
if (tutorialUseCase.execute()) {
showDashboardLiveData.value = Unit
} else {
showTutorialLiveData.value = Unit
}
}
}

Here is useCase class,

class TutorialUseCase(
private val tutorialRepository: TutorialRepository
): UseCase<Boolean, Unit> {

override fun execute(): Boolean = tutorialRepository.isSkipped()
}

Third Approach: Super Clean ViewModel

class TutorialViewModel(
private val tutorialUseCase: TutorialUseCase
): ViewModel() {
val showDashboardLiveData = LiveData<Unit>()
val showTutorialLiveData = LiveData<Unit>()
fun isTutorialSkipped() {
tutorialUseCase.execute(
{ showDashboardLiveData.value = Unit },
{ showTutorialLiveData.value = Unit }
)
}
}

Here is more logical UseCase :)

class TutorialUseCase(
private val tutorialRepository: TutorialRepository
): UseCase<Boolean, Unit> {

override fun execute(showTutorial: () -> Unit = {}, continue: () => {}) {
if (tutorialRepository.isSkipped()) {
showTutorial()
} else {
continue()
}
}
}

Now, ViewModel means the presentation layer gets rid of some logic. So this way is much cleaner. ViewModel just execute the useCase and nothing more.

In CleanArchitecture, useCases are not dependent platform, they keep only your business logic. Think about multi-platform applications like iOS, Android, Desktop or Web apps. Presentation layer has only platform specific codes. So, we can easily use these useCases on other platforms.

--

--