DMM.comの、一番深くておもしろいトコロ。

DMM TVアプリ開発で使用したKotlin Multiplatform Mobile(KMM) について

DMM TVアプリ開発で使用したKotlin Multiplatform Mobile(KMM) について

  • このエントリーをはてなブックマークに追加

はじめに

この記事は DMMグループ Advent Calendar 2022 の21日目の記事です。

こんにちはAndroid版 DMM TVアプリ ブックフロアの開発を担当した森です。
本日はDMM TVアプリ開発で使用したKotlin Multiplatform Mobile(KMM)についてご紹介できればと思います。

DMM TVアプリ アーキテクチャ

DMM TVアプリの開発ではKotlin Multiplatform Mobile(KMM)を使用してiOS・Android共に共通のドメイン層を利用しています。

DMM TVアプリ アーキテクチャ図

UI層はiOS・Androidで別々に用意してNativeClientとし、ViewModel/Presenterから利用するUseCaseやRepository、また取得してきたDomainModel(ドメインモデル)を共通化することで、ドメイン層の開発コストを削減しています。 

KMM利用時のメリット

開発コストの削減

ドメイン層をOS間で共通化することで単純に開発コストが下がりました。

特に初期段階の開発だけでなく、その後のドメイン部分の変更やパフォーマンス改善を実施する際にも開発コストが下がるのでiOS・Android共にUI層の作り込みに時間をかけれた印象があります。

OS間の仕様差分の低減

ドメインロジックをOS間で共通化することでOS間の仕様差分を減らすことができました。

例えばiOS・Androidで共通のロジックで表示されるキャンペーン表示文言などをドメインモデルからプロパティとして公開することで、共通の表示仕様にすることができました。

共通ロジックによるプロパティ公開

val campaignText = when {
    // 割引キャンペーンと還元キャンペーン両方あり
    hasDiscount && hasPointRefund -> {
      when (maxDiscountRate) {
        100 -> discountCampaignText // 100%割引の場合は還元キャンペーンは表示しない
        else -> "$discountCampaignText & $pointCampaignText"
      }
    }
    // 割引キャンペーンのみ
    hasDiscount -> {
      discountCampaignText
    }
    // 還元キャンペーンのみ
    hasPointRefund -> {
      pointCampaignText
    }
    else -> {
      null
    }
  }

共通ロジックで表示されるキャンペーン

共通ロジックで表示されるキャンペーン

KMM利用時のデメリット

iOSエンジニアのKotlin学習コスト

開発初期段階では特にiOSエンジニアの方がKotlinになれておらず、書き方に迷うことがある印象を受けました。

ドメイン部分修正時に各OSでUI層の修正が必要になる

各OSで共通のプロパティに依存しているため、ドメイン層に変更があった時に両OSでUI層の対応をしないと片方のビルドが通らなくなり、開発ブランチから最新の差分を取得してくるとビルドできないという問題が頻発しました。

ドメイン部分修正時に各OSでUI層の修正が必要になる

片方のOSでビルドが通らない課題の解決策

PR自動ラベリングとDanger自動指摘の導入

本来であればCIでビルドできるか自動でチェックできるのが好ましいのですが、現状のプロジェクト構成だと全OSでのビルド完了に1時間以上かかってしまうため、変更のあったパッケージ毎にGithub Actionsのlabeler を使用して自動ラベリングすると共にDangerによる自動指摘を導入しました。

自動ラベリング 設定例

Android:
- android/**/*

iOS:
- ios/**/*

KMP:
- native-shared/**/*

Dangerfile 設定例

added = git.added_files.include? "native-shared/**"
deleted = git.deleted_files.include? "native-shared/**"
modified = git.modified_files.include? "native-shared/**"
renamed = git.renamed_files.include? "native-shared/**"

warn("各OS共通部分に変更が入っています。Android,iOSでビルドが問題ないか確認してください。") if added || deleted || modified || renamed

Dangerによる自動指摘の様子

Dangerによる自動指摘の様子

PR運用フローによる改善

さらに私達のチームでは PullRequest(PR)運用フローを工夫して開発ブランチへのマージ前に両OSでビルドが通る状態になるようにしました。

  1. Androidエンジニアがドメイン層に変更を入れた場合、ドメイン層の修正とAndroid側のUI層の修正が完了したら、その時点でのPRを作成しiOSエンジニアに共有します。
  2. iOSエンジニアはドメイン層のレビューを行うと共にiOSのUI層の修正を追加コミットを行いiOS側もビルドできるようにします。
  3. 上記PRをiOSエンジニア、Androidエンジニアそれぞれからレビューを頂き、共にApproveをもらったらマージをする。

Androidエンジニアがドメイン層に変更を入れた場合のPR運用フロー

Androidエンジニアがドメイン層に変更を入れた場合のPR運用フロー

結果

ビルドが通らない問題が起こる頻度は劇的に下がりました。

またiOSエンジニアとAndroidエンジニアが仕様変更があった際に互いにキャッチアップができるというメリットも生まれました。

おわりに

今回はじめて実務でKMMを利用しましたが、iOS・Androidでほとんど共通の仕様でプロダクトを開発するモバイルアプリ開発では非常に強力なソリューションであると改めて実感しました。

自身はAndroidエンジニアだったため、Kotlinに慣れているというのもありますが、非常に開発はしやすかったイメージです。

特に複雑なロジックが必要になるドメインロジックの実装や、パフォーマンスを改善するための修正をした際に、Android側だけでなくiOS側にも貢献できるというのがとても開発者体験を向上させると思いました。

今後も新規プロダクト開発では積極的に利用していきたいと思いました。

宣伝

マンガも買える、読める DMM TVをよろしくお願いたします。 tv.dmm.com

あとがき

今回導入を先導してくださった @kgmyshinさん @cantabile_hisaさん ありがとうございました。
また様々な改善を提案、導入してくださった @cantabile_hisaさん @roana0229さん 本当にありがとうございました。