DEV Community

Cover image for Stop Using Result Wrappers In Your Reactive Return Types
Melih Aksoy
Melih Aksoy

Posted on

Stop Using Result Wrappers In Your Reactive Return Types

In modern day Android projects, it’s typical to see a Repository , UseCase or xyz abstract component exposing a business logic like

interface SomeRepository{

    fun getSomeData(): Flow<Result<List<Bruh>>>
}
Enter fullscreen mode Exit fullscreen mode

Where Result looks something like this

sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Failure(val error: Error) : Result<Nothing>()
    data object Loading : Result<Nothing>() // This is a UI state, why would domain care about this anyway ? 
}
Enter fullscreen mode Exit fullscreen mode

You’ll have some issues when you want to use this wrapper in your reactive return types, mainly

  • You will write a lot of boilerplate checks ( where you ignore the cases except Success most of the time )

  • Your return types become awkwardly long like and hard to read, like Flow>> — which I’ll use in examples to make it look even worst. Is there any worse ?

  • Instead of a simple collection logic, you need to do a type check every single time you need a value.

  • Reactive operations become cumbersome due to unnecessary when statements and such. Stop shooting yourself on the foot.

As a simple example, let’s compare two methods from each to see what we end up with. Imagine we have following class

interface FancyRepository {

    fun provideFoos(): Flow<Result<List<Foo>>>
    fun provideBars(): Flow<Result<List<Bar>>>
}
Enter fullscreen mode Exit fullscreen mode

Oh boy, aren’t you in for a treat !? Let’s say you simply want to combine these two lists into some other data:

fun simpleCombineOperation(fancyRepository: FancyRepository): Result<List<Baz>> {
    fancyRepository
        .getFoos()
        .combine(fancyProvider.getBars()) { foo, bar ->
            when (foo) {
                is Result.Failure -> { /* Ignore or rethrow */}
                is Result.Loading -> { /* Extra line */}
                is Result.Success -> {
                    val foos = foo.data

                    // Time to check for bar !
                    when (bar) {
                        is Result.Failure -> { /* Keep ignoring or rethrowing */}
                        is Result.Loading -> { /* Yep, again */}
                        is Result.Success -> {
                            val bars = bar.data

                            // Oh my god finally I can do stuff with the data
                            // Now time to create a Result from result of results
                            val thingINeedtToProvide = Result.Success(...)
                            ...
                        }
                    }
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Wow.. This code is even less maintainable if you’re on a business layer.

Imagine trying combine two UseCases that return Flow. You need to check Result cases for both UseCases, then generate a new Result and then check that Result again on the UI layer. This paragraph is shorter than that.

Well, somebody has to maintain this !

Sad :(

Now let’s take a look how it is when we don’t wrap the objects

interface FancyRepository {

    fun provideFoos(): Flow<List<Foo>>
    fun provideBars(): Flow<List<Bar>>
}
Enter fullscreen mode Exit fullscreen mode

How do you feel reading the following code ?

fun simpleCombineOperation(fancyRepository: FancyRepository) = 
    fancyRepository
        .getFoos()
        .combine(fancyProvider.getBars()) { foo, bar ->
             // Where's my wife ? ( ̄ω ̄)
             foo + bar
        }
        .catch { /* Handle errors, or let the collector handle */ }
Enter fullscreen mode Exit fullscreen mode

Try, fail, improve !

Don’t keep doing things that doesn’t make sense just because its mainstream or in official docs. It might be very well this stuff is useful in your project and business logic. But if it doesn’t, nuke it.

P.S. I’m writing another article about using Hexagonal Architecture in android, and wrote this shorty since it didn’t seem like the right place to explain it.

Follow me on twitter where I randomly tweet my awkward ideas about mostly android stuff then disappear for long periods of time or github where I have projects that I did all these stuff I just advocated against .

(ノ◕ヮ◕)ノ*:・゚✧.

Top comments (0)