0Pricing
Kotlin Academy · 课时

使用 Arrow 核心类型进行函数式领域建模

使用 Either、NonEmptyList 和已验证的错误累积来建模复杂领域。

使用 Arrow 核心类型进行函数式领域建模 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

什么是函数式领域建模

函数式领域建模使用代数类型(Either、Option、密封类)将业务规则编码到类型中。无效状态在编译时变得无法表示,错误会在函数签名中明确体现——不会有隐藏的异常。

使用内联类/值类创建值对象

使用 Kotlin 值类包装基本类型,避免基本类型偏执。类型系统会拒绝在需要 PostId 的地方传入 UserId:

@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:累积多个错误

当您希望一次报告所有验证错误(而不只是第一个错误)时,请使用 Arrow 的 Validated(或者在 Raise DSL 中使用 zipOrAccumulate)。它会将错误累积到 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 }

使非法状态无法表示

不要使用带有可空 email 和 isVerified 标志的 User,而应使用密封子类,这样类型系统就能防止您访问未经验证用户的电子邮件:

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

智能构造函数

使用返回 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()
    }
}

在流水线中组合结果类型与选项类型

实际的领域操作通常会将存储库查询(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() 派生新状态。Arrow 的 Lens(来自 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 核心类型进行函数式领域建模

要点:

  • 值类可以避免基本类型偏执
  • 密封错误层次结构使失败情况穷尽且具有类型安全性
  • 智能构造函数确保实例有效
  • either { }.bind() 可以简洁地组合可能失败的操作
  • 不可变数据 + copy() / Arrow Optics 可实现安全的状态转换

常见问题解答

「使用 Arrow 核心类型进行函数式领域建模」课时是免费的吗?

是的 — 「使用 Arrow 核心类型进行函数式领域建模」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「使用 Arrow 核心类型进行函数式领域建模」这节课中我会学到什么?

使用 Either、NonEmptyList 和已验证的错误累积来建模复杂领域。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「使用 Arrow 核心类型进行函数式领域建模」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Either :无需异常的类型化错误处理
  2. Arrow Raise DSL:组合类型化错误
  3. Option 与 Nullable:如何选择
  4. 使用 Arrow 核心类型进行函数式领域建模
← 返回 Kotlin Academy