Android : Use Cases hell in MVVM architecture 🤯

Mohamed Ali BEN MANSOUR
3 min readApr 27, 2024

--

How better organize use cases in MVVM architecture.

Following article is inspired from Philipp Lackner video here.

Introduction

In a CLEAN architecture, use cases offers several advantages such as :

  • Separation of concerns
  • More business logic testability
  • Business logic reusability with single source of truth
  • Readability of code

On the other hand, use cases have disadvantages also if we don’t know how to organize them well.

In this article, we will see how we can structure our use cases and better interact with ViewModel to avoid the “Use cases hell”.

Scenario

Let’s suppose that we have a “SampleViewModel” that takes a lot of use cases as constructor parameters.

class SampleViewModel (
private val useCase1: ProductUseCase1,
private val useCase2: ProductUseCase2,
private val useCase3: ProductUseCase3,
private val useCase4: ProductUseCase4,
private val useCase5: UseCase5,
 // Other constructor parameters ...
) : ViewModel() {

// The rest of SampleViewModel body
}

Now the problem is clear ! Imagine that we have 10 use cases 🤯

Many disadvantages :

  • Huge ViewModel constructor, complicated to read and maintain.
  • Boilerplate code to mock all use cases when writing unit tests for the “SampleViewModel”

Solution:

The solution is creating a data class that groups functionally related use cases in just one data class.

data class ProductUseCases(
val productUseCase1: ProductUseCase1,
val productUseCase2: ProductUseCase2,
val productUseCase3: ProductUseCase3,
val productUseCase4: ProductUseCase4
)

// #################### \\

class SampleViewModel (
private val productUseCases: ProductUseCases
private val useCase5: UseCase5,
 // Other constructor parameters ...
) : ViewModel() {

// The body of the ViewModel
 ...
}

We have keep the fifth use case separated because it is not functionally related to other use cases that handle “Product” object.

Concrete example:

Now, that we have understand the problem and explored the solution, let’s take a concrete example !

Imagine that we have a “Notes” application that handles persistence in the local database (ex : Room)

We have four use cases to handle some requests, a data class “NotesUseCases” to group these use cases in the same class, and the “NotesViewModel”.

Suppose that we use “DaggerHilt” for dependency injection.

class GetNotesUseCase(private val notesRepository: NotesRepository) {
operator fun invoke() {
// The business logic of the GetNotesUseCase :
// sort by date, alphabetical order, filter old notes, ...
}
}
class GetNoteUseCase(...) { ... }
class DeleteNoteUseCase(...) { ... }
class AddNoteUseCase(...) { ... }

// #################### \\

data class NotesUseCases(
val getNotes: GetNotesUseCase,
val getNote: GetNoteUseCase
val deleteNote: DeleteNoteUseCase,
val addNote: AddNoteUseCase
)

// #################### \\

@HiltViewModel
class NotesViewModel @Inject constructor(
private val notesUseCases: NotesUseCases,
private val anotherUseCase: AnotherUseCase // Not related to Note business
// Other constructor parameters ...
) : ViewModel() {

// Example of NotesUseCases usage
val notes = notesUseCases.getNotes()

// The rest of NotesViewModel body
...
}

Now let’s see how to configure DaggerHilt module to provide “NotesUseCases” class needed by the “NotesViewModel”.

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

@Provides
@Singleton
fun provideNotesUseCases(repository: NotesRepository): NotesUseCases {
return NotesUseCases(
getNotes = GetNotesUseCase(repository),
deleteNote = DeleteNoteUseCase(repository),
addNote = AddNoteUseCase(repository),
getNote = GetNoteUseCase(repository)
)
}

// Other dependencies injection
...

}

That’s it !

⚠️ Point of attention ⚠️

  • When grouping use cases, it's crucial to consider how and how frequently they will be used in the ViewModel.
  • The use cases that can be grouped together must be functionally related and they are often all used in the ViewModel.

Conclusion

That’s all about the better way to organize use cases in a clean & MVVM architecture. I hope you find this article useful !

I wanted to take a moment to express my deepest gratitude for Philipp Lackner for the invaluable tutorials he has been providing and generosity in sharing its knowledge.

--

--