モバイルアプリのアーキテクチャ
この章では、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のアーキテクチャの移り変わりは、次のようになっています。
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開発者の学習コストを最小限に抑えながら、プラットフォーム間でのビジネスロジック共有を実現しています。

モジュール構成
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両プラットフォームで一貫したアーキテクチャパターンを提供し、予測可能な状態管理を実現します。
データフロー

基本的なデータフロー
Action: UIからのユーザーインタラクション(タップ、入力など)
Mutate:
Action
を受けてAPI通信などの副作用を実行し、Mutation
を生成Reduce:
Mutation
を受けて現在のState
を新しいState
に変換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)関係を表しています。
データストリーム

初期データ取得: 各画面が必要なデータをDBから取得
リアルタイム監視: DBの変更をFlowで監視
自動UI更新: データ変更時に自動的にUI更新
状態変更フロー

ユーザーアクション: 詳細画面でブックマークボタンをタップ
API通信: サーバーにブックマーク状態を送信
DB更新: レスポンス受信後、ローカルDBを更新
自動伝播: DB変更が全ての購読者(画面)に自動通知
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、コード生成、テストの自動化拡充
開発体験: ホットリロード、デバッグツールの改善
ドキュメント: 継続的な技術文書の更新と改善
アーキテクチャは段階的に改善し続け、古いパターンは徐々に新しいパターンに置き換えていく方針を継続します。
参考資料
技術ブログ・記事
質問・相談
Slack: #mobile_chapter
最終更新
役に立ちましたか?