🐦

Riverpod時代のFlutter開発において、MVVMを気にする必要はもうない話

に公開

この記事は、普段 Flutter と Riverpod を使っていて感じたことをまとめたものです。
実務歴が長いとかではなく、「色々試しているうちにこう思うようになった」という個人的な経験に基づいています。

Flutter のアーキテクチャ論争は昔から定期的に起きます。

  • MVVM が良い
  • Clean Architecture が良い
  • BLoC パターンが王道
  • Controller パターンが簡潔
  • Redux もまだ捨てたものじゃない

しかし、Riverpod をしっかり使うほど「アーキテクチャの名前って意味ある?」と感じ始めます。

結論から言うと、私はこう考えています:

Riverpodを正しく使っていれば、MVVMやClean Architectureといった"パターン名"を深く気にする必要はほとんどない。
なぜなら Riverpod が本質的な問題をすべて解決してくれるからです。

この記事では「なぜMVVMを気にしなくてよくなるのか」「Riverpodがどのようにアーキテクチャ問題を吸収しているのか」を整理していきます。


なぜアーキテクチャ論争が続くのか

まず前提として、アーキテクチャ論争が起きるのは「UI とロジックをどう分離するか」が長年の課題だったからです。

  • Android は MVP → MVVM → Clean Architecture
  • iOS は MVC → MVVM → VIPER
  • Web も Redux / Flux / MVVM など紆余曲折

UI とロジックを混ぜると破綻する。
だから分離の方法論が求められた。

Flutter も最初期は Provider / BLoC / Redux など、多くの選択肢が混在していました。


MVVM の“ViewModel”は何をする役割なのか?

MVVM の中心である ViewModel は、伝統的に以下の責務を持っています:

  • 状態を保持する
  • 状態を整形して View に渡す
  • UI からの入力を受ける
  • API や Repository を呼ぶ
  • 非同期ロジックを実行する
  • 状態更新を通知する

つまり、UI に直接書いてはいけないロジックを VM に隔離するための仕組み です。


Riverpod を使うと、この「ViewModel の仕事」がすべて Provider に吸収される

典型的な Riverpod のコードを見てみます。

final counterProvider =
    NotifierProvider<CounterNotifier, int>(CounterNotifier.new);

class CounterNotifier extends Notifier<int> {
  
  int build() => 0;

  void increment() => state++;
}

これを UI 側では:

final count = ref.watch(counterProvider);

この時点で、ViewModel が担うべき仕事の多くが Riverpod によって実現されています。

✔ 状態管理
✔ 状態の公開
✔ UI からのアクション受付
✔ ロジック実行
✔ 状態更新の通知(リビルド)
✔ 依存関係の注入

ほとんどできてしまっている。

つまり、ViewModelという名前のクラスをわざわざ自分で用意しなくても MVVM の構造は成立してしまいます。

これは重要なポイントです。


Riverpodは"UIとロジックの分離"を公式に保証している

Riverpod の公式ドキュメントには、次のように記載されています。

Riverpod公式 原文(Provider vs Riverpod より)

“With Riverpod, providers are not widgets. Instead they are plain Dart objects. Similarly, providers are defined outside of the widget tree, and instead are declared as global final variables.”
https://riverpod.dev/docs/from_provider/provider_vs_riverpod

つまり:

  • Provider は widget ではない
  • Provider は widget ツリーの外で定義される
  • (= UI にロジックを書かない)

これはまさに MVVM の本質である

  • UI とロジックの分離

を、フレームワークレベルで保証しているということです。


Provider の依存グラフが、そのままアーキテクチャになる

Riverpod は DI(依存注入)も同時に扱えます。

final apiProvider = Provider((ref) => ApiClient());

final repoProvider = Provider(
  (ref) => UserRepository(ref.watch(apiProvider)),
);

final userControllerProvider =
    NotifierProvider<UserController, UserState>(
      () => UserController(),
    );

この3つの Provider だけで:

UI → Controller(=ViewModel) → Repository → API

という 自然な責務分離構造 が完成します。

しかもこれを「MVVM」と呼んでも「Clean Architecture」と呼んでも、内部構造はほとんど同じです。

Riverpodが正しい構造を強制するためです。


結局、アーキテクチャ論争は不毛になる

  • Riverpod を使えば UI とロジックは自然と分離される
  • データアクセスも Provider が分離してくれる
  • 依存関係も Provider が表現する
  • ViewModel という命名さえ形式的になる

だから 「MVVM なの? Clean? BLoC?」という議論はどんどん意味を失う


では、どう呼べばいいのか?

答えはシンプルです。

“Riverpodアーキテクチャ”と呼べばいい。

Provider StateNotifier AsyncNotifier が
UI とロジックの分離・依存関係管理・状態更新の多くをカバーしてくれるため、
もはや"伝統的なアーキテクチャ名"を使う意味が薄くなっています。


まとめ:Riverpod を正しく使っているなら、MVVMを意識する必要はほぼない

  • Provider が ViewModel の仕事をほぼ吸収する
  • UI とロジックは自然に分離される
  • 依存関係も Provider が表現する
  • パターン名を気にする必要が薄くなる

現代の Flutter ではアーキテクチャは Riverpod の上で自然に成立するので、
私たちはただ 「責務分離と分かりやすいコード」 に集中すればよいのだと思います。

Discussion