はじめに
ナビタイムジャパンでは、アプリの継続的な開発・運用をスムーズに行うために「設計」を重視しています。本記事では、その「設計」を実現するアーキテクチャをいくつか紹介し、実際に現場で使われているアーキテクチャについて考察していきたいと思います。
アーキテクチャとは?
アーキテクチャとは、アプリを綺麗に開発・継続的に運用していくための設計方法です。この設計方法を考えずに開発すると以下の問題点が発生してしまいます。
- クラスが肥大化し、コードを追いにくくなる
- ロジックの煩雑化、再利用性の低下
- チーム開発における役割分担がしにくい
- 属人化が進み、引き継ぎにくい
- テストがしにくい
- 機能の追加、改修が困難
- etc…
チームでのアプリ開発はもちろん、個人でアプリを開発する時も、開発するアプリに規模や特性に合わせて適切にアーキテクチャを選ぶことが、アプリを継続的に開発・運用していく上で非常に重要です。ただし、アーキテクチャは手段に過ぎず、固執することなく常に設計について考えることが大切です。
アーキテクチャのご紹介
どのようなアーキテクチャがあって、どのように選べばいいのかをご紹介していきます。
MVC (Model View Controller)
iOS開発において、MVC
というとAppleが推奨しているCocoa MVC
が一般的です。
Cocoa MVC
ではView
がユーザーの入力を受け付け、Controller
にアクションを渡します。そしてController
がModel
にデータの更新を依頼します。最後にModel
がController
に処理の完了を通知し、Controller
がView
を更新します。
-
View
:表示、入出力 -
Controller
:入力にもとづくModel
とView
の制御 -
Model
:データの保持、通信、ビジネスロジック
Cocoa MVC
はUIKit
をそのまま素直に使えば簡単に実装できますが、Controller
がView
と密結合しており、View
のライフサイクルにも影響されるため、テストは容易ではありません。また、Controller
はView
とModel
の仲介を行うので、処理が集中しがちで肥大化しやすいです。このためMassive View controller
と言われることもあります。
ところで、Cocoa MVC
はAppleによって改良されたもので、原初のMVC
とは構造が異なります。原初MVC
は元々SmallTalk
という元祖オブジェクト指向のプログラミング言語の開発環境のために考案されたデザインパターンです。
Cocoa MVC
ではController
がView
とModel
を分離し仲介を行いますが、原初MVC
ではModel
がView
に通知し、View
がModel
の更新された状態を取得して表示を更新するというオブザーバパターンを採用しています。このためView
とModel
の間に依存関係が生じてしまい、View
で表示用にデータを加工することも必要になります。
MVP (Model View Presenter)
MVP
はView
とModel
、そして両者を仲介し、プレゼンテーションロジック(UIのビジネスロジック)を担当するPresenter
で構成されるアーキテクチャです。MVC
とは異なり、入力を受け付けるのがController
ではなくView
になり、MVP
ではUIViewController
はView
に分類されます。このためPresenter
はView
のライフサイクルやレイアウトに依存せずに、プレゼンテーションロジックのみ担当し、import UIKit
が必要ありません。また、View
とPresenter
はデリゲートやインターフェイスを通してやり取りを行うため、View
のモックがしやすく、Presenter
のテストが容易になります。
MVP
にはSupervising Controller
とPassive View
の2種類があり、前者のSupervising Controller
ではデータバインディングによりView
がModel
を監視し、データ更新があれば表示も更新します(上図のView
とModel
にデータバインディングの関係が追加されます)。View
がModel
を監視することで、データを加工せずにそのまま表示するシーンでは、Presneter
の負担を軽減できますが、Model
がView
のレイアウトに依存してしまいます。一方、後者のPassive View
では、View
とModel
のやり取りは全てPresenter
を介しますので、View
とModel
が完全に分離されますが、その分Presenter
の負担が増えてしまいます(これはCocoa MVC
でのController
に似ています)。
MVVM (Model View ViewModel)
MVVM
は元々MicrosoftのWPF (Windows Presentation Foundation)
およびSilverlight
アーキテクチャです。MVP
と似ていますが、MVP
ではデリゲートやインターフェイスで処理を移譲・更新処理を行うのに対して、MVVM
ではデータバインディングでView
とViewModel
を紐付けます。
データバインディングでは、イベントを発行・購読することで、直接的な依存関係がなくても、アクションに対応した処理を行って表示を更新することができます。データバインディングはMVP (Supervising Controller)
での監視よりもさらに密な考え方で、UIとデータオブジェクトを紐付けて、同一のデータをやり取りできるようにしています。双方向データバインディングでは、UIを変更すればデータも書き換えられ、データが更新されればUIも変更されます。
iOSでは、データバインディングを実現するために、リアクティブプログラミング(RP
)を実装したフレームワークであるRxSwift
やReactiveSwift
などのライブラリが使われますが、RP
の習得コストが高く、個人開発者はともかくチームで開発する場合は、全員がRP
について理解していることが必要のため、簡単に導入できないというデメリットがあります。
Clean Architecture
ここまで紹介してきたMVC
, MVP
, MVVM
はGUIアーキテクチャと呼ばれるもので、UIに関するロジック(プレゼンテーション)とシステムに関するビジネスロジック(ドメイン)を分離する※のが目的です。しかし、プレゼンテーション層から分離されたロジックに関するルールはないため、サーバ通信やデータの永続化などのロジックは全てModel
に詰め込まれ肥大化してしまいます。
※Presentation Domain Separation(PDS)
一方、Clean Architecture
はシステムアーキテクチャと呼ばれ、UIだけに止まらず、システム全体の構造を示すものです。Clean Architecture
ではドメイン駆動開発(DDD
)やユースケース駆動開発(UCDD
)を意識して、ビジネスロジックをUIやフレームワークから引き離して、それぞれの層ごとに役割と責任を分離しています。
※ 画像はThe Clean Code Blogを参考
Clean Architecture
は以下の層で構成され、中心には変化の少ないドメインを、外側には変化の激しい層を配置し、中心に向かって一方向の依存性しか持ちません。
- フレームワーク・ドライバ
- DB、UI、デバイス、Web
- インターフェイスアダプター
- Gateway、Presenter、Controller
- アプリ固有のデータ・ロジック
- Use Case
- アプリ非依存のデータ・ロジック
- Entity
Clean Architecture
はシステム全体の構造を、各層に役割と責任を持たせて整理していることから、特定のフレームワークやライブラリに依存しません。また、各層に分割したことでUIやデータの保存処理が頻繁に変化してもビジネスロジックには影響しなく、全ての層でテストを導入することもコストが低いです。
このように、Clean Architecture
は変更に強く、再利用可能で、テストもできるため、「Clean」なアーキテクチャです。しかし、層が多くなるため必然的にコード量が多くなってしまい、プロトタイプや小規模なアプリケーション開発にはあまり向いていませんが、一定規模以上のプロダクトで効力を発揮します。
VIPER
※ 画像はiOS Project Architecture: Using VIPERより引用
VIPER
は上で述べたClean Architecture
をiOS向けに再設計したアーキテクチャです。もちろんClean Architecture
がもとになっているので、システムアーキテクチャであり、単一責任の原則にもとづいて各層を分割し責任を持たせています。View, Interactor, Presenter, Entity, Router
の頭文字を取ってVIPER
と呼びます。また、VIPER
では各層でプロトコルを定義し、それに準拠して実装することでテストも行いやすいです。
-
Router
- 「画面遷移」と「依存関係の解決」を担当
-
View
が画面遷移まで担当する必要がなくなる
-
View
-
UIView
&UIViewController
- 「画面の更新」と「
Presenter
へのイベント通知」を担当
-
-
Presenter
-
View
から受け取ったイベントをもとに「プレゼンテーションロジック」を担当 -
View
に画面の更新を依頼 -
Interactor
にデータの取得を依頼 -
Router
に画面遷移を依頼 -
import UIKit
禁止
-
-
Interactor
- 「データに関するロジック」を担当
- データ取得が完了したらデリゲート経由で
Presenter
に返す -
import UIKit
禁止
-
Entity
- 「データ構造の定義」を担当
-
struct
でデータを定義 -
Interactor
のみアクセス可 -
import UIKit
禁止
VIPER
では役割を細かく分割するため、ファイル構成もやや複雑になってしまうが、Generambaというコード生成ツールを活用すれば必要なファイルを適切な構成で作成できます。
現場で選ばれているアーキテクチャ
ここまでiOSの代表的なアーキテクチャについて紹介してきましたが、では実際の現場ではどのアーキテクチャが選ばれているのか、社内のアプリを対象にアンケートを取りました。以下がその結果です。
MVC
とMVP
がそれぞれ約3割ずつを占めており、MVVM
が約2割弱、少数派としてVIPER
やその他のアーキテクチャがありました。実際、数年前に開発が始まったアプリではMVC
が多く、3年以内の新規アプリやリニューアルしたアプリではMVP
やMVVM
、VIPER
などのアーキテクチャが採用されています。その背景として、アジャイル開発やテスト自動化の重視が考えられ、よりテストがしやすく、しっかりと役割分担ができ、継続的な開発を支えるために適切なアーキテクチャが選ばれています。
MVVM
よりもMVP
が選ばれているのは、上でも述べた通り、データバインディングの学習コストが高いため、比較的導入がしやすく、かつテストも行いやすいからです。また、MVP+Clean Architecture
やMVP+Coordinator
など、MVP
に+αしてGUIアーキテクチャの弱点を補うような使い方もあります。また、MVVM
ではRxSwift
などのデータバインディングライブラリを使うと、ライブラリへの依存性が生じてしまうので、ステータス監視でノーバインディングな使い方もあります。
VIPER
を選ぶアプリもありますが、厳密な分割が必要で、学習コストもやや高いため、実際にはMVP
やMVVM
が多く選ばれている傾向があります。
まとめ
本記事では、iOS開発でよく話に上がるアーキテクチャを紹介し、実際の現場で選ばれているアーキテクチャについて考察をしてきました。近年のアジャイル開発やテスト自動化の潮流に合わせて、選べれるアーキテクチャに変化が見られます。しかし、導入にともなう学習コストやアプリの規模との相性も関係し、「これを選べば正解」なアーキテクチャはなく、プロジェクトのメンバーやプロダクトの規模に合わせて、適切なアーキテクチャを選ぶべきと言えます。
また、冒頭でアーキテクチャは「アプリを綺麗に開発・継続的に運用していくための設計方法」だと述べましたが、アーキテクチャはあくまで手段に過ぎず、使い方を間違えても、それに囚われすぎてもダメで、常に「設計」を意識しながら、適切にアーキテクチャを活用していくことが重要です。
参考
- Model-View-Controller
- 【完全保存版!】iOSエンジニアとして働く上で知っておきたい14のこと
- iOSアプリ設計大全集 2016
- iOSをMVC,MVP,MVVM,Clean Architectureで実装してみた
- Webアプリケーション開発者から見た、MVCとMVP、そしてMVVMの違い
- StackOverFlowの「MVPとMVCの違い」についての回答を読んでみた
- 怖くないiOSでのMVVM
- まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて - Qiita
- iOS Clean Architecture - 騒音のない世界 BLOG
- iOS Project Architecture : Using VIPER 和訳 - Qiita
- VIPERアーキテクチャ まとめ - Qiita