0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Architecture】MVI Architecture(Model-View-Intent)

Last updated at Posted at 2025-10-02

はじめに

MVP や MVVM の進化形とも言える MVI は、単方向データフロー(Unidirectional Data Flow, UDF) を重視し、UI を「状態の関数」として設計します。


MVI の3つの要素

MVI は以下の3つで構成されます。

1. Model(状態)

アプリケーションの状態(State)を表す。
Immutable(不変)なデータとして定義し、UI はこの状態を監視して描画します。

data class UiState(
    val isLoading: Boolean = false,
    val items: List<String> = emptyList(),
    val error: String? = null
)

2. View(UI)

状態を受け取り、描画する役割。
ユーザー操作は Intent として ViewModel に送ります。

@Composable
fun SampleScreen(state: UiState, onIntent: (UiIntent) -> Unit) {
    if (state.isLoading) {
        CircularProgressIndicator()
    } else {
        LazyColumn {
            items(state.items) {
                Text(it)
            }
        }
    }
}

3. Intent(ユーザー操作)

ユーザーの操作やイベントを表す。
「こういうことをしたい」という意思を ViewModel に伝えるためのもの。

sealed class UiIntent {
    object LoadData : UiIntent()
    data class ClickItem(val id: Int) : UiIntent()
}

データフロー

MVI の最大の特徴は 単方向データフロー です。

ユーザー操作 → Intent → Reducer(ViewModel) → 新しい State → View(UI)
  1. View がユーザー操作を Intent として ViewModel に渡す
  2. ViewModel が Reducer で新しい State を計算
  3. State が更新されると View が再描画

このループにより、UI は 常に State に依存する純粋関数 のように振る舞います。


MVI のメリット

  • 状態の一元管理 → バグを減らせる
  • 一方向のデータフロー → デバッグが容易
  • 過去の State を保存すれば タイムトラベルデバッグ が可能
  • View がシンプル → テストがしやすい

MVI のデメリット

  • State の定義が膨大になりがち
  • Reducer の設計が複雑化する可能性
  • 全てを State に含めるため メモリコスト が増えることもある

他のアーキテクチャとの比較

パターン 特徴
MVP Presenter がロジックを担当。状態は明示的に管理されない
MVVM ViewModel が双方向データバインディングを行う場合が多い
MVI Intent → State → View の 一方向フロー に限定

Flutter/Compose での実装例

Flutter の場合は以下のように整理すると良いです。

  • UiIntent: sealed class(Dartなら freezedsealed_unions を利用)
  • UiState: freezed で生成
  • ViewModel: StateNotifier(Riverpod)を利用して Reducer を実装
@freezed
class UiState with _$UiState {
  const factory UiState({
    @Default(false) bool isLoading,
    @Default([]) List<String> items,
    String? error,
  }) = _UiState;
}

@freezed
class UiIntent with _$UiIntent {
  const factory UiIntent.loadData() = LoadData;
  const factory UiIntent.clickItem(int id) = ClickItem;
}

class UiViewModel extends StateNotifier<UiState> {
  UiViewModel() : super(const UiState());

  void onIntent(UiIntent intent) {
    intent.when(
      loadData: _loadData,
      clickItem: _onClick,
    );
  }

  void _loadData() {
    state = state.copyWith(isLoading: true);
    // API呼び出しなど…
  }

  void _onClick(int id) {
    // クリック処理
  }
}

まとめ

  • MVI は 状態駆動のUI を実現するためのパターン
  • 単方向データフローで構造がシンプルになり、デバッグやテストが容易
  • 特に Jetpack Compose, Flutter, React のような宣言的 UI と相性抜群

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?