Arquitectura Android Completado

MVVM Architecture

Ejemplo completo de arquitectura MVVM con Clean Architecture demostrando las mejores prácticas de desarrollo Android

MVVM Architecture Demo

Descripción del Proyecto

Este proyecto demuestra una implementación completa de la arquitectura MVVM (Model-View-ViewModel) combinada con principios de Clean Architecture. Sirve como ejemplo de las mejores prácticas recomendadas por Google para el desarrollo de aplicaciones Android escalables y mantenibles.

La aplicación incluye separación clara de responsabilidades, inyección de dependencias, testing unitario, y patrones de diseño que facilitan el desarrollo en equipo y la evolución del código a largo plazo.

Capturas de Pantalla

Lista principal

Lista principal con datos

Estado de carga

Estado de carga con indicador

Vista de detalle

Vista de detalle de elemento

Tecnologías Utilizadas

🏗️

MVVM Pattern

Separación de responsabilidades

🧹

Clean Architecture

Arquitectura por capas

👁️

Observer Pattern

Comunicación reactiva

Kotlin

Lenguaje moderno

🔄

LiveData

Datos observables

🎯

ViewModel

Gestión de estado UI

Estructura de la Arquitectura

Capas de Clean Architecture

// Capa de Dominio - Use Cases
class GetUserListUseCase(
    private val userRepository: UserRepository
) {
    suspend operator fun invoke(): Result<List<User>> {
        return try {
            val users = userRepository.getUsers()
            Result.success(users)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

// Capa de Datos - Repository Implementation
class UserRepositoryImpl(
    private val remoteDataSource: UserRemoteDataSource,
    private val localDataSource: UserLocalDataSource
) : UserRepository {
    
    override suspend fun getUsers(): List<User> {
        return try {
            val remoteUsers = remoteDataSource.fetchUsers()
            localDataSource.saveUsers(remoteUsers)
            remoteUsers
        } catch (e: Exception) {
            localDataSource.getCachedUsers()
        }
    }
}

ViewModel con LiveData

class MainViewModel(
    private val getUserListUseCase: GetUserListUseCase
) : ViewModel() {
    
    private val _uiState = MutableLiveData<UiState<List<User>>>()
    val uiState: LiveData<UiState<List<User>>> = _uiState
    
    private val _isLoading = MutableLiveData<Boolean>()
    val isLoading: LiveData<Boolean> = _isLoading
    
    fun loadUsers() {
        viewModelScope.launch {
            _isLoading.value = true
            _uiState.value = UiState.Loading
            
            getUserListUseCase()
                .onSuccess { users ->
                    _uiState.value = UiState.Success(users)
                }
                .onFailure { error ->
                    _uiState.value = UiState.Error(error.message ?: "Unknown error")
                }
            
            _isLoading.value = false
        }
    }
    
    fun refreshUsers() {
        loadUsers()
    }
}

Fragment como Vista

class MainFragment : Fragment() {
    
    private var _binding: FragmentMainBinding? = null
    private val binding get() = _binding!!
    
    private val viewModel: MainViewModel by viewModels {
        MainViewModelFactory(
            getUserListUseCase = GetUserListUseCase(
                userRepository = UserRepositoryImpl(
                    remoteDataSource = UserRemoteDataSourceImpl(),
                    localDataSource = UserLocalDataSourceImpl()
                )
            )
        )
    }
    
    private lateinit var userAdapter: UserAdapter
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        return binding.root
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        setupRecyclerView()
        observeViewModel()
        setupSwipeRefresh()
        
        viewModel.loadUsers()
    }
    
    private fun observeViewModel() {
        viewModel.uiState.observe(viewLifecycleOwner) { state ->
            when (state) {
                is UiState.Loading -> showLoading()
                is UiState.Success -> showUsers(state.data)
                is UiState.Error -> showError(state.message)
            }
        }
        
        viewModel.isLoading.observe(viewLifecycleOwner) { isLoading ->
            binding.swipeRefresh.isRefreshing = isLoading
        }
    }
}