NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

モバイルアプリ開発にKMP(Kotlin Multiplatform)を採用して工夫したこと

本記事は  モバイルアプリWeek  5日目の記事です。
📱  4日目  ▶▶ 本記事 ▶▶  6日目  📱

こんにちは岩崎です!「モバイルアプリWeek」の5日目の記事を書かせていただくことになりました。私は現在大規模モバイルアプリのリプレイスプロジェクトに参加しており、そのプロジェクトではKMPというSDKを採用し、Model層からの実装をOS間で共通化して開発しています。今回はKMPを採用してみて業務上工夫した点にフォーカスして紹介していきたいと思います!

レイヤードアーキテクチャの基本的な理解には、「モバイルアプリWeek」の2日目の記事がおすすめです。 tech.nri-net.com

はじめに

昨今のモバイルアプリ開発では、Android/iOS向けにそれぞれネイティブで開発するという典型的なスタイルと併せて、クロスプラットフォーム開発の導入を検討することがほとんどです。これは保守しなければいけないコードをできる限り少なくし、アプリのリリースサイクルを高速化すると共に、障害を作り込む余地を小さくするという恩恵を受けるためです。 私が参加しているプロジェクトでも、上記のような恩恵を得るためにKMPというクロスプラットフォーム開発のSDKを採用しています。KMPを採用したときにエンジニアリングの観点で発生する課題と対処法はちらほら記事を見かけますので、この記事では主に業務上の工夫点に絞って我々のチームで実施した内容を共有したいと思います。 注意ですが、KMPを採用した具体的な理由やFlutterなどの他SDKとの比較には言及しません。また、記載する内容には個人的な所感も含まれている場合があるため、予めご認識ください。

KMPとは

まずKMPがどういったコンセプトのクロスプラットフォーム開発手法なのかを理解しておきます。 KMP(Kotlin Multiplatform)とは、Android、iOS、Web、デスクトップアプリ向けに同じコードを共有できる仕組みを提供するSDKです。 KMM(Kotlin Multiplatform Mobile)という呼び方は、KMPのユースケースをモバイルに限定したときの呼び方になります(以下、KMMで統一します)。

Kotlin Multiplatform | Kotlin から引用

このようにKMMはあくまで同じコードを異なるプラットフォーム向けに共有することが目的なので、共通化する範囲は開発者が柔軟に決めることができます。モバイルに関して言えば、基本的にはUIは各プラットフォームで実装し、ビジネスロジックやOS間で共通のプレゼンテーションロジックをKMMで実装します。こうすることで、ネイティブと同じアプリパフォーマンスとAPIアクセスを維持しつつ、共通ロジックのワンソース化が実現できます。

アプリのアーキテクチャ例

KMMを採用して良かったこと

KMMを採用するとどんなメリットが出てくるのか、私がインパクトが大きいと感じたものを紹介します。

1. チームごとに関心を分離できる

KMMを採用してビジネスロジックを共通化するという方針から、チーム構成は各プラットフォームの実装を行うAndroidチーム/iOSチーム、共通モジュールを実装するSharedチームの3チーム構成となりました。このようなチーム構成となることでチームごとに仕様への関心が分離され、Androidチーム/iOSチームは画面仕様の理解とプレゼンテーションロジックの実装に集中でき、Sharedチームはドメイン/データの理解とビジネスロジックの実装に集中できるようになりました。結果的にエンジニア一人あたりに必要な実装のためのインプットコストが小さくなり、開発工程を効率化することができました。

2. プラットフォーム間の実装差異がなくなる

ビジネスロジックを共通化したことで、典型的なネイティブアプリ開発で発生しがちなプラットフォーム間の実装差異が大部分防止できるようになりました。従来、APIのリクエスト・レスポンスの取り扱いやデータの取り扱い、行動履歴のログ送信などは、正確で精緻な設計・開発・テストによってプラットフォーム間で実装差異がないことが保証されますが、それがアーキテクチャ上保証されるようになります。よって、アプリリリース後の予期せぬ実装差異に悩まされる心配が軽減します。

3. エンジニアを確保しやすい

世の中的にITエンジニアが不足していると言われており、主観ですがモバイルアプリエンジニアについてはそれが顕著です。その上でエンジニアというのは流動的であるため、特に大規模なプロジェクトであるほど必要なスキルセットにマッチするエンジニアが確保しやすいということは、安定した開発・保守体制を実現するために重要なファクターとなります。

KMMを採用する際に新たに必要になるスキルセットは多くありません。KMMではKotlinでロジックを記述していきますが、Kotlinという言語は他のオブジェクト指向言語を修得している人、特にネイティブアプリ開発でJavaやSwiftを触っている人であれば容易に修得可能だと思います。また、ビジネスロジックの実装にネイティブアプリ開発の知識はそこまで求められないので、アプリ開発経験の浅い人でもSharedチームとしては迎え入れることが可能です。一方で、細かなところを挙げるとビルドのためのGradleの知識やKMM対応ライブラリの知識、Model層の設計理解が求められますが、それらは学習しながらでも十分な速度で対応を進めることができます。 そのため、従来の採用要件をほとんど変えることなく採用活動を行えるので、エンジニアを確保しやすいということになります。

KMMを採用して工夫したこと

そんなKMMですが、採用したあとの業務においてはいくつか工夫しながら進めていかないと効率的でない側面が出てきます。 そうならないように我々のプロジェクトで実施した工夫点を紹介していきます。

1. Shared先行開発

Android/iOSのViewModelの実装は、Sharedの実装に依存して決まってくる部分が少なからずあります。仮に必要なビジネスロジックの実装が無いまま画面の実装を進めていくと、Sharedに依存している部分については想定で実装しておき、Sharedの実装が追いついてから本実装を行うスタイルになります。この場合、想定で実装しておいた内容とSharedが実装した内容に乖離があればあるほど手戻りが発生することになり、開発スピードを鈍化させます。また、Sharedに依存していない部分(View層)だけを実装していく方法も考えられますが、ユーザ操作に伴う画面変化などの動作確認ができずフラストレーションの溜まる進め方になってしまいます。

そこで、プロジェクトの開発スケジュールを決める際に最初からある程度Sharedが先行して開発を進めていくスケジュールとしました。こうすることで、開発工程での手戻りをある程度防止することができました。

2. UseCaseのIFレビュー

前述したように、基本的に各チームは各々の関心事を中心に仕様の理解を進め実装を行います。ただし、完全に分業して実装を進めていってしまうと、画面仕様を満たせるビジネスロジックになっていない箇所が出てきて後から修正が入る機会が多くなってしまい、これもまた開発スピードを鈍化させます。

そこで、Sharedチームの開発の進め方として、プレゼンテーション側(Android/iOS)とビジネスロジック側(Shared)のインタフェースを先に定義し各チームによるレビューを行うようにしました。本格的な実装を始める前に各チームの認識をすり合わせておくことで、実装完了後に修正が発生する機会を減らすことができました。

3. Shared内で完結する動作確認手段

SharedはビジネスロジックをUseCaseという単位で実装していきプレゼンテーション側に提供します。ここで、SharedがUseCaseの動作確認をしたいと思ったとき、該当のUseCaseを利用している画面が存在しないとアプリ操作による動作確認ができません。画面の実装は別チームに依存しているので、Sharedは画面が出来上がるまでアプリ上での動作確認を待つ必要があります。また、画面があったとしても毎回そこまで到達して確認するのは効率的ではありません。

そこで、Shared内にUseCaseを自由に叩けるデバッグ用ミニアプリを作成することにしました。そのアプリでは実装済みのUseCaseが一覧化されており、所定のInputでUseCaseを任意に実行し、そのOutputが表示されるようになっています。これにより、各UseCase単独の挙動やUseCase Aを叩いた後のUseCase Bの挙動を確認するといった動作確認を簡単に行うことができるようになりました。統一的な確認手段があることで、人によって動作確認の質に差が生じることがなくなり、開発スピードと品質の向上を実現することができました。

4. Sharedモジュールの改善

レイヤードアーキテクチャを採用しているので、Sharedの実装はプレゼンテーション側とのインタフェースに影響を与えなければ独自に改善を進めていくことが可能です。しかし、そのインタフェース部分も含めて改善したい場合、プレゼンテーション側にも影響を与えてしまうためSharedチームの一存だけで変更していくことができません。そのため、Sharedチームには改善のためのリソースがあったとしてもプレゼンテーション側にそのリソースが無ければ、インタフェース部分も含めた改善活動が行えないということになってしまいます。

そこで、改善対象のモジュールを非推奨にした上でそのまま残しておき、新たに改善した状態のモジュールを実装するという方針を立てました。こうすることで、プレゼンテーション側には一切影響を与えることなくSharedとして改善活動を継続することができ、プレゼンテーション側は改善のためのリソースがあるときにそれを取り込むことができるようになりました。

5. Sharedへの調査依頼

開発を進めていったりテスト工程で不具合が検知されたりしたときに、どうもあるUseCaseの挙動がおかしそうと目星がつくときがあります。この場合、アプリ操作により検知した不具合の一次調査はプレゼンテーション側で行うので、プレゼンテーション側からSharedに調査を引き継ぐことになります。プロジェクト初期のコミュニケーションにおいては、Sharedに調査を依頼する際に調査対象のUseCaseと発生事象をその人の言葉で説明して伝えていました。そのため、Sharedとしては説明内容を正確に理解するためのコミュニケーションコストが余分に発生している状況でした。

そこから、プロジェクト中期には調査対象のUseCaseと事象が再現するInputの具体的な値、そのときに返却されるOutputの具体的な値、期待値を整理して引き継ぐようになりました。こうすることでSharedとしてはすぐに事象の再現と確認ができるようになり、コミュニケーションが円滑になると同時に課題を解決する速度も向上しました。

おわり

個人的な所感になりますが、KMMを採用することによるデメリットはほぼ無いので、ネイティブアプリ開発では積極的に導入しても良いと考えています。iOS向けには細かなトラブルシューティングが必要になる場合がありますが、今のところ大きな問題は感じていません。

さて、今回は主にKMMを担うSharedに関連した工夫点について紹介させて頂きました。このように工夫しながら開発を進めていくことでKMMのメリットを最大化していけると思います。 この記事がKMMの採用を検討している、あるいは既に導入している方の参考になれば幸いです。

執筆者岩崎 聖夜

モバイルアプリ開発者です。AWSでデータ分析基盤を作ったりもしています。