MVVM/DDD/整洁架构:ViewModel是否应向View暴露领域模型?
ViewModel暴露领域模型是否合理?View数据预处理应放在何处?
尽管这个问题关联Android平台,但本质是独立于平台的架构基础问题。我学习了MVVM、DDD及整洁架构的相关概念,也研究了GitHub上的大量示例,但始终对一个问题存疑:ViewModel向View暴露领域模型的做法是否合理,以及View所需数据应在何处完成预处理。
比如我们有这样的领域模型:
data class News( val title: String, val content: String, val isFeatured: Boolean, val publishedAt: LocalDateTime, val tags: List<Tag> )
实现新闻列表的ViewModel时,现有示例普遍是这么写的:
@HiltViewModel class NewsListViewModel @Inject constructor( private val newsRepository: NewsRepository, ) : ViewModel() { // 也可以用UiState或MutableStateFlow包装 private val _news: MutableLiveData<List<News>> = MutableLiveData() val news: LiveData<List<News>> fun loadData() { // 示例,实际可以用Flow、RxJava、CompletionBlock等实现 val loadedNews: List<News> = newsRepository.getNews() news.postValue(loadedNews) } fun onNewsClicked(news: News) { // 处理点击逻辑... } }
不管View是用Compose、带RecyclerView的Fragment哪种方式消费这些数据,最终都得自己处理领域模型来完成展示。所以我想问:遵循MVVM与整洁架构的规范,在ViewModel中定义View所需的全部数据与信息会不会更合理、更整洁?
我设想的写法如下:
首先定义适配View的数据类:
// 命名还不确定,如果不需要复用,可以作为ViewModel的嵌套类,比如NewsListViewModel.Item data class NewsListItemViewModel( val title: String, val content: AnnotatedString, val featuredIcon: Int, val backgroundColorRes: Int, val publishedAt: String, val tags: List<String>, val onClick: () -> Unit )
然后是对应的ViewModel实现:
@HiltViewModel class NewsListViewModel @Inject constructor( private val newsRepository: NewsRepository, ) : ViewModel() { // 也可以用UiState或MutableStateFlow包装 private val _news: MutableLiveData<List<NewsListItemViewModel>> = MutableLiveData() val news: LiveData<List<NewsListItemViewModel>> fun loadData() { // 示例 val loadedNews: List<News> = newsRepository.getNews() val newsItems = loadedNews.map { news -> NewsListItemViewModel( title = news.title, content = Html.fromHtml(news.content), featuredIcon = if (news.isFeatured) R.drawable.ic_featured else 0, backgroundColorRes = if (news.isFeatured) R.color.bg_featured else R.color.bg_default, publishedAt = dateFormatter.format(news.publishedAt), tags = news.tags.map { it.title }, onClick = { onNewsClicked(news) } ) } news.postValue(newsItems) } fun onNewsClicked(news: News) { // 处理点击逻辑... } }
内容的提问来源于stack exchange,提问作者Kevin Flachsmann
相关产品推荐
相关产品推荐

