simplifies many things and reduces boilerplate Perfect match for Kotlin The Spring people like Kotlin as well and will add more support in Spring 5.0 and in Spring Boot 2.0 My latest project combines Kotlin and Spring Boot in a Microservices architecture 3
for getting and ordering tickets (SOAP) Payment Service Provider (SOAP) Service to produce keycards (REST) External e-mail provider (REST) External billing service to create invoices (REST) Google Maps (REST) Weather service (REST) We’re using microservices to isolate services from each other and to handle complexity. 7
automated Complete infrastructure setup is done with Terraform EC2 instance and database setup is done with Ansible Services are packaged as docker containers, deployments are triggered via Ansible, scheduled by Nomad, and discovered with Consul EC2 instances are stateless, state is only in the DB and in S3 Each service is deployed once per availability zone, everything is redundant Service-to-Service communication via REST or asynchronously via SQS 8
like in Java: @SpringBootApplication open class BootApplication { @Bean open fun myBean (): MyBean { val myBean = MyBean () myBean.x = "foo" myBean.y = 12 return myBean } } Do we need an instance variable? 16
open class BootApplication { @Bean open fun myBean () = MyBean (). apply { x = "foo" y = 12 } } All these open keywords are a bit annoying though... 18
which help with framework integration kotlin-allopen Makes classes and all methods automatically open if marked with an annotation. Great for Spring. kotlin-noarg Creates a synthetic default constructor for classes if marked with an annotation. Great for JPA. Annotations are customizable, even supporting meta annotations. Very easy support for Spring and JPA with spring and jpa plugins. 19
service.url=http :// example.com/endpoint Injecting configuration properties works just like in Java: class Service( @Value("\${service.url}") val url: String ) Note that you have to escape the $ because it’s used by Kotlin for string interpolation. The preferred way now is to use @ConfigurationProperties. 23
{ lateinit var url: String } or with the Jackson Kotlin module: @Component @ConfigurationProperties ("service") data class ServiceProperties ( val url: String ) You can even use the standard Java validation annotations. 24
flyway or liquibase to define your schema Set jpa.hibernate.ddl-auto to validate to ensure your database schema matches your entities Don’t create a complex completely navigable domain model, use aggregates (Domain Driven Design) instead 26
val id: UUID = UUID.randomUUID () lateinit var firstname: String lateinit var lastname: String } How can we handle properties better? It’s not a good option to make everything nullable and it’s not even possible for basic types. Every lateinit is a NullPointerException waiting to happen. kotlin-noarg makes it easy to use data classes instead. 27
data class Customer( @Id val id: UUID = UUID.randomUUID (), var firstname: String , var lastname: String ) You can specify sensible default values for your properties. Hibernate will invoke the synthetic default (no-arg) constructor and set the values. Automatic generation of toString, equals, and hashCode plus copy function. 28
yourself you can just define a single interface: interface CustomerRepository : CrudRepository <Customer , UUID > You automatically get methods like findOne(id), save(entity), delete(entity), etc. Code is created dynamically by spring-data-jpa. Tip: Consider defining your own KCrudRepository interface to add nullability information. 29
strings to improve readability: @Query(""" SELECT c FROM Customer c WHERE LOWER(c.firstname) LIKE '%' || LOWER (: search) || '%' """) fun search( @Param("search") search: String ): List <Customer > Now that’s much better. 31
DTOs. By default it would just use the default constructor and you need to use lateinit. class CreateUserCommand { lateinit var email: String lateinit var firstname: String lateinit var lastname: String lateinit var signupCode: String? = null } Fortunately there’s a better option. 33
constructor to create instances Can also use secondary constructors with @JsonCreator Automatically understands nullable and non-nullable types Supports Kotlin data types like Pair, Triple, IntRange, etc. 34
used by Spring: @Bean fun kotlinModule () = KotlinModule () Add it to instances of your ObjectMapper: val om = ObjectMapper () . registerModule (KotlinModule ()) Tip: Subclass ObjectMapper and configure it like you need it in your project. 35
class CreateUserCommand ( val email: String , val firstname: String val lastname: String , val signupCode: String? } Now DTOs can even be immutable! Let’s add some validations. 36
so they are put on the getters data class CreateUserCommand ( @get:Email val email: String , @get:Length(max = 32) val firstname: String , @get:Length(max = 32) val lastname: String , val signupCode: String? } No need for @NotNull, it’s already in the type system. 37
pass user authorization from service to service. val rest = RestTemplate () val headers = HttpHeaders (). apply { set(AUTHORIZATION , auth) } val entity = HttpEntity(body , headers) val response = rest. postForObject ( url , entity , Response :: class.java) This gets boring very soon. 39
response = rest.withUser(auth) { postForObject( url , entity , Response :: class.java) } Make sure not to share instances of such a RestTemplate or store the authorization in a ThreadLocal! 41
of deeply nested structures Let’s create a request to create a contact: Main request object is a CreateContactRequest Consists of the property contact which holds a B2CContact instance Contact has a list of address ids 42
= B2CContact () contact.firstName = cmd.firstname contact.lastName = cmd.lastname contact.title = cmd.title contact.birthDate = cmd.birthday.toCalendar () contact.gender = cmd.gender.name.toGender () contact.email = cmd.email val addressIds = AddressIds () addressIds.addressId += cmd.addressId request.contact = contact The linear structure is not easy to grasp. Easy to forget to link an item. Can you spot the mistake? And that’s a trivial example... Can we do better? You guessed right: Let’s use apply! 43
was my workaround in Java: @Test fun email_adresses_can_not_be_used_twice () {} But Kotlin just allows methods with spaces in the name: @Test fun `email adresses can not be used twice `() {} Please, don’t use this in production code. ; ) 46
are good use case for multi line strings: val userJson = """ { "firstname ": "Max", "lastname ": "Mustermann", "roles ": [ "customer" ] } """ val user = ObjectMapper () .readValue(userJson , User :: class.java) Tip: You can even tell IntelliJ it’s a JSON string by prepending it with // language=JSON. This enables syntax checking and highlighting. 47
String , email: String ) = User(fname , lname , email) fun testUser () = testUser("max", "muster", "[email protected]") fun testUser(fname: String) = testUser(fname , "muster", "[email protected]") fun testUser(fname: String , lname: String) = testUser(fname , lname , "[email protected]") Often seen in Java codebases. The amount of methods explodes really fast and it’s hard to maintain. Get’s even more fun when argument types collide, how do you define a method to just set the lastname? 48
()). thenReturn("bar") Also has alternative methods for e.g. anyObject() which don’t cause problems in Kotlin (those methods return null and Mockito fails). 51
a little bit verbose: val service = Mockito.mock(Service :: class.java) Allows some more compact syntax leveraging reified generics val service = mock <Service >() or val service: Service = mock () Allows easier definition of method stubs within mock, check their wiki. 52
but JUnit won’t see the annotation on the property. Hence you have to add get: to specify the use-site target: @get:Rule val myRule = MyRule () The alternative is to use the @JvmField annotation. 53
Spring services: @SpringBootTest @RunWith(SpringRunner :: class) class MyIntegrationTest { @Autowired lateinit var myService: MyService } Spring will process those annotations and set your services. Use lateinit only for fields set by a framework. 54
changes (e.g. adding methods) to classes and dynamically reloads them Patches your JVM installation - make sure to use a dedicated installation Specify patched JVM in IntelliJ, run your project in debug mode, press Ctrl+F9 and changes will reload Not perfect but saves a lot of time 56
:) Part or full time From Student to Senior Developer Java knowledge, willing to learn Kotlin ;) Experience with frontend development is welcome Talk to me or contact me at [email protected] 58