0Pricing
Kotlin Academy · Урок

Функциональное моделирование предметной области с базовыми типами Arrow

Моделируйте сложные предметные области с помощью Either, NonEmptyList и накопления проверенных ошибок

«Функциональное моделирование предметной области с базовыми типами Arrow» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Что такое функциональное моделирование предметной области

Функциональное моделирование предметной области использует алгебраические типы (Either, Option, запечатанные классы), чтобы закодировать бизнес-правила в типах. Некорректные состояния становятся непредставимыми во время компиляции, а ошибки явно указываются в сигнатурах функций — без скрытых исключений.

Объекты-значения с inline-классами и value-классами

Используйте value-классы Kotlin, чтобы оборачивать примитивные типы и предотвращать одержимость примитивами. Система типов не позволит передать UserId туда, где ожидается PostId:

@JvmInline value class UserId(val value: Long)
@JvmInline value class PostId(val value: Long)

fun findUser(id: UserId): Either<UserError, User> = TODO()
// findUser(PostId(1L))  // Compile error!

Запечатанные классы для ошибок предметной области

Моделируйте ошибки с помощью иерархий запечатанных классов. Каждый подкласс содержит только данные, относящиеся к соответствующему случаю ошибки:

sealed class UserError {
    data class NotFound(val id: UserId) : UserError()
    data class EmailTaken(val email: String) : UserError()
    data class ValidationFailed(val field: String, val reason: String) : UserError()
}

Validated: накопление нескольких ошибок

Если нужно сообщить обо всех ошибках проверки сразу, а не только о первой, используйте Validated из Arrow (или zipOrAccumulate в Raise DSL). Ошибки накапливаются в NonEmptyList:

import arrow.core.Validated
import arrow.core.valid
import arrow.core.invalid

fun validateName(name: String): Validated<String, String> =
    if (name.isNotBlank()) name.valid() else "Name is blank".invalid()

Составление проверок

Используйте zip(), чтобы объединить несколько результатов Validated. Если хотя бы один из них имеет значение Invalid, накапливаются все ошибки:

val result: Validated<NonEmptyList<String>, User> =
    validateName(name).zip(validateEmail(email)) { n, e -> User(n, e) }
        .mapLeft { it }

Представление недопустимых состояний невозможным

Вместо User с nullable-полем email и флагом isVerified используйте запечатанные подклассы, чтобы система типов не позволяла обращаться к адресу электронной почты непроверенного пользователя:

sealed class User {
    data class Unverified(val id: UserId, val pendingEmail: String) : User()
    data class Verified(val id: UserId, val email: String) : User()
}

Умные конструкторы

Используйте фабричные функции companion object, возвращающие Either или Option, вместо открытых конструкторов. Это гарантирует, что экземпляры всегда находятся в корректном состоянии:

class Email private constructor(val value: String) {
    companion object {
        fun of(raw: String): Either<String, Email> =
            if (raw.contains("@") && raw.length <= 255) Email(raw).right()
            else "Invalid email format".left()
    }
}

Сочетание Either и Option в конвейере

Реальная операция предметной области обычно объединяет поиск в репозиториях (Option) с проверками (Either). Используйте блок Arrow either { } с .bind(), чтобы связно объединить эти операции:

fun createPost(authorId: UserId, title: String, body: String): Either<PostError, Post> = either {
    val author = userRepo.findById(authorId).toEither { PostError.AuthorNotFound }.bind()
    val validTitle = validateTitle(title).bind()
    postRepo.save(Post(author, validTitle, body))
}

Неизменяемость и Copy

Сущности предметной области должны быть неизменяемыми. Используйте классы данных и copy(), чтобы получать новые состояния. Lens из Arrow (из arrow-optics) позволяет удобно обновлять вложенные значения без изменения исходных данных:

val updated = user.copy(email = "new@example.com")
// Arrow Optics:
val emailLens = User.email
val updatedWithLens = emailLens.set(user, "new@example.com")

Типобезопасная конфигурация с запечатанными классами

Моделируйте варианты конфигурации приложения с помощью запечатанных классов, чтобы компилятор требовал обработать каждый случай:

sealed class DbConfig {
    data class Postgres(val url: String, val user: String, val pass: String) : DbConfig()
    data class InMemory(val dbName: String = "test") : DbConfig()
}

Преимущества с первого взгляда

Функциональное моделирование предметной области с базовыми типами Arrow обеспечивает:

  • Отсутствие исключений из-за нулевого указателя в коде предметной области
  • Видимые в сигнатурах функций ошибки
  • Проверки на полноту с помощью when для иерархий запечатанных классов
  • Тестируемые чистые функции
  • Самодокументируемый код

Быстрая проверка

Какое главное преимущество даёт иерархия sealed class для ошибок предметной области по сравнению с выбрасыванием исключений?

Итоги: функциональное моделирование предметной области с базовыми типами Arrow

Главное:

  • Value-классы предотвращают одержимость примитивами
  • Иерархии запечатанных ошибок обеспечивают полное и типобезопасное описание случаев сбоя
  • Умные конструкторы гарантируют корректность экземпляров
  • either { }.bind() аккуратно объединяет операции, которые могут завершиться ошибкой
  • Неизменяемые данные + copy() / Arrow Optics обеспечивают безопасные переходы между состояниями

Часто задаваемые вопросы

Урок «Функциональное моделирование предметной области с базовыми типами Arrow» бесплатный?

Да — полный текст урока «Функциональное моделирование предметной области с базовыми типами Arrow» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Функциональное моделирование предметной области с базовыми типами Arrow»?

Моделируйте сложные предметные области с помощью Either, NonEmptyList и накопления проверенных ошибок Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Функциональное моделирование предметной области с базовыми типами Arrow»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Either : типизированная обработка ошибок без исключений
  2. DSL Arrow Raise: объединение типизированных ошибок
  3. Option и Nullable: когда использовать каждый из них
  4. Функциональное моделирование предметной области с базовыми типами Arrow
← Назад к Kotlin Academy