はじめに
iOSアプリ開発に携わって数年、私はVIPERアーキテクチャを採用してきました。
そのため、
VIPER = Clean ArchitectureをiOS向けに最適化したもの
という説明を、ほとんど疑うことなく受け入れていました。
しかし、別プロジェクトで MVVM + Clean Architecture を採用したアプリ開発に参加したことで、その認識が大きく揺らぎました。
この記事では、
- なぜVIPERとClean Architectureを混同していたのか
- iOSにClean Architectureを適用して失敗した実例
- なぜその失敗が構造的に起きやすいのか
- それでもVIPERが「Clean ArchitectureをiOS向けに最適化した」と言われる理由
を、
Clean Architectureの考え方を前提にしつつ、iOSアプリ開発の実体験をもとに整理します。
目次
- 1. なぜVIPERとClean Architectureを混同していたのか
- 2. iOSにClean Architectureを適用して失敗した実例
- 3. なぜこの失敗が起きるのか
- 4. それでもVIPERが「iOS向けに最適化されている」理由
- 5. 結論
1. なぜVIPERとClean Architectureを混同していたのか
VIPERを紹介する記事では、よく次のような説明を目にします。
「VIPERはClean ArchitectureをiOSアプリ向けに最適化したアーキテクチャです」
この一文は、正しくもあり、同時に誤解を生みやすい表現でもあります。
1-1. Clean Architectureを「iOS向けにした」という表現の曖昧さ
Clean Architectureは本来、
- UI
- DB
- Framework
といった 外部要因から業務ルールを守るための設計思想 です。
一方でiOSアプリは、
- UIKitという巨大なフレームワークに強く依存し
- ユーザー操作と画面遷移を起点に処理が始まる
という UI主導の世界 にあります。
この前提の違いを理解しないまま、
「Clean ArchitectureをiOS向けにした」
と聞くと、VIPER = Clean Architecture という誤解が生まれやすくなります。
1-2. VIPERの出発点は「Massive View Controller 問題」
VIPERは、Clean Architectureの理論から生まれたというより、
iOS MVCにおける Controller 肥大化問題
への実践的な解答として登場しました。
MVC時代のViewControllerは、次のような責務をすべて抱えていました。
- UIイベント処理
- 画面遷移
- API通信
- データ整形
- 状態管理
- ビジネスロジック
結果として、Controllerは数千行に肥大化し、
テスト不能・保守困難な存在になります。
1-3. VIPERは「Controllerの責務分解」から生まれた
VIPERは、この肥大化したControllerを 役割ごとに分割 します。
MVC時代とVIPERの対応は次の通りです。
| MVC時代 | VIPERで分割された責務 |
|---|---|
| Controller | Presenter(表示ロジック) |
| Controller | Interactor(処理の調整) |
| Controller | Router(画面遷移) |
| Model | Entity(データ構造) |
重要なのは、ここで分割されたVIPERの各コンポーネントがClean Architectureの要素と1対1で対応するわけではない、という点です。
VIPERをClean Architectureの文脈で説明する記事では、
Interactor = UseCase
のように整理されることがあります。
しかしこの対応付けは、設計意図を説明するための便宜的な比喩 に過ぎません。
VIPERは、もともと存在していたControllerの責務を洗い出しそれらを役割ごとに分解した結果として Presenter / Interactor / Router が生まれたという経緯を持っています。
つまり、Controllerが担っていた責務を分解した結果がVIPERであり、Interactorは「UseCaseを表現するために導入された存在」ではなく、Controllerの中にあった処理調整ロジックを切り出した一部 に過ぎません。
2. iOSにClean Architectureを適用して失敗した実例
この失敗は、Clean Architectureそのものを誤解していたというより、
VIPERを長年使ってきた経験が、逆に思考の癖として作用した結果 でした。
VIPERでは、Interactorは
- Presenterから呼ばれ
- RepositoryやAPIを叩き
- 結果を返す
という「画面単位の処理実行層」として実装されることがほとんどです。
そのため私は、
Interactor = UseCase
という説明を前提に、
Interactor的な感覚のままUseCaseを設計・実装 してしまいました。
結果として私は、
UseCaseを「Interactorっぽく」実装する
という過ちを犯しました。
2-1. UseCaseがRepositoryのラッパーになる
以下は、実際に私が書いたものを簡略化・匿名化したコードです。
protocol OrderSummaryUseCase {
func fetchOrderSummary(
storeId: Int64,
sessionId: String
) async -> Result<OrderSummary, DataAccessError>
}
final class OrderSummaryUseCaseImpl: OrderSummaryUseCase {
private let orderRepository: OrderRepository
init(orderRepository: OrderRepository) {
self.orderRepository = orderRepository
}
func fetchOrderSummary(
storeId: Int64,
sessionId: String
) async -> Result<OrderSummary, DataAccessError> {
let query = OrderQuery(
storeId: storeId,
sessionId: sessionId
)
return await orderRepository.fetchOrder(query: query)
}
}
このUseCaseは一見するとClean Architectureに沿っています。
- UseCaseが存在し
- Repositoryは抽象に依存し
- DIもできる
という「正しそうな構造」になっていました。
しかし実装を見返すと、UseCaseがやっていることは、
- 引数をリクエスト用の構造体に詰める
- Repositoryを呼ぶ
- 結果をそのまま返す
だけでした。
2-2. このUseCaseは「業務ルール」を持っていない
そこには、
- 判断
- 制約
- ビジネス上のルール
が一切存在しません。
これは UseCaseではなく、単なるRepositoryラッパー です。
3. なぜこの失敗が起きるのか
この失敗は、設計ミスというより iOSアプリの性質 に起因します。
3-1. iOSアプリは「クライアント」である
多くの業務アプリでは、
- 業務ルール
- 制約
- 判断ロジック
は すでにAPI、つまりバックエンド側に集約 されています。
iOSアプリの役割は、
- データを取得し
- 表示用に整形し
- ユーザー操作を送信する
ことが中心です。
この構造では、
業務ルールの中心をUseCaseとして切り出す
余地がほとんどありません。
4. それでもVIPERが「iOS向けに最適化されている」理由
では、なぜVIPERは
「Clean ArchitectureをiOS向けに最適化した」
と言われるのでしょうか。
4-1. VIPERが最適化したのは「依存関係の方向」
VIPERでは、Clean Architectureの依存関係ルールを意識し、各責務と依存を次のように分離しています。
- UIKit依存は View に閉じ込める
- 表示ロジックとユーザー操作の解釈は Presenter が担う
- 処理の実行やデータ取得の調整は Interactor に集約する
(画面が必要とする処理を、Repository呼び出しや副作用に分解して実行する役割) - 業務データや状態は Entity として純粋なデータ構造に保つ
(ドメインモデルというより、画面が扱う状態の表現) - 画面遷移や画面構築は Router に隔離する

iOS Project Architecture: Using VIPER
この構成により、UIKitや画面遷移といった外部要因は外側に押し出され中心に近いロジックほどフレームワークに依存しないという Clean Architectureと同じ依存関係の方向 を、UI主導なiOSアプリでも実現しています。
4-2. VIPERが「捨てたもの」と「残したもの」
4-1で述べたように、VIPERがClean ArchitectureをiOS向けに最適化した最大のポイントは、依存関係の方向を内向きに保ったこと です。
一方で、VIPERはClean Architectureのすべてを持ち込んだわけではありません。
VIPERは、
業務ルールを中心に据える構造
UseCaseをアプリケーションの核に据える設計
といった サーバーサイド寄りの前提 は、意図的に採用していません。
その代わりにVIPERが行ったのは、
MVCというiOS標準の構造を、Clean Architectureの依存ルールに沿って再分解すること でした。
つまりVIPERは、Clean ArchitectureをiOSに移植したものでもUseCase中心構造を再現したものでもなく、UI主導なiOSアプリという現実を受け入れたうえで、 Clean Architectureの「依存の考え方」だけを抽出して適用した構造 だと捉えることができます。
この点において、VIPERは確かに「Clean ArchitectureをiOS向けに最適化したアーキテクチャ」と呼ばれる理由を持っているのです。
5. 結論
- VIPERとClean Architectureは別物
- 混同されやすいのは、名前と図が似ているから
- iOSにClean Architectureをそのまま適用すると失敗しやすい
- VIPERは
iOSというUI主導環境でClean Architectureの思想を活かすための現実解
私は以前、
VIPERを使っている = Clean Architectureを理解している
と思っていました。
今はこう考えています。
VIPERを理解したからこそ、
Clean Architectureを「使えない理由」まで理解できた
この記事が、同じ違和感を抱いた方の整理材料になれば幸いです。