Android - TECH PLAY - TECH PLAY

TECH PLAY

Android

イベント

マガジン

技術ブログ

こんにちは、株式会社LuupでEngineering Managerをしている瀧川です。 2026年9月1日(火)〜3日(木)にベルサール渋谷ガーデンで開催されたDroidKaigi 2026に、Androidエンジニアのメンバーといっしょに参加してきました! LuupではLUUPアプリをiOS・Androidともにネイティブで開発しているので、Android開発の最新動向やAI活用の事例をキャッチアップしたく参加してきました。 平日開催のため私は最終日しか参加できず残念でしたが、資料やセッション動画は後日公開されるはずなので、聴けなかったセッションはそちらで追いかけようと思っています。
こんにちは。LINEアプリ開発SBU AIディベロッパーエクスペリエンスユニットの Nagase Takuya です。私のチームは、AndroidとiOSの開発者が混在する体制で、LINEアプリの機能...
はじめに こんにちは、ZOZOTOWN開発2部Androidブロックの大江です。普段はZOZOTOWN Androidの開発を担当しています。 ZOZOTOWN Androidは10年以上にわたって開発されています。機能追加を重ねる中で、特定のモジュールが肥大化したり、モジュール構成が複雑になったりしたため、改善に取り組んでいます。 本記事では、こうしたモジュール構成について、課題とその解決に向けて段階的に進めてきた取り組みを中心に紹介します。 目次 はじめに 目次 背景・課題 段階的に整理を進める方針 dataモジュールの疎結合化 kaptの隔離 coreモジュールとinfraモジュールの依存方向の反転 得られた効果 まとめ 背景・課題 まず、ZOZOTOWN Androidの現在のモジュール構成の概要を紹介します。 app :画面や機能を担う各モジュールを束ね、アプリとして組み立てる役割を持つモジュール feature :画面や機能ごとに分割された実装を置くモジュール core :複数の機能から参照される古い共通処理を置くモジュール(将来的には解体したい) legacy :新しいアーキテクチャへの移行が済んでいない実装を置くモジュール(将来的には解体したい) data :APIやDBへのアクセスを担うモジュール infra :外部サービスとの通信など基盤的な処理を担うモジュール domain :APIやDBの実装から独立したユースケース(業務ロジック)を置くモジュール 現在、ZOZOTOWN Androidは77個のモジュールに分割されています。そのうち app モジュールは特に肥大化していて、単体でコードベース全体の約30%を占める規模になっています。これは feature モジュールへの切り出しが済んでいない画面の実装が app モジュールに残り続けていることが主な要因です。また legacy ・ core モジュールは、責務が曖昧になったり依存関係が複雑になったりして、将来的な解体が難しくなっていました。 こうした状況の中、機能開発を進める中で以下の問題が発生していました。 legacy モジュールと core モジュールに対する参照が増え続けて、解体が先送りになり続ける ビルド時間とユニットテストの実行時間が延び続ける 段階的に整理を進める方針 これらの問題を解決するために、大規模になっているZOZOTOWN Androidのモジュール構成を一気に理想形へ整理し直すのは、コストもリグレッションのリスクも大きくなります。そこで対応コストと得られる効果を見比べながら、優先順位をつけて段階的に手を入れていくことを考えました。 優先順位をつける際は、次の3点を意識しました。 今後も機能追加が続く前提で、繰り返し効果が積み重なる対応かどうか 対応コストに対して、ビルド時間や開発のしやすさへの効果がどれくらい見込めるか AIを活用することで対応コストそのものを下げられるか これらを踏まえて、次の3つの取り組みを選びました。 data モジュールの疎結合化:新しく作るRepositoryを疎結合な構造に強制できれば効果が積み重なるうえ、既存の実装にはほとんど手を入れずに進められるため対応コストも小さく、最初に着手した対応 kaptの隔離: @BindingAdapter を使った実装を1つのモジュールへ集約するだけで済み、対応コストが小さい一方、ビルド時間の短縮はチーム全体の開発体験に直結するため優先度を上げた対応 core モジュールと infra モジュールの依存方向の反転:複雑な依存関係を人手で洗い出すのは時間がかかりそうだが、AIに事前検証させることで対応コストを下げられる見通しが立った対応 ここからは、実際に進めた3つの取り組みを順に紹介します。 data モジュールの疎結合化 API・DBアクセスを担う data モジュールに配置されているRepositoryの中には、interfaceが設けられていないものがありました。こうしたRepositoryは legacy モジュールや core モジュールの実装に密結合しており、使用するたびに両モジュールへの参照が増えてしまいます。その結果、 legacy モジュールと core モジュールの解体コストが上がるという悪循環に陥っていました。 そこでまずはこの悪循環を断ち切ることが必要だと判断し、 data モジュールを次の4つに分割して新しく作るRepositoryは疎結合化を強制できるようにしました。 data:definition :interfaceとDTOだけを置くモジュール data:implementation :新しい設計に沿った実装を置くモジュール data:legacy :既存の実装をそのまま引き継ぐ受け皿 data:di :DIのバインディング定義だけを行うモジュール 利用側は data:implementation ではなく data:definition と data:di にだけ依存する構成にしました。実装クラスを直接使おうとすればビルドが失敗するので、コードレビューに頼らずビルド構成で疎結合を保証できました。 data:legacy は単なる未整理の実装置き場ではなく、新しい設計に沿った実装を既存の実装から隔てる腐敗防止層として意図的に位置づけました。この位置づけによって、既存のRepositoryを全件移行しきる前から、新しい設計を安全に並行導入できる状態を作れました。 legacy ・ core モジュールと異なり、 data:legacy はRepositoryの移行が進むにつれて中身が減っていく受け皿であり、移行完了後にはモジュールごと解体できる見通しを持っています。 既存の実装にはほとんど手を入れずに済むため対応コストは小さく、そのうえ新しく作るRepositoryが増えるたびに効果が積み重なります。この2点から、3つの取り組みの中でも最初に着手する対応として選びました。 kaptの隔離 kaptはJavaスタブを生成する必要があるため、ビルド時間を圧迫する要因として知られています。ZOZOTOWNでもモジュールごと、Gradleのタスクごとのビルド時間を計測しました。その結果、kaptに関連する処理がビルド時間の大半を占めていることがわかりました。原因は、Data Bindingの @BindingAdapter を使った実装があちこちのモジュールに散らばっていたことでした。kaptはモジュールごとに個別の注釈処理タスクが実行されるため、同じアノテーションを使うコードの分散は、その分だけ処理コストの積み重なりを招きます。 そこで @BindingAdapter を使う実装だけを ui-databinding という専用モジュールに集約し、それ以外のモジュールからkaptの設定を削除しました。散らばっていた実装を1つのモジュールへ集約するだけで済むため対応コストは小さく、ビルド時間の短縮という効果はチーム全体の開発体験に直結します。この対応コストと効果のバランスから、優先度を上げて取り組みました。 この対応によって複数のモジュールでビルドにkapt関連の処理が実行されなくなり、GitHub Actionsの4コアCI環境でのビルド時間が30分から18分へと、40%程度短縮できました。この数値はCI環境限定のものですが、ローカル開発環境でも同様にビルド時間の短縮を体感できています。なお、現在もkaptが残っているのは core ・ ui-databinding と、機能単位のモジュール2つのみです。 core モジュールの build.gradle には今も「Epoxyを削除できたらkaptも削除する」という趣旨のコメントが残っており、対応がすべて終わったわけではありません。 core モジュールと infra モジュールの依存方向の反転 legacy モジュールと core モジュールは様々なモジュールで使用されていて、依存関係が複雑になっていることもこれらのモジュールの解体を先送りさせる原因になっています。複雑に絡み合っている依存関係を人手で紐解いて解体するのはコストが高く、リファクタリングとして優先度が上がらない状況でした。 しかし、Claude CodeなどのAIが登場し、こうした人手だと時間のかかる調査や検証を短時間で行えるようになりました。 そんな中、ある機能の開発を進めている際にAPI通信を担う infra モジュールが共通処理を置く core モジュールへ依存していて、理想とは逆の方向の依存関係を持っていることに気づきました。この向きの依存関係だと、機能開発に必要だった core モジュール側の新しい実装から infra モジュール側の既存パーサーを直接参照できません。そのためinterfaceと実装を分けてDIで注入するという、本来不要なはずの回り道の実装が必要になっていました。 そこでAIに、依存方向を反転させる案を別ブランチで検証させました。依存関係の定義を反転させて、関連するクラス群も infra モジュール側のパッケージへ移動しました。ロジックの変更を伴わず、ファイルの移動と参照先の付け替えだけで完結する変更だと分かりました。 この見極めが、AIに実装まで任せる決め手になりました。挙動を変えるロジック修正が必要な変更であれば、AIが提案した内容でも人間が変更の妥当性を細かく確認する必要がありますが、機械的な変更だけで済む場合は検証から適用までを任せやすいと感じています。 一般的に依存関係の変更は影響範囲が広く、大量のソースコードを変更することになります。この見通しを短時間で立てられたことが、着手の判断を後押ししました。 実際の対応でも、AIが作成したブランチをベースに実装を進め、既存のビルドとユニットテストがすべて通ることを確認できましたが、影響範囲の広さから対応コストは高そうに見えました。しかし、AIによる事前検証でその判断コスト自体を下げられたことが、優先して取り組む決め手になりました。この実績によって、今後の依存関係の整理や legacy ・ core モジュールの解体を加速させられる目処が立ちました。 得られた効果 この3つの取り組みを通じて、次の効果が得られました。 data モジュールの疎結合化:新しく作るRepositoryが legacy ・ core への密結合を避けられる構造になり、参照が増え続ける悪循環を断ち切れました kaptの隔離:GitHub Actionsの4コアCI環境でビルド時間を30分から18分(40%程度)に短縮できました core モジュールと infra モジュールの依存方向の反転:機能開発に不要だった回り道の実装を解消できました。加えて、AIに事前検証させることで大規模な依存関係の変更に着手する判断を素早く行えるという実績もできました まとめ 本記事ではZOZOTOWN Androidのモジュール構成に対する課題とその解決方法を紹介しました。大規模なアプリのモジュール整理を一括ではなく段階的に進める前提で設計し、移行しきれていない実装の受け皿を腐敗防止層として用意することで、既存実装への影響を抑えながら新しい設計を導入できました。また依存関係の大規模な組み替えは、AIに実現可能性を先に検証させることで、着手の判断を素早く行えました。マルチモジュール構成の整理を検討している方がいれば、ぜひ本記事を参考にしてみてください。今後は残っている legacy ・ core モジュールの解体や、 app モジュールに残る画面の feature モジュールへの切り出しなど、引き続きモジュールの整理を進めていきたいと考えています。 ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。 corp.zozo.com

動画

書籍