モバイルアプリのアーキテクチャ

この章では、Wantedlyのモバイルアプリケーションのアーキテクチャについて包括的にまとめています。 モバイル開発の歴史的変遷から現在の実装状況、そして将来の技術的展望まで、開発者が理解すべき重要な情報を体系的に整理しています。

この文書は継続的にメンテナンスされ、モバイルエンジニアのオンボーディングと日常的な開発作業の指針となることを目的としています。

Why

モバイルアプリにおいて、アーキテクチャにバージョンが生まれるのは必然です。 既存のアーキテクチャの課題感への解決策として新しいバージョンのアーキテクチャが生まれたり、パラダイムシフトが起きるとアーキテクチャが変わっていきます。

アーキテクチャは、バージョンが新しくなるたびに改善していきます。 しかし、古くなったアーキテクチャをすべて一度にリニューアルするのは、膨大なコストが掛かるため非現実的であり、徐々に古いものを新しくしていくのが現実的です。 そのため、常に古いバージョンのアーキテクチャと向き合う必要があります。

この章は、古いアーキテクチャについては軽く、現在のアーキテクチャについてはより詳細にまとめ、今後のアーキテクチャの理想像を描いていきます。

Wantedlyのモバイルアプリ

現在、WantedlyではVisit, Intern, Peopleの3つのアプリを公開しています。

Visit

Visitは、ユーザーと会社が気軽にマッチングできるアプリです。

2014年から提供しています。 2018年のiOSリニューアル時にReact Nativeが導入されました。 2021年にReact Nativeは取り除かれ、KMPが導入されました。

Visit iOS

2018年にフルスクラッチでリニューアルしています。リニューアル後は、アーキテクチャパターンとしてReactorKitがほとんどの画面で採用されています。 2020年にプレミアムというアプリ内課金機能が追加されました。

Visit Android

iOSのフルスクラッチのリニューアルは、リソースの大量投入が起きてプロダクト開発が止まるという問題点があったため、Androidは2018年から段階的なリニューアルを開始しました。 リニューアル以降は、ReactorKitライクな記述ができるAndroidのViewModelを採用しています。

Intern

Internアプリは、学生の流入を目的としたアプリで、Visitと同じrepo・同じコードでできています。 そのため、Visitと全く同じアーキテクチャで同じ機能を持ちます。

People

2016年から提供しています。 つながりの情報をローカルに保持し、各OSの連絡先に同期する機能があります。このために一部にRealmが使われています。 名刺撮影機能のためにOpenCVが使われています。 両OSどちらもMVVMアーキテクチャを採用しています。

アーキテクチャの遷移

細かな点は省きますが、各アプリ・各OSのアーキテクチャの移り変わりは、次のようになっています。

アプリ
OS
世代
言語
アーキテクチャパターン
UI実装
備考

Visit

iOS

1

Swift

Flux(ReactorKit)

UIKit(No Storyboard)

2018年リニューアル

Visit

iOS

2

Swift

Flux(KMP Reactor)

UIKit(No Storyboard)

KMP導入(2021年)

Visit

iOS

3

Swift

Flux(KMP Reactor)

SwiftUI

SwiftUI導入(2022年)、SPM移行中

Visit

Android

1

Java

MVC

XML

段階的リニューアル開始

Visit

Android

2

Kotlin

Flux(ReactorKitライクViewModel)

XML

UdfViewModel採用

Visit

Android

3

Kotlin

Flux(KMP Reactor)

XML

KMP Reactor導入

Visit

Android

4

Kotlin

Flux(KMP Reactor)

Jetpack Compose

Compose移行中

People

iOS

1

Swift

MVVM

UIKit(コード)

People

iOS

2

Swift

MVVM

UIKit(xib)

People

Android

1

Java

MVC

XML

People

Android

2

Kotlin

MVVM(Rx)

XML

People

Android

3

Kotlin

MVVM(Coroutines)

XML

現在のiOS/Androidのアーキテクチャ

各アプリ・OSによって実装の詳細は異なりますが、現在のWantedlyモバイルアプリケーションは以下の共通原則に基づいて設計されています。

共通アーキテクチャ原則

  • Clean Architecture準拠: 3〜5層のレイヤードアーキテクチャを採用

  • Reactive Programming: UI状態管理にRx、LiveData、Coroutines Flowを活用

  • 単方向データフロー: Flux/MVVMパターンによる予測可能な状態管理

  • モジュール分割: 機能単位での疎結合なモジュール構成

  • テスト可能性: 依存性注入とモック化による高いテスタビリティ

アプリ別実装状況

Visit iOS

  • アーキテクチャ: ReactorKit + Coordinator パターン

  • KMP統合: visit-app-shared による共有ビジネスロジック

  • UI Framework: SwiftUI + UIKit(段階的移行中)

  • パッケージ管理: Swift Package Manager

  • モジュール構成: Feature-based modular architecture

Visit Android

  • アーキテクチャ: Single Activity + Multiple Fragments

  • ViewModel: UdfViewModel(Flux-like architecture)

  • UI Framework: Jetpack Compose移行中

  • データベース: Room Database as Single Source of Truth

  • ナビゲーション: JetPack Navigation + DeepLinks

People

  • アーキテクチャ: MVVM パターン

  • データ永続化: Realm(連絡先同期機能)

  • 画像処理: OpenCV(名刺撮影機能)

Visitアプリで積極的に導入されているKMPについては後述します。

レイヤー構成

依存関係の原則

レイヤーの依存方向は必ず上位から下位への単方向とし、下位レイヤーが上位レイヤーを参照することは禁止されています。 例:ViewModelがViewやViewControllerへの参照を持つことは許可されません。

標準レイヤー構成

現行のアーキテクチャ

UI Layer

  • 責務: ユーザーインターフェースの描画と画面遷移

  • 実装:

    • iOS: UIViewController、SwiftUI View、Coordinator

    • Android: Fragment、Jetpack Compose

  • 特徴: Presentation Layerの状態を監視してUI更新を行う

Presentation Layer

  • 責務: UI状態管理とビジネスロジックの呼び出し

  • 実装:

    • iOS: Reactor(ReactorKit/KMP Reactor)

    • Android: ViewModel(UdfViewModel)

  • パターン: 単方向データフロー(Flux/MVI)

Use Case Layer

  • 責務: アプリケーション固有のビジネスルール

  • 実装:

    • iOS: UseCase classes in BusinessLogic module

    • Android: UseCase classes with invoke operator

  • 関係: 基本的にPresentation Layerの1アクションに対して1 UseCase

Repository Layer

  • 責務: データソースの抽象化とEntity操作

  • 実装:

    • iOS: Repository classes with Moya/Alamofire

    • Android: Repository classes with Retrofit

  • パターン: 基本的に1 Entity = 1 Repository

Data Source Layer

  • 責務: 外部データソース(API、DB)との通信

  • 実装:

    • API: REST/GraphQL通信

    • DB: Room(Android)、Core Data/SQLite(iOS)

    • KMP: SQLDelight(共有DB層)

Entity Layer

  • 責務: ドメインオブジェクトの定義

  • 実装: ビジネスルールを含むデータクラス/構造体

Reactive Programming

概要

データストリームを構築し、データ変更時に即座に変更を伝搬する仕組みです。 複数画面間での状態共有、APIレスポンスのリアルタイム反映、ユーザーインタラクションの処理など、現代的なモバイルアプリケーションの要件を満たすために不可欠な技術です。

プラットフォーム別実装

iOS

  • RxSwift: 主要なReactive Programming実装

  • Combine: Apple純正フレームワーク(iOS 13+)

  • 使用例: ReactorKitでの状態管理、API通信の非同期処理

Android

  • RxJava: 従来のReactive Programming実装

  • LiveData: Android Architecture Components

  • Kotlin Coroutines Flow: 現在推奨される実装

  • 使用例: ViewModelでの状態管理、Room Databaseの監視

KMP(Kotlin Multiplatform)

  • Kotlin Coroutines Flow: プラットフォーム間で共有可能

  • 使用例: visit-app-sharedでの共有ビジネスロジック

学習要件

モバイル開発者は以下の理解が必要です:

  • Reactive Programmingの基本概念(Observer pattern、Stream processing)

  • 各プラットフォームの実装のうち少なくとも1つの習得

  • エラーハンドリングとメモリリーク対策

Kotlin Multiplatform (KMP)

導入背景と現状

KMPは2021年からVisitアプリに本格導入され、現在はビジネスロジックの共有において中核的な役割を果たしています。 導入の経緯については、React Nativeをやめる話とKotlin Multiplatformで詳しく解説されています。

現在の導入状況

visit-app-shared

  • リポジトリ: wantedly/visit-app-shared

  • 役割: Visitアプリの共有ビジネスロジックライブラリ

  • 配布: Maven経由でiOS/Androidに提供

  • 主要機能: Reactor実装、API通信、データベース操作

KMPの方針

Visitアプリ: 積極的な導入を推進

  • 新機能開発時はKMP Reactorの採用を優先

  • 既存機能の段階的な移行を実施

全社展開: 慎重な検討段階

  • 認証処理などの共通性が高い機能から実験的に導入

  • 各アプリの特性を考慮した段階的な適用

KMPのアーキテクチャ

設計思想

KMPでは、既存のReactorKitパターンを踏襲した独自のReactorアーキテクチャを採用しています。 これにより、iOS/Android開発者の学習コストを最小限に抑えながら、プラットフォーム間でのビジネスロジック共有を実現しています。

KMPアーキテクチャ

モジュール構成

visit-app-shared の主要モジュール

  • :api: API通信関連(REST/GraphQL)

    • :entity: APIエンティティ定義

    • :graphql: GraphQL クライアント(Apollo)

    • :s3: S3アップロード機能

  • :core: 共通ユーティリティ

  • :visit-app-shared: メインモジュール(Reactor実装)

  • :testing: テスト用ユーティリティ

技術スタック

  • データベース: SQLDelight(型安全なSQL)

  • API通信: Ktor Client + Apollo GraphQL

  • 非同期処理: Kotlin Coroutines

  • 依存性注入: Koin

  • テスト: Kotlin Test + MockK

次項からより詳細に解説します。

Reactor

概要

ReactorはReactorKitを参考に開発された、KMP環境での単方向データフローを実現するフレームワークです。 iOS/Android両プラットフォームで一貫したアーキテクチャパターンを提供し、予測可能な状態管理を実現します。

データフロー

Reactor

基本的なデータフロー

  1. Action: UIからのユーザーインタラクション(タップ、入力など)

  2. Mutate: Actionを受けてAPI通信などの副作用を実行し、Mutationを生成

  3. Reduce: Mutationを受けて現在のStateを新しいStateに変換

  4. State: UIが監視する状態オブジェクト、変更時にUI更新をトリガー

拡張機能

Event Stream(現在非推奨)

  • ワンタイムイベント用のストリーム

  • 画面遷移やダイアログ表示などに使用

  • Declarative UI導入により非推奨化

Error Stream(現在非推奨)

  • エラーハンドリング用のストリーム

  • 通信エラーやバリデーションエラーの伝播

  • Declarative UI導入により非推奨化

実装詳細

詳細な実装については以下を参照:

Database as Single Source of Truth

設計原則

KMPアーキテクチャでは、SQLDelightを使用したデータベースをSingle Source of Truthとして採用しています。 この設計により、データの一貫性を保ちながら、複数画面間でのリアルタイムな状態同期を実現しています。

技術実装

SQLDelight の特徴

  • 型安全性: SQLクエリのコンパイル時検証

  • マルチプラットフォーム: iOS/Android共通のDB操作

  • Reactive: Kotlin Coroutines Flowによるリアクティブクエリ

  • パフォーマンス: ネイティブSQLiteの性能を活用

データフロー例:ブックマーク機能

依存関係

依存の流れ

各画面のReactorは、SQLDelightのFlowを通じてデータベースの変更を監視します。 破線は、データの購読(Observe)関係を表しています。

データストリーム

データの流れ
  1. 初期データ取得: 各画面が必要なデータをDBから取得

  2. リアルタイム監視: DBの変更をFlowで監視

  3. 自動UI更新: データ変更時に自動的にUI更新

状態変更フロー

ブックマークのフロー
  1. ユーザーアクション: 詳細画面でブックマークボタンをタップ

  2. API通信: サーバーにブックマーク状態を送信

  3. DB更新: レスポンス受信後、ローカルDBを更新

  4. 自動伝播: DB変更が全ての購読者(画面)に自動通知

  5. UI反映: 一覧画面と詳細画面の両方で即座にUI更新

実装上の利点

  • データ整合性: 単一のデータソースによる一貫した状態管理

  • パフォーマンス: ローカルDBによる高速なデータアクセス

  • オフライン対応: ネットワーク状況に依存しない基本機能

  • 開発効率: プラットフォーム間でのロジック共有

GraphQL統合

導入背景 Wantedlyでは開発効率とパフォーマンス向上のためにGraphQLを段階的に導入しています。 詳細はGraphQL Gateway - アプリ向けにAPIを公開するを参照してください。

KMPでの実装

  • クライアント: Apollo GraphQL Kotlin版

  • コード生成: GraphQLスキーマからKotlinコードを自動生成

  • 型安全性: クエリとレスポンスの型安全な操作

  • キャッシュ: Apollo Clientの自動キャッシュ機能

開発方針

  • 新機能: GraphQLを優先的に採用

  • 既存機能: REST APIから段階的に移行

  • パフォーマンス: 必要なデータのみを取得(Over-fetching回避)

将来の展望

アーキテクチャ進化の方針

段階的移行の重要性 Visit iOSの全面リニューアル経験を踏まえ、フルスクラッチでの大規模変更は避け、画面・機能単位での段階的な改善を継続します。

技術的な方向性

  • Declarative UI: SwiftUI/Jetpack Composeへの完全移行

  • KMP拡張: プラットフォーム間でのより多くのロジック共有

  • モジュール化: 機能単位での独立性向上

  • 型安全性: GraphQL、SQLDelightによる型安全な開発

開発効率の向上

  • 自動化: CI/CD、コード生成、テストの自動化拡充

  • 開発体験: ホットリロード、デバッグツールの改善

  • ドキュメント: 継続的な技術文書の更新と改善

アーキテクチャは段階的に改善し続け、古いパターンは徐々に新しいパターンに置き換えていく方針を継続します。

参考資料

技術ブログ・記事

質問・相談

最終更新

役に立ちましたか?