Android Coden
Android 4 min lesen

Use Cases: Geschäftslogik sauber kapseln

Use Cases kapseln eine konkrete Businessaktion in einer eigenen Klasse. Damit bleibt deine Architektur testbar und übersichtlich.

Android-Apps wachsen schnell in Komplexität. Sobald ein ViewModel mehrere Repositories abfragen, Daten transformieren und dabei noch Businessregeln durchsetzen muss, wird der Code unübersichtlich, schwer zu testen und noch schwerer zu warten. Use Cases – in älteren Texten auch als Interactors bezeichnet – lösen dieses Problem, indem sie genau eine Geschäftsaktion in einer eigenen, fokussierten Klasse kapseln und so das ViewModel von dieser Verantwortung befreien.

Was ist das?

Ein Use Case ist eine einfache Kotlin-Klasse, die eine einzelne, klar benannte Businessaktion repräsentiert. In der offiziellen Android-Architektur von Google bildet er das Herzstück der optionalen Domain-Schicht – der mittleren Ebene zwischen der UI-Schicht (ViewModels, Composables) und der Data-Schicht (Repositories, Datenquellen).

Der Name eines Use Cases beschreibt immer, was er tut: GetUserProfileUseCase, SubmitOrderUseCase oder LogOutUseCase. Diese Konvention macht den Code selbst dokumentierend, ohne dass zusätzliche Kommentare nötig sind. Der Use Case besitzt keinen eigenen Zustand, kein UI-State-Management und keine Datenhaltung – er koordiniert lediglich den Ablauf zwischen bestehenden Komponenten.

Im Unterschied zu einem Repository, das für den Datenzugriff zuständig ist, oder einem ViewModel, das UI-State verwaltet, ist der Use Case ausschließlich für die Orchestrierung von Businesslogik verantwortlich. Er beantwortet die Frage: „Was muss in der App passieren, wenn der Nutzer diese Aktion auslöst?”

Wie funktioniert es?

Ein Use Case implementiert typischerweise den operator fun invoke()-Operator, damit er im aufrufenden Code wie eine Funktion aussieht. Das ist keine technische Vorgabe von Jetpack, aber eine in der Android-Community weit verbreitete und empfohlene Konvention.

class GetUserProfileUseCase(
    private val userRepository: UserRepository,
    private val settingsRepository: SettingsRepository
) {
    suspend operator fun invoke(userId: String): Result<UserProfile> {
        val user = userRepository.getUser(userId).getOrElse { return Result.failure(it) }
        val settings = settingsRepository.getSettings()
        return Result.success(user.applySettings(settings))
    }
}

Im ViewModel wird der Use Case per Dependency Injection – üblicherweise Hilt – bereitgestellt und direkt aufgerufen:

@HiltViewModel
class ProfileViewModel @Inject constructor(
    private val getUserProfile: GetUserProfileUseCase
) : ViewModel() {

    private val _uiState = MutableStateFlow<ProfileUiState>(ProfileUiState.Loading)
    val uiState: StateFlow<ProfileUiState> = _uiState.asStateFlow()

    fun loadProfile(userId: String) {
        viewModelScope.launch {
            _uiState.value = getUserProfile(userId)
                .fold(
                    onSuccess = { ProfileUiState.Success(it) },
                    onFailure = { ProfileUiState.Error(it.message) }
                )
        }
    }
}

Das ViewModel weiß nichts darüber, dass zwei Repositories beteiligt sind oder wie die Daten zusammengeführt werden. Diese Orchestrierung liegt vollständig im Use Case – das ist der entscheidende Qualitätsgewinn dieser Architekturentscheidung.

In der Praxis

Wann lohnt sich ein Use Case?

Google empfiehlt ausdrücklich, einen Use Case nur einzuführen, wenn er tatsächlich Logik trägt. Ein Use Case, der lediglich eine Repository-Methode eins zu eins weiterleitet, ist ein unnötiger Wrapper, der die Codebasis aufbläht, ohne einen Mehrwert zu liefern:

// Unnötiger Wrapper – bitte vermeiden
class GetUsersUseCase(private val repo: UserRepository) {
    suspend operator fun invoke() = repo.getUsers()
}

Hier ist es besser, das Repository direkt im ViewModel zu injizieren. Ein Use Case lohnt sich erst, wenn er mindestens eine der folgenden Aufgaben übernimmt: mehrere Repositories kombinieren, Daten filtern oder transformieren, bedingte Businessregeln durchsetzen, die nichts mit der UI zu tun haben.

Typische Stolperfalle: Schleichende Verantwortungsübernahme

Der häufigste Fehler bei der Arbeit mit Use Cases ist, ihnen schrittweise zu viele Aufgaben zu geben. Beginnt eine SubmitOrderUseCase-Klasse neben der eigentlichen Bestellübermittlung auch die Adressvalidierung, die Zahlungsabwicklung und das Auslösen von Analytics-Events zu übernehmen, verletzt sie das Single-Responsibility-Prinzip – genau das, das Use Cases eigentlich schützen sollen. Die Lösung: Teile solche Klassen in kleinere, dedizierte Use Cases auf und komponiere sie bei Bedarf in einem übergeordneten Use Case. Orchestrierung bedeutet Koordination, keine Anhäufung.

Unit-Tests ohne Emulator

Da Use Cases keine Android-Framework-Abhängigkeiten besitzen, lassen sie sich mit reinem JUnit und einem gefälschten Repository testen:

@Test
fun `invoke kombiniert Nutzer und Einstellungen korrekt`() = runTest {
    val fakeUserRepo = FakeUserRepository(user = testUser)
    val fakeSettingsRepo = FakeSettingsRepository(settings = darkModeSettings)
    val useCase = GetUserProfileUseCase(fakeUserRepo, fakeSettingsRepo)

    val result = useCase("user-123")

    assertTrue(result.isSuccess)
    assertTrue(result.getOrNull()?.isDarkModeEnabled == true)
}

Dieser Test läuft in Millisekunden, benötigt keinen Emulator und prüft die Businesslogik vollständig isoliert. Das ist einer der größten praktischen Vorteile der Domain-Schicht.

Fazit

Use Cases sind ein einfaches, aber wirkungsvolles Werkzeug, um Businesslogik aus ViewModels herauszulösen und testbar zu machen. Der Schlüssel liegt in der Disziplin bei ihrer Einführung: Ein Use Case ohne echte Orchestrierung ist Ballast, ein Use Case mit klar abgegrenzter Verantwortung ist ein Gewinn für die gesamte Codebasis. Öffne ein aktuelles Projekt und prüfe, ob ein ViewModel Logik enthält, die mehrere Datenquellen kombiniert oder Bedingungen auswertet, die nichts mit der UI zu tun haben. Lagere diese Logik in einen Use Case aus, schreibe einen Unit-Test dafür und beobachte, wie viel lesbarer ViewModel und Test dadurch werden.

Quellen (2)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.