Deling af Repository- og Use Case-lag
Flyt forretningslogik og dataadgang til commonMain for maksimal genbrug.
Deling af Repository- og Use Case-lag er en gratis Kotlin Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Kotlin Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Kotlin Academy-kurset indeholder 4 lektioner i alt.
Arkitekturmål
I KMP er målet at flytte så meget logik som muligt til commonMain: domænemodeller, repository-grænseflader, anvendelsestilfælde og ViewModels. Kun platformsafhængig input og output forbliver i platformens kildekodesæt.
// 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)Domænelag i commonMain
Definér domæneenheder og repository-grænseflader i commonMain. Ingen platformimporter.
// 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)
}Delt repositoryimplementering med Ktor
Implementér repositoryet i commonMain ved hjælp af Ktor-klienten (multiplatform). Den faktiske HTTP-motor leveres pr. platform.
// 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) }
}
}Lag for anvendelsestilfælde
Anvendelsestilfælde (interaktorer) orkestrerer repositoryer og indeholder forretningslogik. De er ren Kotlin uden platformafhængigheder.
// 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) }
}Delt ViewModel
ViewModels kan placeres i commonMain ved hjælp af kotlinx-coroutines og StateFlow. Android bruger dem direkte, mens iOS pakker dem ind i 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()
}Konfiguration af Ktor HttpClient
Opret Ktor HttpClient i commonMain med fælles konfiguration. Motoren (OkHttp / Darwin) indsættes eller oprettes på hver platform.
// 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 til delt vedvarende lagring
SQLDelight genererer typesikre Kotlin-forespørgsler til alle platforme ud fra .sq-filer i commonMain. Den faktiske databasedriver er platformspecifik.
// 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()Sammenkobling af platformspecifik DI
Saml det hele i et platformspecifikt DI-modul. Grænsefladen er fælles; kun konstruktørargumenterne er forskellige.
// 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 NativeSqliteDriverTest af anvendelsestilfælde i commonTest
Anvendelsestilfælde har ingen platformafhængigheder, hvilket gør dem nemme at enhedsteste i commonTest med et falsk repository.
// 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)
}
}Fejlhåndtering i anvendelsestilfælde
Brug Result eller returværdier fra sealed class i anvendelsestilfælde for at føre fejl videre uden at undtagelser krydser grænsen mellem delt kode og platformspecifik kode.
// 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) }
}Deling uden at ofre platformsidiomer
Del logik, ikke brugerflade. Android bruger Compose, og iOS bruger SwiftUI. Begge observerer den samme tilstand i den delte ViewModel via platformspecifikke bindinger.
// 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") }Hurtigt tjek
Hvilket lag bør IKKE indeholde platformspecifikke importer i et velformet KMP-projekt?
Opsummering
Placér domænemodeller, repository-grænseflader, anvendelsestilfælde og delte ViewModels i commonMain. Brug Ktor (netværk) og SQLDelight (database) med platformsmotorer, der indsættes i DI-laget. Test forretningslogikken i commonTest med falske repositoryer.
Lær Kotlin med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 51
- Lektioner
- 203
Ofte stillede spørgsmål
Er lektionen “Deling af Repository- og Use Case-lag” gratis?
Ja — alle 3 lektioner i læringssporet Kotlin Academy, inklusive “Deling af Repository- og Use Case-lag”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Kotlin Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Deling af Repository- og Use Case-lag”?
Flyt forretningslogik og dataadgang til commonMain for maksimal genbrug. Du øver dig i Kotlin Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Kotlin Academy?
Der kræves ingen tidligere erfaring. Kotlin Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Deling af Repository- og Use Case-lag”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Kotlin Academy-lektion?
Ja. Alle Kotlin Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- KMP-projektstruktur: commonMain, androidMain, iosMain
- expect/actual-mekanismen til platform-API'er
- Deling af Repository- og Use Case-lag
- Dependency injection i KMP med Koin