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

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

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

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なりを付ける