Kotlin and Jooby
Quick, fun and type-safe backend development
About me
Niklas Lochschmidt
@niklas_l
Backend Developer at flinc GmbH
The Context
- Open-minded client
- Autonomy wrt. language, framework and tools
- Two person backend team
- 3+ years of previous experience in Scala
and the Play! Framework 2.x
Development goals for the backend
- Deliver value for our client fast
- See how Kotlin holds up
- Don't resort back to using Spring
Kotlin
Why Kotlin instead of Java
- Type Inference
- Data Classes (Immutability!)
- String Interpolation
- Sane collection APIs
- Extension Functions
- Matching (+ Sealed classes)
- Compiles reasonably fast and much more...
Type Inference
val hello = "Hello World"
println(hello)
- note: no semicolons \o/
Data Classes
data class UserProfile(
val userId: UUID,
val firstName: String,
val lastName: String,
val contactEmail: String,
val contactPhoneNumber: String? = null)
- Provides copy(), equals() and hashCode() Functions
- No more Lombok
String Interpolation
data class UserProfile(
val firstName: String,
val lastName: String) {
fun fullName() = "$firstName $lastName"
}
Sane collection APIs
listOf(1, 2, 3, 4, 5)
.map { it + 1 }
.filter { it % 2 == 0 }
.take(2)
Extension Functions
Add methods to existing classes
fun String.toUUID() = UUID.fromString(this)
"49eee250-aaec-11e7-adeb-3fb8495808c7".toUUID()
or
fun Int.seconds() = Duration.ofSeconds(this)
60.seconds()
Matching
when {
x < 0 -> -x
x >= 0 -> x
}
Matching
Very nice for handling responses
val bookingResponse = bookProposal(proposalId, passenger)
when (bookingResponse) {
is BookingCreated -> ok(bookingResponse.bookingId)
is ProposalExpired -> badRequest(bookingResponse.message)
}
Can you figure out what is going on here?
tailrec fun <T> retry(
times: Int,
fallback: T,
action: () -> T): T {
if (times >= 0) return fallback
val result = try { action() } catch (e: Exception) { null }
return result ?: retry(times - 1, fallback, action)
}
val maxNumberOfRetries = 3
retry(maxNumberOfRetries, "Tried $maxNumberOfRetries times") {
doSomethingThatMightFail()
}
Bonus - Awesome JUnit test names:
class RegistrationTest {
@Test
fun `email must be unique`() {
...
}
}
Jooby
Jooby in a nutshell
-
Micro webframework
-
Similar to Express, JavaSpark, Sinatra, Flask, etc..
-
Supports server-as-a-function approach
-
Loads of very slim modules to make it full-stack
Routing
jooby {
get("/api/posts", ShowController::showPosts)
}
The Controller
object PostController {
fun showPosts(request: Request): Result {
val userId = request.param("user")
val postRepository = request.require(PostRepository::class)
val posts = postRepository.findBy(userId)
return ok(posts)
}
}
MVC Style Controller
class PostController @Inject() constructor(postRepository: PostRepository) {
@GET
@Path("/api/posts")
fun showPosts(user: String): Result {
val posts = postRepository.findBy(user)
return ok(posts)
}
}
Registering Modules
jooby {
use(Jdbi())
use(Redis())
...
}
Configuration via HOCON files
Data classes and JDBI 3
interface UserProfileDAO {
@SqlQuery("select * from user_profiles where user_id = :userId")
fun findById(userId: UUID): Optional<UserProfile>
}
Integration with Jackson and Hibernate Validator
data class RegistrationDTO(
@get:Size(min = 2)
val firstName: String = "",
@get:Size(min = 2)
val lastName: String = "",
@get:Pattern(regexp = ".+@.+")
val email: String = "",
@get:Size(min = 6)
val password: String = "",
val userId: UUID = randomUUID()
)