エムスリーでのKotlinへの取り組み / jjug-ccc-2018fall-kotlin-in-m3

F33a4157978cd59d5417ae69b0265943?s=47 M3 Engineering
December 15, 2018

エムスリーでのKotlinへの取り組み / jjug-ccc-2018fall-kotlin-in-m3

JJUG CCC 2018 Fallで発表した「エムスリーでのKotlinへの取り組み」の資料です。

F33a4157978cd59d5417ae69b0265943?s=128

M3 Engineering

December 15, 2018
Tweet

Transcript

  1. 1.

    Copyright © 2018 M3, Inc. All Rights Reserved エムスリー Kotlinへ

    取り組み エムスリー株式会社 滝安純平 星川貴樹 @JJUG CCC 2018 Fall #jjug_ccc #ccc_a4
  2. 4.

    m3.comはこんなサービス 医師 主 無料 コンテンツを提供 • 医療関連ニュース • 薬剤情報 •

    講演会動画 仕事 合間 手軽 情報収集 きる 製薬会社等 有償 サービスを提供 • 医師へ 薬剤情報・講演 会動画 発信 • 医師向けアンケート(ニー ズ把握) 低コスト 医師 営業活動が きる
  3. 8.

    社内エンジニアチームの紹介 • エンジニアは事業部門ごとのチームに所属 • いくつかの横断チームもある 事業 部門 ご 横断 製薬

    プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験 コンシュー マ モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA
  4. 9.

    Kotlinを使ってるチーム サーバーサイド・モバイルアプリ開発によらず使われている 製薬 プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験

    コンシュー マ 事業 部門 ご 横断 モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA
  5. 10.

    製薬 プロモ 新領域 サイト プロモ 転職事業 リサーチ 治験 コンシュー マ

    モバイル アプリ SRE セキュリティ AI機械学習 基盤 電子 カルテ QA Kotlinを選んだ主な理由 事業 部門 ご 横断 Androidアプリ開発メンバが サーバーサイドを作る Javaエンジニアが多いチーム BetterJava し エンジニア し 新しいも 挑戦したい 他 プロジェクト も使 いた ため
  6. 13.

    サーバーサイドいろいろ • JavaからKotlinに移行する方法 ◦ いろいろな移行方法 ◦ 移行時に気をつけること ◦ リグレッションテストで品質を担保 •

    サーバーサイドKotlinで採用している要素技術紹介 ◦ SpringBoot ◦ Swagger ◦ GraphQL エムスリー あ た実際 りくみから、そ ノウハウを紹介します!
  7. 22.

    Golden file testingとは APIのレスポンスとGolden fileを比較して、 レスポンスが変わらないことを確認する テスト対象: Application層 ハンドラ テストコード

    レスポンスを検査 Golden file (=JSON) Response Object Request Object 自動生成 全テスト共通 ここだけ、書く必要がある DB Mock データ
  8. 23.

    fun assertEqualOrSaveJsonFile( actual: Any, javaClass: Class<Any>, fileName: String ) {

    val javaClassPath = javaClass.name.replace(".", "/") val path = "src/test/resources/testdata/$javaClassPath/$fileName.json" val expected = TestForApplication::class.java.getResource(path)?.readText() if (expected == null) { saveToJsonFile(path, actual) } else { assertEqual(expected, actual) } } Golden fileを生成するコード Golden fileが • なかったら そのままJSONを書き込んで、例外投げる • あったら ファイルの中身とJSONを比較 javaClass ディレクトリ 構成も自動 決める
  9. 24.

    ふつうのテストとの違い リグレッションテストに向いている よくあるケース Golden file testing ふ う テスト テストを増やそう

    普通 ファイル生成 assert地獄、あきらめる 漏れる 出力が変わる変更をした 消し 、普通 ファイル生成 assertを おし いく地獄 巨大 出力を扱う 普通 ファイル生成 Method code too large! JSON文字列をチェック 普通 ファイル生成 \”エスケープが必要\” テスト 仕様を明示したいケース 向い い い 、使い分け 大事 日付や、ランダム 生成し いる IDが含まれるよう 場合 、モック等 対応が必要
  10. 29.

    Swagger Codegen, UI (OpenAPI) Kotlin+SpringBootでサーバサイドを作ったら • クライアントライブラリは自動生成できる(Codegen) • ドキュメントも自動生成できる(UI) Swagger

    Codegen APIクライアントgem 自動生成 #m3kt https://speakerdeck.com/juntaki/swagger-codegende-apikuraiantogem-zi-dong-sheng-cheng-number-m3kt 生成されたドキュメント
  11. 30.

    GraphQL Swagger(OpenAPI)と似たメリットがある 新サービスで導入して、ノウハウを蓄積中 • クライアントの自動生成 • ドキュメントの管理を実装と差分無く • スキーマファーストな開発 •

    複数のリクエストをまとめる フロントエンド向けの API サーバリニューアルに GraphQL を検討している話 https://www.m3tech.blog/entry/graphql-on-spring-boot-with-kotlin
  12. 32.

    Androidいろいろ • チームの紹介 ◦ メンバー構成 ◦ 導入の経緯 ◦ 社内制度 •

    Android周りのKotlin導入時の懸念点 ◦ Optional or Nullable ◦ Lombok or data class • チームメンバーへの浸透
  13. 34.

    弊社Androidアプリ MR君アプリ • 弊社で最初に作られたMR君と いうサービス専用のアプリ • 製薬プロモーション専用 • その分m3.comアプリにはな い機能がある

    m3.comアプリ • 現在の主要アプリ • MR君を含む主要なサービスが 1まとめになっているアプリ + 未公開新規アプリ 2
  14. 36.

    OKRについて • “Objectives and Key Results” • Intel発祥、Googleでも1999年から利用 • 「目的」を設定し、それを達成するための「目標」を

    定義する ◦ Objectiveは必ずしも定量的で無くても良い ◦ Key Resultは定量的で、客観的に判断できる • (大きな目標) • 弊社の場合 ◦ 2013/11からやっている ◦ チームごとに目標設定 ◦ 目標はリファクタや新規プロダクトなど、短期的にビジネスに 繋がらなくても良い
  15. 37.

    2018年現在のAndroid×Kotlin • Google公式サポート (2017~) • 活発なライブラリはほとんどKotlin対応されている • Android公式ドキュメントもKotlin, Javaが併記さ れているものが増えている

    • Android OSのフレームワーク自体はJavaで書かれて いるが、Android 9から@NotNull, @Nullableアノ テーションがついたことでよりKotlinフレンドリーに • 勉強会やブログ記事でもKotlinが主流
  16. 40.

    弊社アプリのKotlin対応の歴史 2016/1 2017/5 2018/6 Kotlinで フルリニューアル 徐々に書き換え 2017/5/18 Google I/O

    2017で AndroidのKotlin公式 サポート発表 初めから Kotlinで 新規 アプリ Java Kotlin
  17. 47.

    Optional in Kotlin • GuavaのOptionalは正直使い勝手が悪い ◦ nullになるはずのない部分が@Nullableになっていたり ◦ ラムダを渡してもinline展開されなかったり ◦

    代替案としてはArrowというFPライブラリののOptionがある • orNull()を呼べば、Kotlin上ではNullable型とし て処理可能 // Optionalのまま処理 val text = intOptional .transform { it!!.toString() } // なぜかNullable .or("") // Nullableに変換 val text = intOptional.orNull()?.toString() ?: ""
  18. 48.

    Optional or Nullable チーム内コーディング規約 • Javaから呼び出されるKotlinクラスのpublicメソッ ドはOptionalでやりとりする • Kotlin内で閉じている場合はNullableにしてOK •

    RxJavaではnullを流せないので、どうしても使う場 合のみOptional ◦ Single<Optional<T>>はケースによってはMaybe<T>に ◦ コルーチンはJava側から触れないので
  19. 49.

    Lombok or data class • JavaではLombokを使用していた • Kotlinにはdata classという、自動でequals, hashCodeメソッドを作ってくれる機能がある

    data class User( val name: String, val sex: Sex, val age: Int, val prefectureCode: Int ) @Value @Builder @Wither public class User { @NonNull String name; @NonNull Sex sex; int age; int prefectureCode; }
  20. 50.

    Lombok or data class • Kotlinでは、Builderパターンなどの代わりに名前付 き引数を用いる User.builder() .name("oboenikui") .sex(Sex.MAN)

    .age(25) .prefectureCode(12) .build(); val user = User( name = "oboenikui", sex = Sex.MAN, age = 25, prefectureCode = 12 )
  21. 52.

    Javaからdata classの呼び出し val user = User( name = "oboenikui", sex

    = Sex.MAN, age = 25, prefectureCode = 12 ) User user = new User( "Takaki Hoshikawa", Sex.MAN, 25, 12 ); 年齢? 県コード? ビルダーや名前付き引数の機能がないので コンストラクタなどに渡す変数がわかりにくい
  22. 53.

    Javaからdata classの呼び出し user.copy ( age = user.age + 1 )

    user.copy( user.getName(), user.getSex(), user.getAge() + 1, user.getPrefectureCode() ); 名前付き引数がないので copyメソッドが使いにくい
  23. 54.

    KotlinからLombokの呼び出し User.builder() .name("oboenikui") .sex(Sex.MAN) .age(25) .prefectureCode(12) .build() User.builder() .name("oboenikui") .sex(Sex.MAN)

    .age(25) .prefectureCode(12) .build(); ERROR LombokとKotlinは同じモジュール内で共存できない (モジュールを分けたりDelombokする必要がある)
  24. 55.

    その他 User.builder() // .name("oboenikui") .sex(Sex.MAN) .age(25) .prefectureCode(12) .build(); val user

    = User( // name = "oboenikui", sex = Sex.MAN, age = 25, prefectureCode = 12 ) 引数が足りないので シンタックスエラー build()実行時にエラー Builderパターンはdata classに比べて厳密にならない
  25. 56.

    data classとLombokの使用方針 • 他のクラスより慎重にKotlin化する ◦ 多くの箇所で生成するものは後回しにする ◦ data class化したら、使っているクラスも合わせてKotlin 化

    ◦ 利用するwitherのみ定義する ◦ JavaのUTで使う場合はコメントを入れる data class User( /* 略 */ ) { fun withAge(age: Int): User { return copy(age = age) } }
  26. 57.

    data classとLombokの使用方針 • 他のクラスより慎重にKotlin化する ◦ 多くの箇所で生成するものは後回しにする ◦ data class化したら、使っているクラスも合わせてKotlin 化

    ◦ 利用するwitherのみ定義する ◦ JavaのUTで使う場合はコメントを入れる User testUser = new User( "Takaki Hoshikawa", // name Sex.MAN, // sex 25, // age 12 // prefectureCode );
  27. 59.

    data classとLombokの使用方針 • Lombokを使うモジュールを隔離 / Delombok ◦ マルチモジュール化でビルド速度改善も期待できる ◦ Lombokクラスからはdata

    classを利用できない Delombok @Value @Builder @Wither public class User { @NonNull String name; @NonNull Sex sex; int age; int prefectureCode; } public class User { /* 略 */ public String getName() { return name; } /* 略 */ }
  28. 62.

    コーディング規約・Lint • 著名なスタイルガイド2種を踏襲 ◦ JetBrainsスタイルガイド ◦ Googleスタイルガイド • Lintでスタイルガイドに準拠しているかチェック ◦

    ktlint ◦ detekt • IntelliJ IDEAのインスペクタ・フォーマッタも Kotlin 1.3からスタイルガイドに準拠できるように • スタイルガイドにないものはゆるい規約として社内 Wikiで共有
  29. 63.

    Android Unit Test • JUnit4 + Robolectric + AssertJ +

    MockK • バッククォートで囲むことで可読性向上 @Test fun `getFoo should return foo bar`() { /* Test code */ } @Test public void getFoo_should_return_foo_bar() { /* Test code */ }
  30. 67.

    スポンサー活動 • JJUG CCC ◦ ゴールドスポンサー ◦ コーヒースポンサー ◦ ブーススポンサー

    ← NOW!! • Kotlin Fest • ScalaMatsuri • Ruby Kaigi • Vue Fes Japan • DroidKaigi 2019 • try! Swift Tokyo 2019 JJUG CCC 2017 Springのコップ
  31. 69.

    We are hiring! • グローバルで働いてみたいエンジニア ◦ US出張やってます! • Androidエンジニア ◦

    新規アプリはGraphQLを使って開発中! • サーバーサイドエンジニア ◦ Spring Boot + Kotlin + GraphQL ◦ Java, Scalaエンジニアも募集中! https://bit.ly/ccc2018m3