0Pricing
Kotlin Academy · Pelajaran

Membagikan Lapisan Repository dan Use Case

Pindahkan logika bisnis dan akses data ke commonMain untuk penggunaan ulang maksimal.

Membagikan Lapisan Repository dan Use Case adalah pelajaran Kotlin Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Kotlin Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Kotlin Academy mencakup 4 pelajaran total.

Tujuan Arsitektur

Dalam KMP, tujuannya adalah memindahkan sebanyak mungkin logika ke commonMain: model domain, antarmuka repositori, kasus penggunaan, dan ViewModels. Hanya I/O platform yang tetap berada dalam set sumber platform.

// 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)

Lapisan Domain di commonMain

Definisikan entitas domain dan antarmuka repositori dalam commonMain. Jangan gunakan impor platform.

// 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)
}

Implementasi Repositori Bersama dengan Ktor

Implementasikan repositori dalam commonMain menggunakan klien Ktor (multiplatform). Mesin HTTP yang sebenarnya disediakan untuk setiap 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) }
    }
}

Lapisan Kasus Penggunaan

Kasus penggunaan (interaktor) mengoordinasikan repositori dan berisi logika bisnis. Semuanya adalah Kotlin murni tanpa dependensi platform.

// 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 Bersama

ViewModels dapat berada dalam commonMain menggunakan kotlinx-coroutines dan StateFlow. Android menggunakannya secara langsung; iOS membungkusnya dalam 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()
}

Konfigurasi Ktor HttpClient

Buat HttpClient Ktor dalam commonMain dengan konfigurasi bersama. Mesinnya (OkHttp / Darwin) diinjeksi atau dibuat untuk setiap 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 untuk Persistensi Bersama

SQLDelight menghasilkan kueri Kotlin bertipe aman untuk semua platform dari berkas .sq dalam commonMain. Driver basis data yang sebenarnya bersifat khusus platform.

// 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()

Perakitan DI Platform

Rangkai semuanya dalam modul DI khusus platform. Antarmukanya bersifat bersama; hanya argumen konstruktornya yang berbeda.

// 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 NativeSqliteDriver

Menguji Kasus Penggunaan di commonTest

Kasus penggunaan tidak memiliki dependensi platform, sehingga mudah diuji sebagai unit dalam commonTest menggunakan repositori palsu.

// 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)
    }
}

Penanganan Error dalam Kasus Penggunaan

Gunakan Result atau pengembalian kelas tersegel dari kasus penggunaan untuk meneruskan error tanpa exception melintasi batas bersama/platform.

// 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) }
}

Berbagi Tanpa Mengorbankan Idiom Platform

Bagikan logika, bukan UI. Android menggunakan Compose; iOS menggunakan SwiftUI. Keduanya mengamati state ViewModel bersama yang sama melalui binding khusus platform.

// 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") }

Pemeriksaan Singkat

Lapisan mana yang seharusnya NOT memiliki impor khusus platform dalam proyek KMP yang terstruktur dengan baik?

Ringkasan

Tempatkan model domain, antarmuka repositori, kasus penggunaan, dan ViewModels bersama dalam commonMain. Gunakan Ktor (jaringan) dan SQLDelight (basis data) dengan mesin platform yang diinjeksi pada lapisan DI. Uji logika bisnis dalam commonTest menggunakan repositori palsu.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Membagikan Lapisan Repository dan Use Case” gratis?

Ya — teks lengkap “Membagikan Lapisan Repository dan Use Case” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Kotlin Academy, upgrade ke CoddyKit PRO. Kursus Kotlin Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Membagikan Lapisan Repository dan Use Case”?

Pindahkan logika bisnis dan akses data ke commonMain untuk penggunaan ulang maksimal. Kamu berlatih Kotlin Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Kotlin Academy?

Tidak diperlukan pengalaman sebelumnya. Kotlin Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Membagikan Lapisan Repository dan Use Case” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Kotlin Academy ini?

Ya. Setiap pelajaran Kotlin Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Struktur Proyek KMP: commonMain, androidMain, iosMain
  2. Mekanisme expect/actual untuk API Platform
  3. Membagikan Lapisan Repository dan Use Case
  4. Injeksi Dependensi dalam KMP dengan Koin
← Kembali ke Kotlin Academy