Synchronous code is easy to write, predictable, and easy to test, but in some cases, it doesn't use system resources in an optimal manner:
class SynchronousUserService(private val userClient: UserClient,
private val factClient: FactClient,
private val userRepository: UserRepository,
private val factRepository: FactRepository) : UserService {
override fun getFact(id: UserId): Fact {
val user = userRepository.getUserById(id)
return if (user == null) {
val userFromService = userClient.getUser(id)
userRepository.insertUser(userFromService)
getFact(userFromService)
} else {
factRepository.getFactByUserId(id) ?: getFact(user)
}
}
private fun getFact(user: User): Fact {
val fact = factClient.getFact(user)
factRepository.insertFact(fact)
return fact
}
}
There's nothing fancy here, just your normal, old boring code:
fun main(args: Array<String>) {
fun execute(userService: UserService, id: Int) {
val (fact, time) = inTime {
userService.getFact(id)
}
println("fact = $fact")
println("time = $time ms.")
}
val userClient = MockUserClient()
val factClient = MockFactClient()
val userRepository = MockUserRepository()
val factRepository = MockFactRepository()
val userService = SynchronousUserService(userClient,
factClient,
userRepository,
factRepository)
execute(userService, 1)
execute(userService, 2)
execute(userService, 1)
execute(userService, 2)
execute(userService, 3)
execute(userService, 4)
execute(userService, 5)
execute(userService, 10)
execute(userService, 100)
}
We execute the UserService.getFact method 10 times to warm up the JVM (JVM optimizations make the application run faster after a while). Needless to say, execution time is 1,600 milliseconds, no surprises here.