Slide 1

Slide 1 text

Ubieにおける サーバサイドKotlin 活用事例 2021-02-18 LINE Developer Meetup #70 長澤太郎

Slide 2

Slide 2 text

長澤 太郎 ● @ngsw_taro ● Ubie株式会社 ● 日本Kotlinユーザグループ代表

Slide 3

Slide 3 text

Ubie(ユビー)

Slide 4

Slide 4 text

もくじ 1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 5

Slide 5 text

1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 6

Slide 6 text

Kotlinは元々 Better Java として産声を上げた! ● 発表自体は2011年、実装のリリースは2012年 ● 当時はJava 7でLambdaやStream APIがなかった ● 特にAndroid利用で強みを見せた

Slide 7

Slide 7 text

いまやJava 15, 16 ● KotlinとJavaの文法における差が縮まってきた ● それでもKotlinにしかないもの ○ 名前付き引数 ○ 拡張関数 ○ suspend関数 ○ プロパティ ○ null安全 ○ 型エイリアス (etc…) ● Kotlinエコシステムも充実してきた

Slide 8

Slide 8 text

UbieでサーバサイドKotlinを導入した経緯 ● 2018年、太郎 Ubie入社、6人目の社員 ● 当時 Web APIはRuby on Railsアプリ1個だけ ● 新たな機能を追加するにあたってKotlinでWeb APIを開発 ● 理由: ○ 複雑な医療データを扱う上で静的型を欲した ○ 僕自身、手の馴染む道具だった(Javaの利用経験もあった) ● 大原則として、合理で判断。Kotlinにこだわりはない。

Slide 9

Slide 9 text

1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 10

Slide 10 text

Spring Boot ● Javaでも大人気のフレームワーク ● 気軽にさくっと始められる Languageから Kotlinを選んで プロジェクトの雛形を ダウンロード

Slide 11

Slide 11 text

KotlinでさくっとHelloWorld @SpringBootApplication class DemoApplication fun main(args: Array) { runApplication(*args) } @Service class HelloWorldService { fun helloWorld(): String = "Hello, world!" } @RestController class HelloWorldController(val helloWorldService: HelloWorldService) { @GetMapping("/hello-world") fun helloWorld(): String = helloWorldService.helloWorld() }

Slide 12

Slide 12 text

なんだかんだ言ってSpring Bootは楽 ● アノテーションでDI ● アノテーションでルーティング ● YAMLファイルに設定値

Slide 13

Slide 13 text

Kotlinってデフォルトでfinalクラスだよね ● Kotlinのクラスはデフォルトでfinalクラス(継承ダメ) ● open修飾子で継承を明示的に許可する ● @Serviceなどが付いたクラスはSpringによってサブクラスが生 成される(継承を許可する必要がある) plugins { ... kotlin("plugin.spring") version "1.4.21" } プラグインがうまいことやってくれるから 意識する必要ないのです!

Slide 14

Slide 14 text

WebFlux: Reactorごりごり使うスタイル @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = Mono.zip( demoService.getMonoA(), demoService.getMonoB() ) .flatMap { demoService.getMonoC(it.t1, it.t2) } .map { it.answer } }

Slide 15

Slide 15 text

WebFlux: async/awaitでやってMonoに変換 @RestController class DemoController(val demoService: DemoService) { @GetMapping("demo") fun handle(): Mono = mono { val a = async { demoService.getItemA() } val b = async { demoService.getItemB() } val c = async { demoService.getItemC(a.await(), b.await()) } c.await().answer } }

Slide 16

Slide 16 text

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 } }

Slide 17

Slide 17 text

Ktorはどう? ● JetBrains公式のWebマイクロフレームワーク ○ 非常に薄い ○ DSL、ノンブロッキングなどの特徴 ● だからこその苦労はありそう→考えることたくさん ○ DIどうする…? ○ DBアクセスは…? ○ コントローラとか、ファイル分割、アーキテクチャ ○ やりたいことが明確に決まってるならSpring Bootでよさそう ● UbieではPHR基盤プロジェクトでKtor採用を真面目に検討し ていた。が、断念。

Slide 18

Slide 18 text

1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 19

Slide 19 text

バリデーションに注意 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 )

Slide 20

Slide 20 text

Kotlin DSLでBean登録 @SpringBootApplication class DemoApplication fun main(args: Array) { SpringApplicationBuilder() .sources(DemoApplication::class.java) .initializers(beans { bean { HelloWorldService() } }) .run(*args) } class HelloWorldService { fun helloWorld(): String = "Hello, world!" }

Slide 21

Slide 21 text

ルーティングもDSLに! fun main(args: Array) { SpringApplicationBuilder() .sources(DemoApplication::class.java) .initializers(beans { bean { HelloWorldService() } bean { HelloWorldController(ref()) } bean { router { GET("/hello-world") { ref().helloWorld() } } } }) .run(*args)

Slide 22

Slide 22 text

DBアクセスはJdbcTemplateで ● 我々はSQLを知っている(学習コスト低でとっつきやすい) ● 重厚なORMのAPIはおろか、癖や生成されるSQLを気にする 必要はない ● やりたいことをスッとできる、痒いところに手が届く ● 記述すべきコードは増えるけど脳みそ使わず筋肉使う

Slide 23

Slide 23 text

Ubieのコーディングガイドライン公開中 https://github.com/ubie-inc/kotlin-coding-style

Slide 24

Slide 24 text

Ubieコーディングガイドラインのポイント ● スタイルはktlintに従う ● シンプルに保つ ○ Kotlinは表現力が高い分、エレガントの極みを目指すと可読性が下がって本末転 倒になるおそれが ● テストコードは素直に ○ テストに必要なものが、一目ですべて手に入る世界がいいな ● 型を明記する ● !!を使用しない ○ 代わりにrequireNotNull関数を使用する ● lateinitは使用しない ○ 本来NullableなのにNotNullにしたいがための抜け道 ○ テストコードの@Autowiredなプロパティにはlateinitを使用

Slide 25

Slide 25 text

1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 26

Slide 26 text

Ubieのアーキテクチャ(一部) 患者用問診タ ブレット 医師用 デスクトップ

Slide 27

Slide 27 text

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 { return drugService.getDrugs(yjCode) } } しらじさんによるサンプルコード https://github.com/ubie-inc/kotlin-graphql-sample

Slide 28

Slide 28 text

GraphQLやるときの登場人物 ● QueryResolver: API、問い合わせの入口に相当 ● Resolver: ネストで取得したいリソースを指定したとき ● DataLoader: N + 1を回避するための遅延ロード機構 フレームワークが提供するインタフェースを実装し @Componentなりを付ける

Slide 29

Slide 29 text

1. なぜKotlinなのか 2. Spring Boot いいかんじ 3. Spring Boot あれこれ 4. GraphQL 少しだけ使ってる

Slide 30

Slide 30 text

まとめ ● KotlinはBetter Javaとして有用 ● SpringとKotlinは仲良し!多くの場面で自然に使える ● Ktorは薄いがゆえに大変そうか?個人的な見解 ● GraphQLもKotlin x SpringでOK サーバサイドKotlin いいよ!