MVVM Usecase Repository Architecture with Base Classes: Best Practices and Clean Architecture Compliance

75 Views Asked by At

I am trying to implement the MVVM usecase repository architecture in my Jetpack Compose project. I use a sealed class called Resource to manage service requests:

sealed class Resource<out T : Any?> {
    class Success<T>(val data: T) : Resource<T>()
    class Error<T>(val message: String) : Resource<T>()
}

I am trying to write BaseUseCase, BaseViewModel, and BaseRepository classes that follow best practices and are compatible with modern architectures to listen to the Resource state with when in all view models and avoid emitting Resource separately in all use cases.

abstract class BaseViewModel : ViewModel() {

    private val _baseState = mutableStateOf(false)
    val baseState: State<Boolean> = _baseState
    abstract fun <T> handleEvent(event: T)

    protected fun <T> serviceCall(
        flow: Flow<Resource<T>>,
        onLoading: (() -> Unit)? = null,
        onError: ((String) -> Unit)? = null,
        onSuccess: (T) -> Unit
    ) {
        viewModelScope.launch {
            flow.onStart {  }.collect {
                when (it) {
                    is Resource.Success -> it.data?.let { data ->
                        onSuccess(data)
                    }

                    is Resource.Error -> it.message.let { errorMessage ->
                        onError?.invoke(errorMessage)?.let {

                        } ?: {
                            //popup
                        }
                    }
                }
            }
        }
    }
}
abstract class BaseUseCase<P, R, D> {
    protected abstract suspend fun execute(param: P): Flow<Resource<R>>

    protected abstract fun mapper(response: R): D
    operator fun invoke(req: P): Flow<Resource<D>> = flow {
        execute(req).collect {
            when (it) {
                is Resource.Success -> {
                    emit(Resource.Success(mapper(it.data)))
                }

                is Resource.Error -> {
                    emit(Resource.Error(it.message))
                }
            }
        }
    }
}
open class BaseRepository {
    fun <T> retrieveData(call: () -> T): Flow<Resource<T>> = flow {
        try {
            emit(Resource.Success(call()))
        } catch (e: Exception) {
            val errorCode = when (e) {
                is IOException -> ErrorCode.NETWORK_ERROR
                is HttpException -> ErrorCode.SERVER_ERROR
                else -> ErrorCode.UNKNOWN_ERROR
            }
            emit(Resource.Error(errorCode.message))
        }
    }.flowOn(Dispatchers.IO)
}

Questions:

  1. What are the best practices and architectural issues with the above base classes?
  2. How can I implement the Jetpack Compose MVVM usecase repository architecture in compliance with Clean Architecture principles today?
  3. Is the above architecture appropriate? If not, do you have any sample production applications or comments?
0

There are 0 best solutions below