筆者について
石田 湊
4年制の情報系専門学校に在籍。
学校の授業内でwebアプリケーション開発(React)、独学でモバイルアプリ開発(Flutter,Swift)を学習中。
はじめに
これまで個人開発やアルバイト等でアプリ開発の経験を積んできたものの、「アーキテクチャ設計」と聞くとなんだか難しそうなイメージが先行してしまい、あまり詳しく勉強できずにいました。
しかし開発規模が少しずつ大きくなるにつれ、MVC、MVP、MVVM、BLoC、TCA……と、数多くのアーキテクチャから最適な物を選定しないといけない状況が多くなりました。「結局どれを使えばいいの?」「全部覚えないといけないの?」と混乱していましたが、意を決してそれぞれの歴史と「なぜそれが生まれたのか」を辿ってみると、実はすべてが「前の時代の弱点を克服するため進化している」ことに気がつきました。
今回は、そもそもアーキテクチャを選定する意味とは何か、そしてUIとロジックの分離を巡る歴史について、自分なりの解釈をまとめてみたいと思います。
そもそも、なぜアーキテクチャを選定するのか?
ざっくりとしか知らなかったアーキテクチャですが、学ぶうちに「なぜ導入するのか」「どうやって選ぶのか」が徐々に見えてきました。
アーキテクチャを導入する「意味」
一言で言えば、 「アプリが成長しても、チームやコードが崩壊しないようにするため」 です。
アーキテクチャという「コードの置き場所とデータの流れの絶対的なルール」を敷くことで、以下のメリットが生まれます。
- 関心の分離 :「画面の見た目」と「裏側の処理」を切り離し、分業しやすくする。
- 保守性 :画面のデザインを大幅に変えても、裏側のロジックには一切影響が出ない。
- テスト容易性 :画面をポチポチ押さなくても、ロジックが正しく動くか自動テストができる。
アーキテクチャを選定する「条件」
では、常に一番厳格で最強のアーキテクチャを選べばいいかというと、そうではありません。プロジェクトによっては過剰になっていたりする場合もあるからです。
例えば、スポーツ大会にスーツを着て行ったり、結婚式にサンダルで行ったりしたらおかしいですよね?
すなわち、アーキテクチャにもTPOがあるのです。
現場では、主に以下の5つの基準を天秤にかけてアーキテクチャを選定していることが分かりました。
-
プロジェクトの規模と複雑さ
単純にデータをリスト表示するだけなら簡易的なMVVMで十分ですが、複数の画面から同じデータが頻繁に書き換えられるような複雑なアプリの場合、状態の迷子を防ぐためにBLoCやTCAのような「単方向データフロー」が必須になります。 -
チームの規模とスキルレベル
数人のチームなら小回りの利くアーキテクチャで最速で進めますが、数十人が関わる規模になると「個人の書き方の癖」が致命的なバグを生むため、型によって書き方が強制される(誰が書いても同じ構造になる)厳格なルールが求められます。 -
アプリの寿命(ライフサイクル)
数ヶ月で捨てるプロトタイプ(PoC)なら初期スピードが最優先です。しかし、3〜10年運用するアプリであれば、将来のOSアップデートやデザインの大改修に耐えられるよう、初期スピードを犠牲にしてでも保守性を重視した設計にする必要があります。 -
採用するUIフレームワーク
従来の「命令的UI」ならMVCやMVPが自然ですが、現代の「宣言的UI」を採用する場合は、状態監視と相性の良いMVVMやTCAなどが前提となります。 -
テストの重要度(品質要求)
金融や医療アプリなど「絶対にバグが許されない」ドメインでは、画面をポチポチ操作しなくても、裏側のロジック単体を100%自動でテストできること(テスト容易性)が絶対条件になります。
宣言的UIと命令的UIの例
宣言的UI:-
Flutter (Dart)
-
SwiftUI (Swift)
-
React / React Native (JavaScript/TypeScript)
-
Jetpack Compose (Kotlin)
-
Vue.js (JavaScript/TypeScript)
命令的UI:
-
UIKit (Swift/Objective-C)
-
Android View System (Java/Kotlin)
-
素のJavaScript / jQuery (JavaScript)
-
Windows Forms (C# / C++)
-
Java Swing (Java)
規模別のアーキテクチャ選定条件
これらの基準を総合すると、プロジェクトの規模に合わせて以下のように使い分けられることが多いようです。
| プロジェクト規模・内容 | 具体例 | 適したアーキテクチャ | 理由と特徴 |
|---|---|---|---|
| 小規模・短期 | キャンペーンアプリ、社内ツール、ハッカソンでの開発 | MVC / 簡易的なMVVM | 複雑なルールは不要。何よりも 「開発スピード(初期リリース)」 を最優先にする。 |
| 中規模・成長前提 | 一般的なSNS、ECアプリ、ツール系アプリ | MVVM | スピードと保守性のバランスが最も良い。UIの状態監視の管理が容易。 |
| 大規模・高複雑度 | 金融系アプリ、巨大インフラアプリ、数十人規模での開発 | BLoC / TCA | 「絶対にバグを起こさない堅牢性」 が最優先。ルールをガチガチにして、大勢で触ってもコードが破綻しないようにするため。 |
このように、個人開発や新規事業の立ち上げフェーズでは「スピード重視」でMVVMなどが採用され、数年運用されて組織が大きくなったタイミングで「堅牢性重視」のBLoCやTCAなどへリプレイスされる、というケースもよくあります。
この「スピード」と「堅牢性」の天秤の中で、アーキテクチャは進化を遂げてきました。
アーキテクチャ3兄弟の変遷(MVC → MVP → MVVM)
すべての基本となるこの3つは、「誰がどうやって画面(View)を更新するのか」という矢印の向きに進化の鍵があります。
| アーキテクチャ | 画面の更新方法 | 抱えやすい課題 |
|---|---|---|
| MVC | Controllerが直接Viewをいじる | Fat Controller(肥大化) |
| MVP | Presenterがインターフェース越しに命令する | 記述量の増加・使い回しの難しさ |
| MVVM | ViewModelの状態をViewが監視して自動更新 | 処理の流れが追いにくい |
1. MVC:すべての原点
最も歴史が古いMVC(Model-View-Controller)ですが、モバイルアプリ開発においては、画面(View)と操作受付(Controller)が実質的にくっついてしまうという構造上の問題がありました。
通信処理から画面の書き換えまで、すべてを現場監督であるControllerが担ってしまい、数千行に及ぶ「Fat Controller」という巨大なモンスターを生み出す原因になってしまいました。

2. MVP:ロジックとUIの分離
「ロジックとUIをきっちり切り離そう!」というMVCの反省から生まれたのがMVP(Model-View-Presenter)です。
ViewとPresenterの間にインターフェースという厳格な契約を結ぶことで、ロジックだけを切り離してテストすることが劇的に簡単になりました。
しかし、MVPには 「汎用的なコンポーネント化(使い回し)が苦手」 という大きな弱点がありました。
PresenterはViewに対して「このラベルにこの文字を出せ」「このぐるぐるを表示しろ」と 具体的な命令(How) を出します。命令が具体的すぎるため、少しでもデザインや要件が違う別の画面にPresenterを使い回そうとすると、インターフェースが噛み合わなくなり、結果的にコード量が多くなってしまう課題がありました。

3. MVVM:宣言的UI時代のスタンダード
「いちいち画面に命令を出すのが面倒」「ロジックをもっと部品化して使い回したい」という要求から、現代の主流であるMVVM(Model-View-ViewModel)です。
ViewModelは「名前は〇〇です」という 状態(State:データ)だけ を出力し、画面には一切命令しません。画面(View)側が勝手にViewModelの状態を監視(データバインディング)し、データが変わったら自動で自分を描き直します。
この「画面への直接的な命令を手放し、状態だけを管理する」というアプローチは、近年の「宣言的UI」の登場によって、スタンダードとなりました。

現代のトレンド:BLoC / TCA
MVVMによって画面の更新は自動化されましたが、アプリがさらに巨大化すると「どこから状態が変更されているのかわからない」という新たな問題が発生します。そこで現代の大規模開発では、「データの流れを一方通行にする(単方向データフロー)」を強制するアーキテクチャが登場しています。
BLoC
Flutterの現場で特に話題に挙げられるgoogle推奨のアーキテクチャです。データの入り口(Event)と出口(State)を完全に分け、その間をStream(非同期のパイプ)で繋ぎます。「画面側から状態を直接書き換えることを文法レベルで物理的に禁止する」という超・厳格なルールを敷くことで、大規模チームでも絶対に破綻しない堅牢さを手に入れています。

TCA (The Composable Architecture)
iOS(SwiftUI)界隈で熱狂的な支持を集めている最先端のフレームワークです。MVP時代に叶わなかった「ロジックのコンポーネント化」を極限まで突き詰めています。
「いいねボタン」のようなミクロなロジックから「画面全体」のマクロなロジックまで、すべての状態と処理を美しく組み立てていくことができます。

コラム:ディレクトリ構成も進化している
アーキテクチャの進化に合わせて、ファイルの置き場所(ディレクトリ構成)も変わってきています。
昔のMVC/MVVMプロジェクトでは、Models/ Views/ Controllers/ という 「役割(レイヤー)別」 にフォルダを分けるのが主流でした。しかし、これだと1つのログイン機能を作るためにあちこちのフォルダを反復横跳びしなければなりません。
現代のTCAやFlutter開発では、Features/Login/ のように 「機能ごと」 にフォルダを作り、その中にViewもViewModelもModelもすべて詰め込む構成(Feature-First)が主流になりつつあります。「どこに何があるか」が直感的に分かり、チーム開発でのコンフリクト(競合)も防ぎやすくなるためです。
まとめ:正解はない
歴史を辿ってみて深く実感したのは、 「すべてにおいて完璧なアーキテクチャは存在しない」 ということです。
- 綺麗に分けようとすればコード量が増える(MVP)
- 自動化しようとすれば処理の追跡が難しくなる(MVVM)
- 堅牢性を極めようとすればルールが厳しくなり過ぎてしまい、小回りが利かなくなる(BLoC/TCA)
これまで「なんか難しそう」と敬遠していましたが、それぞれのアーキテクチャが「当時どんな課題を解決したくて、代わりに何を妥協したのか」という歴史を知ることで、一気に親近感が湧きました。
今後は、目の前のプロダクトやチームの状況に合わせて、自信を持って選定ができるよう、更に経験を積んでいきたいと思います。