Skip to main content

Repository Interfaces

Repository interfaces define the contracts for data access operations in the domain layer. Following Clean Architecture principles, these interfaces reside in the domain layer while their implementations are in the data layer.

ProductRepository

Defines operations for obtaining product data from Mercado Libre.

Interface Definition

interface ProductRepository {
    suspend fun searchProducts(query: String): Result<List<Product>>
    suspend fun getProductDetail(id: String): Result<ProductDetail>
}

Methods

searchProducts

Performs a product search based on a query term.
suspend fun searchProducts(query: String): Result<List<Product>>
query
String
required
Search text to query active products
returns
Result<List<Product>>
Result containing a list of Product models on success, or a mapped failure
Usage Example:
class GetProductsUseCase @Inject constructor(
    private val repository: ProductRepository
) {
    suspend operator fun invoke(query: String): Result<List<Product>> {
        return repository.searchProducts(query)
    }
}

// In a ViewModel or use case
val result = productRepository.searchProducts("laptop")
result.fold(
    onSuccess = { products ->
        println("Found ${products.size} products")
    },
    onFailure = { error ->
        println("Search failed: ${error.message}")
    }
)

getProductDetail

Retrieves complete details of a specific product by its identifier.
suspend fun getProductDetail(id: String): Result<ProductDetail>
id
String
required
Unique product identifier (e.g., “MCO12345”)
returns
Result<ProductDetail>
Result containing the ProductDetail model if found, or a failure otherwise
Usage Example:
class GetProductDetailUseCase @Inject constructor(
    private val repository: ProductRepository
) {
    suspend operator fun invoke(id: String): Result<ProductDetail> {
        return repository.getProductDetail(id)
    }
}

// In a ViewModel
viewModelScope.launch {
    val result = productRepository.getProductDetail("MCO12345")
    result.fold(
        onSuccess = { detail ->
            _uiState.value = UiState.Success(detail)
        },
        onFailure = { error ->
            _uiState.value = UiState.Error(error.message)
        }
    )
}

TokenRepository

Contract for managing authentication tokens required for authorized API requests to Mercado Libre.

Interface Definition

interface TokenRepository {
    fun getAccessToken(): String?
    suspend fun refreshToken(): Result<String>
}

Methods

getAccessToken

Retrieves the currently stored access token.
fun getAccessToken(): String?
returns
String?
The access token as a String, or null if no token is available
Usage Example:
class AuthInterceptor @Inject constructor(
    private val tokenRepository: TokenRepository
) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val token = tokenRepository.getAccessToken()
        val request = if (token != null) {
            chain.request().newBuilder()
                .addHeader("Authorization", "Bearer $token")
                .build()
        } else {
            chain.request()
        }
        return chain.proceed(request)
    }
}

refreshToken

Requests a renewal of the current access token.
suspend fun refreshToken(): Result<String>
returns
Result<String>
Result containing the new access token on success, or an exception on error
Usage Example:
class TokenAuthenticator @Inject constructor(
    private val tokenRepository: TokenRepository
) : Authenticator {
    override fun authenticate(route: Route?, response: Response): Request? {
        return runBlocking {
            val result = tokenRepository.refreshToken()
            result.fold(
                onSuccess = { newToken ->
                    response.request.newBuilder()
                        .header("Authorization", "Bearer $newToken")
                        .build()
                },
                onFailure = {
                    null // Authentication failed
                }
            )
        }
    }
}

Error Handling

Both repositories return Result<T> types, which can contain either a success value or a failure. Failures may include:
  • Network errors: Connectivity issues
  • Timeout errors: Request exceeded time limit
  • Server errors: HTTP 4xx/5xx responses
  • Unknown errors: Unexpected exceptions
Refer to the AppError sealed class for the complete error hierarchy:
sealed class AppError : Exception() {
    class Network : AppError()
    class Timeout : AppError()
    data class Server(val code: Int, val msg: String) : AppError()
    data class Unknown(val throwable: Throwable) : AppError()
}

Package Location

com.alcalist.tecmeli.domain.repository
Repository interfaces are located in the domain.repository package, while implementations reside in data.repository following Clean Architecture and Dependency Inversion principles.

Build docs developers (and LLMs) love