Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KotlinとSpring BootとDoma2でAPIサーバーを作る #m3kt

Hidenori Maehara
September 28, 2017

KotlinとSpring BootとDoma2でAPIサーバーを作る #m3kt

どこでもKotlin #2 サーバーサイドKotlin特集での発表資料です
サンプルコードgithubにあげてます
https://github.com/maeharin/kotlin-dvd-rental

Hidenori Maehara

September 28, 2017
Tweet

More Decks by Hidenori Maehara

Other Decks in Technology

Transcript

  1. サンプルコード、githubにあげてます https://github.com/maeharin/kotlin-dvd-rental • Kotlin x Spring Boot x Doma2で作ったAPIサーバーのサンプル •

    DBにPostgreSQL、検索にElasticsearch(Dockerで起動) • Postgresql tutorialのDVD Rentalサンプルデータベースを利用
  2. 自己紹介 • 前原 秀徳 • @maeharin(まえはりん) • エムスリー株式会社 エンジニア •

    チームリーダー、グループ会社取締役等を歴任 • 自慢:ブログ記事が、はてぶ1200 ◦http://maeharin.hatenablog.com/
  3. 2) KotlinでRestControllerを作成 package com.example.demo.controller import org.springframework.web.bind.annotation.* @RestController class HelloController {

    @GetMapping fun index():String = "hello!" } src/main/kotlin/com/example/demo/controller/HelloController.kt Kotlinで書く
  4. kotli-springプラグインでopen不要に classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") apply plugin: 'kotlin' apply plugin: 'kotlin-spring' compileKotlin

    { kotlinOptions.jvmTarget = "1.8" } compileTestKotlin { kotlinOptions.jvmTarget = "1.8" }lin-reflect:${kotlinVersion}") これのお陰でopen不要に ※Spring Initializr経由で作成する とデフォルトで入る ↓Spring Initializr経由で作成したbuild.gradle(抜粋)
  5. controller @RestController @RequestMapping("/api/v1/films") class FilmRestController( private val filmRepository: FilmDomaRepository )

    { @GetMapping fun index(): List<FilmResource> = ... @PostMapping fun create( @RequestBody @Validated filmParam: FilmParam ): Int { open不要 springの アノテーションは普 通に使える
  6. @Service class FilmApplicationService( private val filmRepository: FilmDomaRepository ) { @Transactional

    fun create(command: FilmCommand): Int { @Repository class FilmDomaRepository( private val filmDao: FilmDao ) { DI
  7. @Configuration class ObjectMapperConfig { @Bean fun objectMapper(): ObjectMapper = ObjectMapper()

    .registerModule(JavaTimeModule()) .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .registerModule(KotlinModule()) } Config
  8. SpringSecurity @Configuration @EnableAuthorizationServer class Oauth2AuthorizationServerConfig( private val authManager: AuthenticationManager, private

    val dataSource: DataSource ) : AuthorizationServerConfigurerAdapter() { @Bean fun passwordEncoder(): BCryptPasswordEncoder = BCryptPasswordEncoder() Spring周辺の ライブラリも 問題なく使える
  9. SpringFox(Swagger) @Configuration @EnableSwagger2 class SwaggerConfig { @Bean fun apiV1Document(): Docket

    = Docket(DocumentationType.SWAGGER_2) .groupName("api v1") .select() .paths(PathSelectors.ant("/api/v1/**")) .build() }
  10. data class FilmResource( var name: String = "", var isAdmin:

    Boolean = false, var companyId: Int = 0 ) APIへのリクエスト(Json)をKotlinへマッピング Jacksonは デフォルト値ありの コンストラクタを要 求 デフォルト値は使い たくないのに。。
  11. val source = FilmSource(film) val json = objectMapper.writeValueAsString(source) val indexRequest

    = IndexRequest(INDEX, TYPE, id) .source(json, XContentType.JSON) ElasticSearchとJsonやり取りする時も嬉しい data class => json
  12. searchResponse.hits.map { hit -> val json = hit.sourceAsString val filmSource:

    FilmSource = objectMapper.readValue(json) } ElasticSearchとJsonやり取りする時も嬉しい json => data class
  13. Javaの部分: DomaのDao @ConfigAutowireable @Dao public interface FilmEntityDao { @Select FilmEntity

    selectById(Integer filmId); @Select List<FilmEntity> selectAll(); @Insert int insert(FilmEntity entity);
  14. Javaの部分: DomaのEntity @Entity(naming = NamingType.SNAKE_LOWER_CASE) @Table(name = "film") public class

    FilmEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @SequenceGenerator(sequence = "film_film_id_seq") public Integer filmId; public String title; public String description; SQL実行結果のマッピ ングと捉え、ロジックを 書かないようにした
  15. Kotlinの部分: Repository @Repository class FilmDomaRepository( ... ) { fun findAll():

    List<Film> { val entities = filmDao.selectAll() return Film.createByFilmEntities(entities) } Java(DomaのEntity)からKotlin(Model, ValueObject)に マッピング
  16. Kotlinの部分: Model, ValueObject class Film( val id: Int? = null,

    val title: String, val description: String?, val language: Language, val actors: List<Actor>, val categories: List<Category> ) { ...
  17. Kotlinの部分: Service @Service class FilmApplicationService( ... ) { @Transactional fun

    create(command: FilmCommand): Int { val film = Film( command = command, language = langRepo.findById(command.languageId) ?: throw NotFoundException(), actors = actorRepo.findByIds(command.actorIds), categories = categoryRepo.findByIds(command.categoryIds) ) val filmId = filmRepo.store(film) mailService.sendCreated(film) return filmId }
  18. まとめ • Kotlin x Spring BootでAPIサーバーは特に問題なく作れる • kotlin-springプラグインのおかげでopen不要 • jackson-module-kotlinのおかげでdata

    classとのマッピングが楽に • doma2はKotlinを実験的にサポート ◦domaのentityをKotlinにできる。だが、kaptの不安定な挙動リスクあり • 私達はdoma2の層をjavaにした ◦ドメイン層、アプリケーション層はKotlinなのでほぼKotlin • Kotlinかわいい(^ω^)ペロペロ