Как писать качественный common code в Kotlin Multiplatform
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Подход к написанию качественного common-кода в KMM
Качественный common-код в Kotlin Multiplatform (KMP) — это основа успешной кросс-платформенной разработки. Вот ключевые принципы и практики:
Архитектурные принципы
Многоуровневая архитектура — разделяйте код на слои:
// Domain-слой (полностью common)
interface UserRepository {
suspend fun getUser(id: String): User
}
// Data-слой с expect/actual для платформенных реализаций
expect class DatabaseDriverFactory {
fun createDriver(): SqlDriver
}
Принцип инверсии зависимостей — зависьте от абстракций, а не от реализаций:
// В common-коде
expect interface BiometricAuthenticator {
suspend fun authenticate(): Boolean
}
// В платформенных модулях
actual class AndroidBiometricAuthenticator : BiometricAuthenticator {
actual override suspend fun authenticate(): Boolean {
// Android-реализация
}
}
Паттерны проектирования
- Используйте корутины для асинхронных операций:
class NetworkService(
private val httpClient: HttpClient
) {
suspend fun fetchData(): Result<Data> = runCatching {
httpClient.get("https://api.example.com/data")
}
}
- Применяйте sealed-классы/интерфейсы для моделирования состояний:
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Error(val exception: Throwable) : Result<Nothing>
object Loading : Result<Nothing>
}
Организация кода
Структура модулей:
shared/
├── commonMain/ # Общая бизнес-логика
├── androidMain/ # Android-специфичные реализации
├── iosMain/ # iOS-специфичные реализации
└── desktopMain/ # Desktop-реализации (если нужно)
Правила именования:
expect/actualдля платформенно-зависимого кода- Суффиксы
Common,Sharedдля общих компонентов - Четкое разделение по пакетам:
domain,data,presentation
Тестирование common-кода
Unit-тесты в commonTest:
// commonTest/kotlin
class CalculatorTest {
@Test
fun testAddition() {
val calculator = Calculator()
assertEquals(5, calculator.add(2, 3))
}
}
Mocking платформенных зависимостей:
class TestFileSystem : FileSystem {
override fun readFile(path: String): String = "test content"
override fun writeFile(path: String, content: String) {}
}
Оптимизация и производительность
- Минимизируйте expect/actual declarations — выносите максимум логики в common
- Используйте inline-классы для type-safe примитивов:
@JvmInline
value class UserId(val value: String)
@JvmInline
value class Email(val value: String)
// Компилятор не создает дополнительных объектов
- Оптимизируйте сериализацию с помощью
kotlinx.serialization:
@Serializable
data class ApiResponse<T>(
@SerialName("data")
val data: T,
@SerialName("status")
val status: String
)
Обработка ошибок
Единая система ошибок:
sealed class AppError : Throwable() {
data class NetworkError(val code: Int) : AppError()
data class ValidationError(val field: String) : AppError()
object UnknownError : AppError()
}
// Общий обработчик
suspend fun <T> safeCall(block: suspend () -> T): Result<T> {
return try {
Result.success(block())
} catch (e: AppError) {
Result.failure(e)
}
}
Платформенно-зависимый код
Изолируйте платформенные зависимости через интерфейсы:
// Common
expect class PlatformLogger {
fun logDebug(message: String)
fun logError(message: String, throwable: Throwable)
}
// Android
actual class PlatformLogger {
actual fun logDebug(message: String) {
Log.d("App", message)
}
// ... остальные методы
}
Миграция и поддержка
- Постепенная миграция — начинайте с небольших модулей
- Совместимость с существующим кодом — используйте адаптеры
- Документация — документируйте ограничения и особенности common-кода
Инструменты и библиотеки
Обязательный набор:
kotlinx.coroutines— асинхронностьkotlinx.serialization— сериализацияKoinилиKodein— DI (если нужно)kotlinx-datetime— работа с датами
Качество кода:
- Detekt для статического анализа
- Dokka для документации
- Gradle version catalog для управления зависимостями
Ключевой принцип: пишите common-код так, будто это обычный Kotlin-модуль, изолируя платформенно-зависимые части через четкие интерфейсы. Тестируемость, поддерживаемость и четкое разделение ответственности — залог успешного мультиплатформенного проекта.