Compartilhando camadas de repositório e casos de uso
Mova a lógica de negócio e o acesso a dados para commonMain para maximizar a reutilização.
Compartilhando camadas de repositório e casos de uso é uma aula grátis de Kotlin Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Kotlin Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Kotlin Academy inclui 4 aulas no total.
Objetivo da arquitetura
Em KMP, o objetivo é transferir o máximo possível de lógica para commonMain: modelos de domínio, interfaces de repositório, casos de uso e ViewModels. Somente a E/S da plataforma permanece nos conjuntos de origem específicos da plataforma.
// Target architecture:
// commonMain:
// domain/ - data classes, interfaces
// data/ - repository implementations using shared clients
// usecase/ - business logic, composing repositories
// androidMain / iosMain:
// DI wiring, platform drivers (DB, network)Camada de domínio em commonMain
Defina as entidades de domínio e as interfaces de repositório em commonMain. Não use importações da plataforma.
// commonMain/domain/User.kt
data class User(val id: String, val name: String, val email: String)
// commonMain/domain/UserRepository.kt
interface UserRepository {
suspend fun getUser(id: String): User?
suspend fun getAllUsers(): List<User>
suspend fun saveUser(user: User)
}Implementação de repositório compartilhado com Ktor
Implemente o repositório em commonMain usando o cliente Ktor (multiplataforma). O mecanismo HTTP real é fornecido por plataforma.
// commonMain/data/RemoteUserRepository.kt
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
class RemoteUserRepository(private val client: HttpClient) : UserRepository {
override suspend fun getUser(id: String): User? =
client.get("https://api.example.com/users/$id").body()
override suspend fun getAllUsers(): List<User> =
client.get("https://api.example.com/users").body()
override suspend fun saveUser(user: User) {
client.post("https://api.example.com/users") { setBody(user) }
}
}Camada de casos de uso
Os casos de uso (interatores) orquestram os repositórios e contêm a lógica de negócio. Eles são Kotlin puro, sem dependências da plataforma.
// commonMain/usecase/GetUserUseCase.kt
class GetUserUseCase(private val repo: UserRepository) {
suspend operator fun invoke(id: String): Result<User> =
runCatching { repo.getUser(id) ?: error("User not found: $id") }
}
// commonMain/usecase/SaveUserUseCase.kt
class SaveUserUseCase(private val repo: UserRepository) {
suspend operator fun invoke(user: User): Result<Unit> =
runCatching { repo.saveUser(user) }
}ViewModel compartilhado
Os ViewModels podem ficar em commonMain usando kotlinx-coroutines e StateFlow. O Android usa o ViewModel diretamente; o iOS o envolve com SwiftUI.
// commonMain/viewmodel/UserViewModel.kt
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
class UserViewModel(private val getUser: GetUserUseCase) {
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
private val _user = MutableStateFlow<User?>(null)
val user = _user.asStateFlow()
fun load(id: String) {
scope.launch { _user.value = getUser(id).getOrNull() }
}
fun clear() = scope.cancel()
}Configuração do HttpClient do Ktor
Crie o HttpClient do Ktor em commonMain com uma configuração compartilhada. O mecanismo (OkHttp / Darwin) é injetado ou criado por plataforma.
// commonMain/network/HttpClientFactory.kt
import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
fun createHttpClient(engine: io.ktor.client.engine.HttpClientEngine? = null) =
HttpClient(engine ?: io.ktor.client.engine.cio.CIO) {
install(ContentNegotiation) { json() }
}SQLDelight para persistência compartilhada
O SQLDelight gera consultas Kotlin com segurança de tipos para todas as plataformas a partir de arquivos .sq em commonMain. O driver real do banco de dados é específico da plataforma.
// commonMain/db/UserQueries.sq:
// CREATE TABLE User (id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL);
// selectAll:
// SELECT * FROM User;
// Usage in commonMain:
// val db = UserDatabase(driver)
// val users = db.userQueries.selectAll().executeAsList()Configuração de DI da plataforma
Conecte todos os componentes em um módulo de DI específico da plataforma. A interface é comum; somente os argumentos do construtor diferem.
// androidMain:
val androidModule = module {
single { AndroidSqliteDriver(UserDatabase.Schema, androidContext(), "users.db") }
single { UserDatabase(get()) }
single<UserRepository> { RemoteUserRepository(get()) }
single { GetUserUseCase(get()) }
viewModel { UserViewModel(get()) }
}
// iosMain:
// Same pattern with NativeSqliteDriverTestando casos de uso em commonTest
Os casos de uso não têm dependências da plataforma, o que facilita testá-los como unidades em commonTest com um repositório falso.
// commonTest/usecase/GetUserUseCaseTest.kt
import kotlin.test.Test
import kotlin.test.assertEquals
class FakeUserRepo : UserRepository {
override suspend fun getUser(id: String) = if (id == "1") User("1", "Alice", "a@b.com") else null
override suspend fun getAllUsers() = emptyList<User>()
override suspend fun saveUser(user: User) {}
}
class GetUserUseCaseTest {
private val useCase = GetUserUseCase(FakeUserRepo())
@Test
fun testGetExistingUser() = kotlinx.coroutines.runBlocking {
val result = useCase("1")
assertEquals("Alice", result.getOrNull()?.name)
}
}Tratamento de erros nos casos de uso
Use retornos Result ou de uma classe selada nos casos de uso para propagar erros sem que exceções atravessem o limite entre o código compartilhado e a plataforma.
// commonMain:
sealed class UserResult {
data class Success(val user: User) : UserResult()
data class NotFound(val id: String) : UserResult()
data class Error(val cause: Throwable) : UserResult()
}
class SafeGetUser(private val repo: UserRepository) {
suspend operator fun invoke(id: String): UserResult = try {
val user = repo.getUser(id)
if (user != null) UserResult.Success(user) else UserResult.NotFound(id)
} catch (e: Exception) { UserResult.Error(e) }
}Compartilhamento sem abrir mão dos padrões da plataforma
Compartilhe a lógica, não a interface do usuário. O Android usa Compose; o iOS usa SwiftUI. Ambos observam o mesmo estado do ViewModel compartilhado por meio de vinculações específicas da plataforma.
// Android (Compose):
// val user by viewModel.user.collectAsState()
// iOS (SwiftUI):
// @State var user = viewModel.user.value
// viewModel.load(id: "1")
// Shared ViewModel:
// _user.value = ... (commonMain)
fun main() { println("Share logic; use platform UI frameworks") }Verificação rápida
Qual camada NÃO deve ter importações específicas da plataforma em um projeto KMP bem estruturado?
Recapitulação
Coloque os modelos de domínio, as interfaces de repositório, os casos de uso e os ViewModels compartilhados em commonMain. Use Ktor (rede) e SQLDelight (banco de dados), com os mecanismos da plataforma injetados na camada de DI. Teste a lógica de negócio em commonTest com repositórios falsos.
Aprenda Kotlin com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 51
- Aulas
- 203
Perguntas Frequentes
A aula “Compartilhando camadas de repositório e casos de uso” é grátis?
Sim — o texto completo de “Compartilhando camadas de repositório e casos de uso” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Kotlin Academy, atualize para CoddyKit PRO. O curso de Kotlin Academy inclui 4 aulas no total.
O que vou aprender em “Compartilhando camadas de repositório e casos de uso”?
Mova a lógica de negócio e o acesso a dados para commonMain para maximizar a reutilização. Você pratica Kotlin Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Kotlin Academy?
Nenhuma experiência prévia é necessária. Kotlin Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Compartilhando camadas de repositório e casos de uso”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Kotlin Academy?
Sim. Cada aula de Kotlin Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Estrutura de projeto KMP: commonMain, androidMain, iosMain
- Mecanismo expect/actual para APIs de plataforma
- Compartilhando camadas de repositório e casos de uso
- Injeção de dependências em KMP com Koin