はじめに
私は普段、個人開発でAndroidアプリの作成を楽しんでおりますが、昨年、10人以上の規模のチームで大規模なAndroidアプリの新規開発プロジェクトにPLとして参加する機会がありました。
チーム開発では、個人開発とは異なり、チームとしてのルールを事前に決めておかないと、開発環境や実装方法が統一されず、プロジェクトの進行に支障をきたすことになります。
本記事では、チーム開発を始める前に決めておくべき重要な事項について、実際の経験を基に解説していきたいと思います。
目次
上記の目次のとおり、重要なポイントは「開発環境を揃える」「技術選定を行う」の2点です。
以下、これらの点について詳しく解説いたします。
開発環境を揃える
チーム開発において最も重要なのは、開発環境の統一です。環境が統一されていないと、開発の進行に伴い生産性の低下を招くことになります。
そこで、環境として何を揃えるべきか、揃えないことでどのような問題が発生するかについて説明いたします。
1. Android Studioのバージョンを統一する
「最新バージョンを使用すれば問題ない」という考えで開発を開始すると、メンバー間でバージョンが異なってしまうことがあります。初期メンバーは同じバージョンを使用していても、チーム開発ではメンバーの入れ替わりが発生することもあります。途中から参画したメンバーが、その時点での最新バージョンを使用しようとすると、マイナーバージョンが異なることが多々あります。
このような状況が続くと、メンバーAの環境では動作するがメンバーBの環境では動作しないといった問題が発生し、バージョンの違いが原因だと気づくまでに時間を要することがあります。このような事態を避けるためにも、プロジェクト開始時に必ずチーム全体でバージョンを統一することが重要です。
なお、当時のプロジェクトではAndroid Studio download archivesから「Android Studio Giraffe | 2022.3.1」を指定して統一しました。
2. 実装言語をKotlinに統一する
JavaとKotlinは相互運用性が非常に高いですが、1つのプロジェクトに2つの言語が混在すると可読性が低下してしまいます。現在、あえてJavaで実装する開発者は少ないと思われますが、参考にしたコードがJavaで書かれており、それをそのまま流用して実装してしまう場合を見たことがあります。このような問題を防ぐため、プロジェクト開始時からKotlinのみでの実装と明確に定めることが重要です。
3. コーディングルールを定義する
コーディングルールの遵守は当たり前だよねと思われるかもしれませんが、ルールを明確に定義しておかないと、各メンバーが自由なスタイルでコーディングを始めてしまうことがあります。そこで、独自のルールを一から細かく決めるのではなく、Androidの公式ドキュメント「Kotlin スタイルガイド | Android デベロッパー」に準拠することを前提として、特に遵守すべきルールを明記しました。
クラスコメント・メソッドコメントについてはKDoc形式に準拠することで、Dokkaを使用してHTML形式で設計書として出力し、詳細設計のアウトプットとして活用しました。
技術選定を行う
環境やルールが整ったからといって、すぐに実装に取り掛かれるわけではありません。チーム規模でのプロジェクトでは、コード量やファイル数が多くなるため、どこに何を実装するのか、どのようなライブラリを使用するのかを事前に選定し、共通ルールとして定めておく必要があります。
以下に、当時のプロジェクトで選定した技術要素について説明したいと思います。
1. Androidアプリケーションアーキテクチャを適用する
UI・Domain・Dataの3層のレイヤードアーキテクチャを採用し、MVVMアーキテクチャパターンを適用しました。大規模な開発になることを考慮し、モジュール構成を以下のように定義しました。
-
UI層
画面レイアウトをSingleActivity、MultipleFragments構成とし、画面制御としてViewModelで完全に分離することで複雑なUI制御においてもクリーンな実装ができるような構成としました。 -
Domain層
ビジネスロジック部分をUseCase毎に完全にクラスで独立させることで、1つ1つのロジックをシンプル化し、UI層からはUseCaseを組み合わせて利用できる構成としました。また、Data層へのアクセスには共通となるRepositoryインタフェースを用意することでData層に依存しない構成としています。 -
Data層
各機能をsubmodule化しています。これにより、今後また別の新しいAndroidアプリを開発する際に、通信部分やデータ制御部分を再実装することなく、このサブモジュールを連携するだけで済むようになります。
2. 利用するライブラリを選定する
-
UIはXMLレイアウト+DataBinding+ViewModelを利用する
技術選定当時は、JetpackComposeで全てのUIを表現できるかが不明確であり、チームにJetpackComposeの知見が不足していたことから、XMLレイアウトを選定しました。また、DataBindingを利用することでコードを簡潔にし、宣言的なコーディングスタイルに近づけることができました。現在であれば、JetpackComposeの採用も検討に値するでしょう。
イベント発生時には、XMLレイアウトからDataBindingによりViewModelクラスにバインドされ、ViewModelからFlowを利用してFragmentへ変更通知を行う仕組みで統一しました。
-
画面遷移にはNavigationを利用する
SingleActivity、MultipleFragments構成を採用することで、全ての画面遷移をFragment間の遷移とし、Navigationで遷移先を定義する方針としました。画面遷移の処理をXMLファイルに分離できるため、グラフィカルに画面遷移を参照することができ、実装が簡潔になります。また、SafeArgsを利用することで画面間のデータの受け渡しも実現できます。 -
非同期処理にはKotlin Coroutinesを利用する
Androidの非同期処理にはThreadやHandler、AsyncTaskなど複数の選択肢がありますが、推奨されているKotlin Coroutinesを利用することとしました。実装が統一されることで可読性が向上し、軽量なライブラリであることも利点です。 -
DB制御にはRoomを利用する
データベースを操作する場合、SQLiteOpenHelperライブラリを使用することも可能ですが、SQL文を全て手動で記述する必要があり、データベースのopen、close、cursorの操作を実装するなど、定型的なコードを多く書かなければなりません。
このような冗長な部分は、Roomライブラリを利用することで定型的な実装が不要になり、ボイラープレートコードが削減できます。
また、標準的なCRUD操作については、SQL文を記述することなく、アノテーション(@Insert
、@Delete
など)をメソッドに付けるだけでデータベース操作が可能なため、非常に便利なライブラリです。
チーム開発においては、各メンバーの書き方の癖も抑制できます。 -
ネットワーク制御にはRetrofitを利用する
ネットワーク通信を行う場合、OkHttpライブラリを使用することも可能ですが、インスタンスの作成、Requestオブジェクトの生成、URLの設定、リクエストの実行、実行結果の受け取りといった一連の流れを全て実装する必要があります。
RetrofitはOkHttpライブラリのラッパーであり、このような定型的な部分は、URLを指定したアノテーション(@Get
、@Post
など)をメソッドに付けるだけで実現できるため、ボイラープレートコードが削減され、可読性が向上します。
3. UnitTestの方針を決定する
チーム開発では、単体試験書を作成し、デバッガーを接続しながら1件ずつテストを実施するスタイルが一般的なパターンの一つですが、今回は基本的にUnitTestはテストコードを記述するという方針を採用しました。
-
ViewModel以下を全てテスト対象とする
UI層のViewModel、Domain層のUseCaseロジック、Data層の各サブモジュールを対象とし、MockKライブラリを利用して、全てJUnitのテストコードを実装しました。ActivityやFragmentについては、Espressoを使ったUIテストの実装も検討しましたが、UI部分は効率性を考慮した結果、1つ1つの画面を動作確認しながら検証した方が効率的であると判断し、見送りました。
-
Hiltを適用する
DIライブラリとしてGoogleが推奨するHiltライブラリを利用しました。Hiltを利用することでAndroid特有のボイラープレートコードを削減できます。また、UnitTestを実装する上でDIを適用することで、テストしやすいコードに仕上げることができます。
ただし、注意点として、ViewModel以下のほぼ全てのモジュールにテストコードを実装すると、本体のコードの2倍近くのテストコードが生成されるため、メンテナンス性や効率性を考慮した場合、もう少し範囲を絞ることも検討すべきだと思いました。
おわりに
本記事で紹介した内容は、Android開発においては標準的な内容と思われるかもしれませんが、意外にもこれらをプロジェクト開始前に決めておかないと、気づいた時には想定していた方針とは異なる実装が進んでいたということもあります。
そのため、チームで開発を行う際には、これらの事項を事前に決定し、チーム全体で共通認識を持っておくことが重要です。適切な準備を行うことで、スムーズなチーム開発を実現していきましょう!