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

http://jooby.org/doc/

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()
)
Made with Slides.com