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?

【WIP】【Android】MVIアーキテクチャのサンプル実装

Last updated at Posted at 2025-12-16

注意

本記事は、
筆者個人の学習内容を整理するためのアウトプット
としてまとめたものです。
マサカリ歓迎します。

1-1. 概要

OpenWeatherAPIで東京の天気データを取得し、
Viewに表示するまでの流れをサンプルコード付きで記載してます。
アーキテクチャはAndroid MVVM + MVI(UDF)になります。

1-2. 全体構成図

View (Compose)
  ↓
ViewModel
  ↓
UseCase(機能単位で1つ)
  ↓
Repository
  ↓
OpenWeather API

1-3. 設計方針

以下に基づき、サンプルコードを含めて記載します。
・Viewは状態を表示するだけ
・ViewModelは状態遷移を管理する
・Stateはsealed + Factory で生成する
・UseCaseはfeature単位で1つ

2-1. OpenWeather API(Model)

data class WeatherResponse(
    val weather: List<Weather>,
    val main: Main
)

data class Weather(
    val main: String,
    val description: String
)

data class Main(
    val temp: Double
)

2-2. Repository

interface WeatherRepository {
    suspend fun fetchTokyoWeather(): WeatherResponse
}
class WeatherRepositoryImpl(
    private val api: OpenWeatherApi
) : WeatherRepository {

    override suspend fun fetchTokyoWeather(): WeatherResponse {
        return api.getWeather(
            city = "Tokyo",
            apiKey = BuildConfig.OPEN_WEATHER_API_KEY
        )
    }
}

2-3. UseCase

class LoadTokyoWeatherUseCase(
    private val repository: WeatherRepository
) {
    suspend operator fun invoke(): WeatherResponse {
        return repository.fetchTokyoWeather()
    }
}

2-4. State(Factoryメソッド使用)

sealed interface WeatherState {

    object Idle : WeatherState
    object Loading : WeatherState

    data class Success(
        val description: String,
        val temp: Double
    ) : WeatherState

    data class Error(
        val message: String
    ) : WeatherState

    companion object {
        fun idle() = Idle
        fun loading() = Loading

        fun success(response: WeatherResponse) =
            Success(
                description = response.weather.first().description,
                temp = response.main.temp
            )

        fun error(message: String) =
            Error(message)
    }
}

State Factory メソッド化の理由
・状態生成ロジックをViewModelから排除
・copy の引数漏れを防止
・状態遷移を明文化
Stateの生成をFactoryメソッドに集約することで、
ViewModelは「いつ遷移するか」だけを判断する役割になります。

2-5. ViewModel(reduce + Factory)

class WeatherViewModel(
    private val loadTokyoWeatherUseCase: LoadTokyoWeatherUseCase
) : ViewModel() {

    private val _state =
        MutableStateFlow<WeatherState>(WeatherState.idle())
    val state: StateFlow<WeatherState> = _state

    fun loadWeather() {
        viewModelScope.launch {
            reduce { WeatherState.loading() }

            val result = runCatching {
                loadTokyoWeatherUseCase()
            }

            reduce {
                result.fold(
                    onSuccess = {
                        WeatherState.success(it)
                    },
                    onFailure = {
                        WeatherState.error("天気情報の取得に失敗しました")
                    }
                )
            }
        }
    }

    private fun reduce(
        reducer: () -> WeatherState
    ) {
        _state.value = reducer()
    }
}

2-6. View(Compose)

@Composable
fun WeatherScreen(
    viewModel: WeatherViewModel
) {
    val state by viewModel.state.collectAsState()

    LaunchedEffect(Unit) {
        viewModel.loadWeather()
    }

    when (state) {
        is WeatherState.Idle -> Text("準備中")
        is WeatherState.Loading -> CircularProgressIndicator()

        is WeatherState.Success -> {
            val success = state as WeatherState.Success
            Text(
                text = "東京の天気: ${success.description}\n気温: ${success.temp}℃"
            )
        }

        is WeatherState.Error -> {
            Text("エラーが発生しました")
        }
    }
}

まとめ

MVVM と MVI は対立するものではなく、
State管理をどう設計するかの違いだと考えています。
reduce経由で状態を一つにまとめてViewに返却する設計にすることで
複数のデータ取得時に状態が複雑化するのを防ぎ、運用面での恩恵が強いと考えています。

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?