Wantedly Engineering Handbook
  • Wantedly Engineering Handbook
  • まえがき
  • 第一部:開発チームへの案内
    • 技術とアーキテクチャ
    • プロダクト概要(未執筆)
    • 開発チームの構造
    • コミュニケーションの全体
    • ドキュメンテーション(未執筆)
    • カレンダー
    • 障害対応の心構え
    • 効率的な社内知識の調べ方
    • 外部発信(執筆中)
  • 第二部:技術領域への案内
    • Apps
      • アプリを提供するプラットフォーム
      • デザインシステム入門
      • Web アプリのアーキテクチャ
      • プロダクトデザイナーと上手に協働するための心得
      • Web アプリのデザインシステムライブラリ
      • Web アプリ共通ライブラリ "React Shared Component" の紹介
      • モバイルアプリのアーキテクチャ
      • モバイルアプリのデザインシステムライブラリ(未執筆)
    • The System
      • protobuf スキーマと gRPC 通信
      • 実践: gRPC in Ruby
      • 実践: gRPC in Go
      • GraphQL Gateway - アプリ向けに API を公開する
      • Wantedly Visit で BFF GraphQL サーバーを辞めた理由
      • 実践: GraphQL スキーマ設計(未執筆)
      • API での認可処理 (Authorization)
      • マイクロサービス共通ライブラリ "servicex" の紹介
      • 非同期メッセージング処理入門(未執筆)
      • バッチ処理入門(未執筆)
    • Infrastructure
      • Infrastructure Squad
      • プロダクト開発のための Kubernetes 入門
      • インフラ構成概要
      • リリース・デプロイ戦略を支える技術
      • SaaS を活用する:New Relic, Honeybadger, Datadog
    • Data
      • データ基盤入門
      • レコメンデーション
      • Looker 入門
      • 推薦システムの開発に使っているツール
    • 開発プロセス
      • Git の慣習
      • ポストモーテムの取り組み
      • 負債返済日の取り組み
      • プロダクトの課題発見及び解決
      • ソフトウェアデザインの基礎
      • コードレビュー
      • コーディング規約
      • リリース・デプロイ戦略
      • 上長承認が必要な作業
      • アーキテクチャディシジョンレコード(ADR)
      • 作業ログを残す意味
      • 多言語化対応(i18n)
      • メール開発
    • 開発ツール
      • 社内利用している開発ツールの最新状況
      • kube
      • Code Coverage
      • Kubefork
  • おわりに
    • ロードマップ(未執筆)
    • Handbook の書き方
    • コントリビューター
  • 付録
    • 社内用語集
    • 主要な GitHub レポジトリのリスト(未執筆)
    • 今後の挑戦・未解決イシュー(未執筆)
    • プロダクト開発組織のバリュー(未執筆)
    • 採用についての考え方(未執筆)
GitBook提供
このページ内
  • Why
  • Wantedlyのモバイルアプリ
  • Visit
  • Intern
  • People
  • アーキテクチャの遷移
  • 現在のiOS/Androidのアーキテクチャ
  • レイヤー構成
  • Reactive Programming
  • Kotlin Multiplatform (KMP)
  • KMPの方針
  • KMPのアーキテクチャ
  • Reactor
  • DB as a Single source of truth
  • 将来の展望

役に立ちましたか?

  1. 第二部:技術領域への案内
  2. Apps

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

前へWeb アプリ共通ライブラリ "React Shared Component" の紹介次へモバイルアプリのデザインシステムライブラリ(未執筆)

最終更新 1 年前

役に立ちましたか?

この章では、モバイルのアーキテクチャについてまとめています。 Wantedlyでのモバイルの歴史から、現在の状態、そして将来の展望について記述しています。

この章は、継続的にメンテナンスされ、モバイルエンジニアのオンボーディングの一助になることを期待します。

Why

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

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

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

Wantedlyのモバイルアプリ

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

Visit

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

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

Visit iOS

2018年にフルスクラッチでリニューアルしています。リニューアル後は、アーキテクチャパターンとしてがほとんどの画面で採用されています。 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(コード)

Visit

iOS

2

Swift

Flux(KMP Reactor)

UIKit(コード)

Visit

iOS

3

Swift

Flux(KMP Reactor)

SwiftUI

Visit

Android

1

Java

MVC

XML

Visit

Android

2

Kotlin

Flux(ReactorKitライクViewModel)

XML

Visit

Android

3

Kotlin

Flux(KMP Reactor)

XML

Visit

Android

4

Kotlin

Flux(KMP Reactor)

Jetpack 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によって細かい部分が異なっています。 現状、アーキテクチャパターン(MVVMやFluxなど)は、全体では統一されていません。

大枠では、次のような共通点があります。

  • ある程度Clean Architectureに従った、3~5層のレイヤードアーキテクチャ

  • Reactive Programming(Rx、LiveData、Coroutines Flow)によるUI状態管理

Visitの一部で導入されているKMPについては後述します。

レイヤー構成

大原則として、レイヤーの依存方向は必ず上から下になります。下層が上層の参照を持ってはいけません。 例:ViewModelがViewやViewControllerへの参照を持つ。

Use Caseが存在しない場合もありますが、概ね現行のアーキテクチャのレイヤー構成は、次のようになっています。

  • UI

    • Frontend Behaviorの状態を元にUIを構築したり、画面遷移といったユーザーに見えるものに責務を持つレイヤー

    • iOSならViewControllerやView、SwiftUIが該当

  • Presentation

    • UIの状態を管理したり、UIからイベントを受けてビジネスロジックを呼び出すレイヤー

    • ReactorやViewModelが該当

  • Use Case

    • Repositoryを隠蔽するレイヤー

    • 基本的にPresentationから見た行動1つに1つのUse Caseが存在し、大体はRepositoryのメソッドと1:1

  • Repository

    • Data Sourceを隠蔽し、適切なData SourceからEntityを取り出したり、操作したりするレイヤー

    • 基本的にEntityとRepositoryは1:1

  • Data Source

    • APIやDBなど、Entityを取得するレイヤー

  • Entity

    • ドメインで取り扱うオブジェクト

Reactive Programming

データストリームを構築し、データに変更があったときに即座に変更を伝搬する仕組みです。 一覧画面と詳細画面でブックマークの状態を共有する、APIのレスポンスを即座にUIに反映するといった要件を満たすために使われます。

現在のモバイルのアーキテクチャにおいて、これは必須の概念です。 実装としては、iOSではRxSwiftとCombine、AndroidではRxJavaとLiveDataとKotlin Coroutines Flowがあります。 概念の理解と、どれか一つの実装を習得する必要があります。

Kotlin Multiplatform (KMP)

KMPの方針

KMPの導入は、Visitでは積極的に進める方針ですが、全社的に導入を進めるのは小さく検討している段階です。

KMPのアーキテクチャ

Presentationレイヤーとして、ReactorKitライクな独自のアーキテクチャを採用しています。 採用理由としては、KMP導入の学習コストを減らすために、既存のアーキテクチャに似せたためです。

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

Reactor

Reactorのデータフローは、図にすると次のようになります。

  • UIからタップなどのトリガーでActionが送られる。

  • Actionはmutate()によってAPI通信などの副作用を経てMutationに変換される。

  • Mutationはreduce()によって現在の`Stateを変化させる。

  • UIはStateのストリームを監視してUIを変化させる。 といった単方向のデータフローを持ちます。

eventとerrorは、ReactorKitにはない概念です。

eventはワンタイムなイベントが流れてくるストリームです。Reactor内からpublish()することでストリームにイベントが流れます。 例えば、登録フローのように登録API成功後に画面遷移を伴う場合、Reactorから完了のイベントを放出してUI側で画面遷移を行うときなどに使います。 段階的な導入をする上で、ナビゲーションは共通ロジックでは所持しない方針にしたため、UI側へイベントを投げて遷移させるためにこのようなストリームを作りました。

errorはその名の通りReactor内部でハンドリングされたエラーが流れてくるストリームです。Reactor内からerror()することでストリームにエラーが流れます。 通信エラーのようにハンドリング可能なエラーを放出して、UI側でエラーを表示するときなどに使います。

Declarative UI環境下ではワンタイムなイベントは状態として持つことが難しく、UIの状態と整合性を保つのが困難です。そのためDeclarative UIの導入に伴ってこれらは現在非推奨となっています。

DB as a Single source of truth

KMPのアーキテクチャでは、データベースをSingle source of truthとして使っています。 Single source of truthとは、データの操作などをすべて1箇所に集約することで、データの更新は即座に参照先へ伝わる仕組みです。

募集検索画面と詳細画面でブックマークの状態を共有する例で見ていきましょう。

依存の流れは次のようになっています。

各画面は、最初にデータを取得します。データはDBのストリームを介して、常にProjectの最新の状態がUIまで通知されます。 破線は、データを購読(Observe)していることを表します。

ここで、詳細画面でブックマークをしたとします。 このときのデータフローは次のようになります。

DBへ状態の変更を保存することで、DBは最新状態をブロードキャストします。 ブロードキャストはストリームの購読者である2つの画面両方に通知され、結果UIまで即座にブックマークした状態が反映されます。

GraphQL

将来の展望

Visit iOSのリニューアルの経験から、フルスクラッチでのリニューアルというのは当面ないと想定されます。(チームやプロダクトとの合意があれば別です。) そのため、段階的に1画面ずつ変わっていくことが求められ、画面単位で依存性が別れたアーキテクチャが必要になってきます。

今後、Declarative UIというパラダイムシフトが確実に起きるため、アーキテクチャにさらにバージョンが増えることが予想されます。 アーキテクチャは頻繁に大きく変えられません。なので細かくアーキテクチャを改善し、古いバージョンのアーキテクチャは徐々に消し去っていきましょう。

話を聞きに行きたい

もっと知りたい

KMPは、2020年からVisitへ導入されています。 経緯とか説明は、ブログにまとめられているので、を参照してください。

具体的には、ログイン処理は全社的に共通のロジックであるため、というrepoでKMPの共通実装を行い、実験的にVisitとPeopleへ導入を進めています。

Reactorはを元に作られた、単方向データフローを実現する独自のフレームワークです。

詳細な実装はや実際のコードを参照してください。

Wantedlyではソフトウェアデリバリのパフォーマンスを最大化するためにGraphQLを採用し、段階的に適用範囲を拡大しています。

モバイルアプリも新しく機能開発する箇所ではGraphQLを採用し、上記をを用いた仕組みで実現しています。

Slack:

ReactorKit
React Nativeをやめる話とKotlin Multiplatform
wantedly/mobile-shared
ReactorKit
Wantedly VisitにおけるKotlin Multiplatformの導入と実装
GraphQL Gateway - アプリ向けにAPIを公開する
Apollo GraphQL
#mobile_chapter
React Nativeをやめる話とKotlin Multiplatform
Wantedly VisitにおけるKotlin Multiplatformの導入と実装
現行のアーキテクチャ
KMPアーキテクチャ
Reactor
依存の流れ
データの流れ
ブックマークのフロー