Writing a repository with async functions

Having functions that return an implementation of Job can be convenient in some scenarios, but it has the disadvantage of requiring the code to use join() or await() in order to suspend while a coroutine is being executed. What if we want to suspend as the default behavior? Let's design a repository using async functions and see what an implementation would look like. Let's start with the following data class:

data class Profile(
val id: Long,
val name: String,
val age: Int
)

Now let's design an interface of a client that retrieves profiles based on name or id. The initial design would look like this:

interface ProfileServiceRepository {
fun fetchByName(name: String) : Profile
fun fetchById(id: Long) : Profile
}

But we want the implementation to have async functions, so we switch to returning a Deferred of Profile and rename the functions accordingly. Something like the following would work:

interface ProfileServiceRepository {
fun asyncFetchByName(name: String) : Deferred<Profile>
fun asyncFetchById(id: Long) : Deferred<Profile>
}

A mock implementation would be straightforward:

class ProfileServiceClient : ProfileServiceRepository {
override fun asyncFetchByName(name: String) = async {
Profile(1, name, 28)
}

override fun asyncFetchById(id: Long) = async {
Profile(id, "Susan", 28)
}
}

This implementation can then be invoked from any other suspending computation. See the following example:

fun main(args: Array<String>) = runBlocking {
val client : ProfileServiceRepository = ProfileServiceClient()

val profile = client.asyncFetchById(12).await()
println(profile)
}

The output is what we expect:

There are some things that we can observe from this implementation:

  • The name of the functions is conveniently verbose: it's important that we make it explicit that the function is async to make the client aware that they should wait until completion to continue if they need.
  • Because of the nature of this client, it's probable that the caller will always have to suspend until the request is completed, so the call to await()  will commonly be there right after the call to the function.
  • The implementation will be tied to Deferred. There is no clean way to implement the interface ProfileServiceRepository with a different type of future. It would be messy to try to write an implementation that uses concurrency primitives that aren't from Kotlin. For example, imagine yourself writing an implementation with RxJava or Java's Future.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.200.3