26、架构模式:MVVM与Compose、MVI模式、单向数据流实践、Clean Architecture

聊到架构模式,很多新手会觉得这是“大项目才需要的东西”。
其实不然。我见过太多小项目,一开始图省事,所有逻辑全塞在Composable里,结果三个月后连自己都看不懂。
嗯,今天我们就来把这几个模式彻底讲透。

一、MVVM + Jetpack Compose:官方推荐的标准姿势

MVVM(Model-View-ViewModel)是Google官方推荐的架构。
在Compose里,它的核心思路就一句话:UI不存数据,数据驱动UI

我个人习惯把ViewModel当作“状态管家”。
它持有StateFlowMutableState,Composable只负责订阅和渲染。

// ViewModel 层
class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count: StateFlow<Int> = _count.asStateFlow()

    fun increment() {
        _count.value += 1
    }
}

// Composable 层
@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()
    Button(onClick = { viewModel.increment() }) {
        Text("点击了 $count 次")
    }
}
我的经验:ViewModel里不要持有Composable引用,也不要做任何UI操作。它只负责业务逻辑和状态管理。我曾经见过同事把Context传进ViewModel,结果内存泄漏查了两天。

二、MVI模式:让状态变化可追溯

MVI(Model-View-Intent)是MVVM的升级版。
它强调单向数据流不可变状态

说白了,MVI把用户操作抽象成“意图(Intent)”,所有状态变化都通过Reducer处理。
这样做的好处是:每个状态变化都有迹可循,调试起来特别爽。

// 定义状态
data class CounterState(val count: Int = 0, val isLoading: Boolean = false)

// 定义意图
sealed class CounterIntent {
    object Increment : CounterIntent()
    object Decrement : CounterIntent()
    object Reset : CounterIntent()
}

// ViewModel 实现
class CounterViewModel : ViewModel() {
    private val _state = MutableStateFlow(CounterState())
    val state: StateFlow<CounterState> = _state.asStateFlow()

    fun processIntent(intent: CounterIntent) {
        _state.update { currentState ->
            when (intent) {
                is CounterIntent.Increment -> currentState.copy(count = currentState.count + 1)
                is CounterIntent.Decrement -> currentState.copy(count = currentState.count - 1)
                is CounterIntent.Reset -> currentState.copy(count = 0)
            }
        }
    }
}
MVI vs MVVM:MVVM允许ViewModel直接暴露多个StateFlow,而MVI强制你走一个统一的Intent通道。我个人在复杂业务中更倾向MVI,因为状态变化一目了然。

三、单向数据流:Compose的底层哲学

Compose本身就是单向数据流的产物。
父组件通过参数传数据给子组件,子组件通过回调通知父组件。

你想想看,如果子组件能直接修改父组件的状态,那调试起来得多痛苦?
单向数据流的核心原则:数据向下流动,事件向上传递

@Composable
fun Parent() {
    var text by remember { mutableStateOf("") }
    Child(text = text, onTextChange = { text = it })
}

@Composable
fun Child(text: String, onTextChange: (String) -> Unit) {
    TextField(value = text, onValueChange = onTextChange)
}
避坑指南:我曾经在项目中把ViewModel直接传给子Composable,让子组件直接调用viewModel的方法。结果状态被多处修改,bug查了三天。记住:子组件永远只通过回调通知父组件。

四、Clean Architecture:分层解耦的艺术

Clean Architecture把代码分成三层:
数据层(Data)领域层(Domain)展示层(Presentation)

我刚开始接触时觉得太啰嗦,一个小功能要写三个类。
但后来项目规模大了,才发现分层的好处:替换数据库、切换网络库,只需要改数据层,其他层完全不动。

层级 职责 典型内容
Data 数据来源 Repository实现、Room DAO、Retrofit API
Domain 业务逻辑 UseCase、Entity、Repository接口
Presentation UI展示 ViewModel、Composable、State
// Domain 层:定义接口
interface UserRepository {
    suspend fun getUser(id: String): User
}

// Data 层:实现接口
class UserRepositoryImpl(private val api: UserApi) : UserRepository {
    override suspend fun getUser(id: String): User {
        return api.fetchUser(id).toDomain()
    }
}

// Presentation 层:使用
class UserViewModel(private val repository: UserRepository) : ViewModel() {
    fun loadUser(id: String) {
        viewModelScope.launch {
            val user = repository.getUser(id)
            _state.update { it.copy(user = user) }
        }
    }
}
我的建议:小项目可以先用MVVM + 单向数据流,等业务复杂了再引入Clean Architecture。不要一开始就上全套,容易过度设计。

五、架构模式对比与选择

我整理了一张对比表,方便你根据项目情况选择:

模式 适用场景 复杂度 可测试性
MVVM 中小型项目、快速迭代
MVI 复杂交互、需要状态回溯
Clean Architecture 大型项目、多团队协作 极高

我个人习惯:
小工具用MVVM,业务复杂的模块用MVI,整个项目用Clean Architecture组织。
三者并不冲突,可以混合使用。

六、架构核心逻辑图

下面这张SVG图,展示了MVVM、MVI、Clean Architecture之间的关系:

Android 架构模式核心逻辑 Presentation 层 Domain 层 Data 层 Composable / ViewModel UseCase / Repository接口 Repository实现 / DAO UI事件 业务逻辑 数据返回 状态更新 MVI 核心:Intent → Reducer → State → UI → Intent(闭环) MVVM + MVI + Clean Architecture 可共存,不冲突

七、总结

架构模式没有银弹。
MVVM适合快速开发,MVI适合复杂状态管理,Clean Architecture适合大型项目。
我建议你从MVVM入手,逐步引入MVI的单向数据流思想,等团队成熟了再上Clean Architecture。

记住:架构是为了让代码更容易维护,而不是为了炫技
选最适合你当前项目的模式,比选最流行的模式重要得多。

一句话总结:MVVM是基础,MVI是进阶,Clean Architecture是终极形态。三者可以共存,关键看你的项目需要什么。

公众号:蓝海资料掘金营,微信deep3321