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

UbieにおけるサーバサイドKotlin活用事例

Avatar for Taro Nagasawa Taro Nagasawa
February 18, 2021

 UbieにおけるサーバサイドKotlin活用事例

Avatar for Taro Nagasawa

Taro Nagasawa

February 18, 2021
Tweet

More Decks by Taro Nagasawa

Other Decks in Programming

Transcript

  1. いまやJava 15, 16 • KotlinとJavaの文法における差が縮まってきた • それでもKotlinにしかないもの ◦ 名前付き引数 ◦

    拡張関数 ◦ suspend関数 ◦ プロパティ ◦ null安全 ◦ 型エイリアス (etc…) • Kotlinエコシステムも充実してきた
  2. UbieでサーバサイドKotlinを導入した経緯 • 2018年、太郎 Ubie入社、6人目の社員 • 当時 Web APIはRuby on Railsアプリ1個だけ

    • 新たな機能を追加するにあたってKotlinでWeb APIを開発 • 理由: ◦ 複雑な医療データを扱う上で静的型を欲した ◦ 僕自身、手の馴染む道具だった(Javaの利用経験もあった) • 大原則として、合理で判断。Kotlinにこだわりはない。
  3. KotlinでさくっとHelloWorld @SpringBootApplication class DemoApplication fun main(args: Array<String>) { runApplication<DemoApplication>(*args) }

    @Service class HelloWorldService { fun helloWorld(): String = "Hello, world!" } @RestController class HelloWorldController(val helloWorldService: HelloWorldService) { @GetMapping("/hello-world") fun helloWorld(): String = helloWorldService.helloWorld() }
  4. WebFlux: Reactorごりごり使うスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun

    handle(): Mono<String> = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } }
  5. WebFlux: async/awaitでやってMonoに変換 @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun

    handle(): Mono<String> = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } }
  6. WebFlux: suspend関数で素直に @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") suspend

    fun handle(): String { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } return c.await().answer } }
  7. Ktorはどう? • JetBrains公式のWebマイクロフレームワーク ◦ 非常に薄い ◦ DSL、ノンブロッキングなどの特徴 • だからこその苦労はありそう→考えることたくさん ◦

    DIどうする…? ◦ DBアクセスは…? ◦ コントローラとか、ファイル分割、アーキテクチャ ◦ やりたいことが明確に決まってるならSpring Bootでよさそう • UbieではPHR基盤プロジェクトでKtor採用を真面目に検討し ていた。が、断念。
  8. バリデーションに注意 class PatientPostBody ( @NotNull val age: Int, @NotBlank val

    name: String ) class PatientPostBody ( @field:NotNull val age: Int, @field:NotBlank val name: String ) class PatientPostBody ( @field:NotNull val age: Int?, @field:NotBlank val name: String )
  9. Kotlin DSLでBean登録 @SpringBootApplication class DemoApplication fun main(args: Array<String>) { SpringApplicationBuilder()

    .sources(DemoApplication::class.java) .initializers(beans { bean { HelloWorldService() } }) .run(*args) } class HelloWorldService { fun helloWorld(): String = "Hello, world!" }
  10. ルーティングもDSLに! fun main(args: Array<String>) { SpringApplicationBuilder() .sources(DemoApplication::class.java) .initializers(beans { bean

    { HelloWorldService() } bean { HelloWorldController(ref()) } bean { router { GET("/hello-world") { ref<HelloWorldController>().helloWorld() } } } }) .run(*args)
  11. Ubieコーディングガイドラインのポイント • スタイルはktlintに従う • シンプルに保つ ◦ Kotlinは表現力が高い分、エレガントの極みを目指すと可読性が下がって本末転 倒になるおそれが • テストコードは素直に

    ◦ テストに必要なものが、一目ですべて手に入る世界がいいな • 型を明記する • !!を使用しない ◦ 代わりにrequireNotNull関数を使用する • lateinitは使用しない ◦ 本来NullableなのにNotNullにしたいがための抜け道 ◦ テストコードの@Autowiredなプロパティにはlateinitを使用
  12. Kotlin x Spring Boot x GraphQL type Query { drugs(yjCode:

    String!) : [Drug!] } type Drug { yjCode: String! name: String! } @Component class DrugQueryResolver( val drugService: DrugService ): GraphQLQueryResolver { fun drugs(yjCode: String): List<Drug> { return drugService.getDrugs(yjCode) } } しらじさんによるサンプルコード https://github.com/ubie-inc/kotlin-graphql-sample
  13. GraphQLやるときの登場人物 • QueryResolver: API、問い合わせの入口に相当 • Resolver: ネストで取得したいリソースを指定したとき • DataLoader: N

    + 1を回避するための遅延ロード機構 フレームワークが提供するインタフェースを実装し @Componentなりを付ける