Kotlin: Kotlin项目开发详解

最后更新:2026-08-26

设计完成,现在是代码时刻——Charlie、Alice 和 Bob 协作实现 OrderProcessor 的四层代码:领域层(Order + 事件)、服务层(业务逻辑 + 协程)、持久层(Repository)、集成层(HTTP 客户端)。

1. 你将学到


2. 一个团队的真实故事

(1) 痛点:分工不清晰导致冲突

Alice 和 Bob 同时修改 ,Git 冲突 10 次。原因:没有按层分工,两人都在改同一个文件。

(2) 分层分工的解法

TEXT 📖 仅展示
Charlie: Domain layer (Order, OrderEvent, StateMachine)
Alice:   Service layer (OrderService, business logic)
Bob:     Repository + Integration layer (data access, HTTP clients)

分层架构 = 分层分工,每层独立开发,接口契约减少冲突。


3. 领域层

(1) 核心实体

KOTLIN
// Domain entities - pure Kotlin, no framework dependency
data class OrderItem(
    val sku: String,
    val quantity: Int,
    val unitPrice: Double
) {
    val subtotal: Double get() = quantity * unitPrice
    init {
        require(quantity > 0) { "Quantity must be positive" }
        require(unitPrice >= 0) { "Price must be non-negative" }
    }
}

data class Address(
    val street: String,
    val city: String,
    val country: String
)

data class Order(
    val id: String,
    val customerId: String,
    val items: List`<OrderItem>`,
    val shippingAddress: Address?,
    var status: OrderStatus,
    var trackingCode: String?,
    val createdAt: String,
    var updatedAt: String?
) {
    val total: Double get() = items.sumOf { it.subtotal }

    init {
        require(id.startsWith("ORD-")) { "Invalid order ID format" }
        require(items.isNotEmpty()) { "Order must have at least one item" }
    }
}

(2) 状态与事件

KOTLIN
sealed class OrderStatus {
    data class Pending(val createdAt: String) : OrderStatus()
    data class Confirmed(val confirmedAt: String) : OrderStatus()
    data class Shipped(val trackingCode: String, val shippedAt: String) : OrderStatus()
    data class Delivered(val deliveredAt: String) : OrderStatus()
    data class Cancelled(val reason: String, val cancelledAt: String) : OrderStatus()
}

sealed class OrderEvent {
    abstract val orderId: String
    abstract val timestamp: String

    data class Created(
        override val orderId: String,
        val customerId: String,
        val total: Double,
        override val timestamp: String
    ) : OrderEvent()

    data class Confirmed(override val orderId: String, override val timestamp: String) : OrderEvent()
    data class Shipped(override val orderId: String, val trackingCode: String, override val timestamp: String) : OrderEvent()
    data class Delivered(override val orderId: String, override val timestamp: String) : OrderEvent()
    data class Cancelled(override val orderId: String, val reason: String, override val timestamp: String) : OrderEvent()
}

(3) 状态机

KOTLIN
object OrderStateMachine {
    fun transition(current: OrderStatus, event: OrderEvent): OrderStatus = when {
        current is OrderStatus.Pending && event is OrderEvent.Confirmed ->
            OrderStatus.Confirmed(event.timestamp)
        current is OrderStatus.Confirmed && event is OrderEvent.Shipped ->
            OrderStatus.Shipped(event.trackingCode, event.timestamp)
        current is OrderStatus.Shipped && event is OrderEvent.Delivered ->
            OrderStatus.Delivered(event.timestamp)
        current is OrderStatus.Pending && event is OrderEvent.Cancelled ->
            OrderStatus.Cancelled(event.reason, event.timestamp)
        else ->
            throw IllegalStateException("Invalid transition from $current with $event")
    }

    fun canTransition(current: OrderStatus, event: OrderEvent): Boolean = when {
        current is OrderStatus.Pending && event is OrderEvent.Confirmed -> true
        current is OrderStatus.Confirmed && event is OrderEvent.Shipped -> true
        current is OrderStatus.Shipped && event is OrderEvent.Delivered -> true
        current is OrderStatus.Pending && event is OrderEvent.Cancelled -> true
        else -> false
    }
}

4. 服务层

(1) OrderService

KOTLIN
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

class OrderService(
    private val repository: OrderRepository,
    private val eventStore: EventRepository,
    private val paymentClient: PaymentClient,
    private val inventoryClient: InventoryClient
) {
    suspend fun createOrder(request: CreateOrderRequest): Order {
        val order = Order(
            id = generateOrderId(),
            customerId = request.customerId,
            items = request.items.map { OrderItem(it.sku, it.quantity, it.unitPrice) },
            shippingAddress = request.shippingAddress,
            status = OrderStatus.Pending(request.timestamp),
            trackingCode = null,
            createdAt = request.timestamp,
            updatedAt = null
        )
        repository.save(order)
        eventStore.append(OrderEvent.Created(order.id, order.customerId, order.total, request.timestamp))
        return order
    }

    suspend fun confirmOrder(orderId: String, timestamp: String): Order {
        val order = repository.findById(orderId) ?: throw NoSuchElementException("Order $orderId not found")
        val event = OrderEvent.Confirmed(orderId, timestamp)
        val newStatus = OrderStateMachine.transition(order.status, event)
        order.status = newStatus
        order.updatedAt = timestamp
        repository.save(order)
        eventStore.append(event)
        return order
    }

    suspend fun shipOrder(orderId: String, trackingCode: String, timestamp: String): Order {
        val order = repository.findById(orderId) ?: throw NoSuchElementException("Order $orderId not found")
        val event = OrderEvent.Shipped(orderId, trackingCode, timestamp)
        val newStatus = OrderStateMachine.transition(order.status, event)
        order.status = newStatus
        order.trackingCode = trackingCode
        order.updatedAt = timestamp
        repository.save(order)
        eventStore.append(event)
        return order
    }

    fun orderEvents(orderId: String): Flow`<OrderEvent>` = flow {
        eventStore.findByOrderId(orderId).forEach { emit(it) }
    }

    private fun generateOrderId(): String = "ORD-${System.currentTimeMillis()}"
}

(2) 请求/响应 DTO

KOTLIN
data class CreateOrderRequest(
    val customerId: String,
    val items: List`<CreateItemRequest>`,
    val shippingAddress: AddressRequest?,
    val timestamp: String
)

data class CreateItemRequest(val sku: String, val quantity: Int, val unitPrice: Double)
data class AddressRequest(val street: String, val city: String, val country: String)

data class OrderResponse(
    val id: String,
    val customerId: String,
    val total: Double,
    val status: String,
    val items: List`<OrderItemResponse>`,
    val trackingCode: String?,
    val createdAt: String,
    val updatedAt: String?
)

data class OrderItemResponse(val sku: String, val quantity: Int, val unitPrice: Double, val subtotal: Double)

fun Order.toResponse() = OrderResponse(
    id, customerId, total, status::class.simpleName ?: "Unknown",
    items.map { OrderItemResponse(it.sku, it.quantity, it.unitPrice, it.subtotal) },
    trackingCode, createdAt, updatedAt
)

5. 持久层

(1) Repository 接口

KOTLIN
interface OrderRepository {
    suspend fun save(order: Order)
    suspend fun findById(id: String): Order?
    suspend fun findAll(): List`<Order>`
    suspend fun deleteById(id: String)
}

interface EventRepository {
    suspend fun append(event: OrderEvent)
    fun findByOrderId(orderId: String): List`<OrderEvent>`
}

(2) 内存实现(开发阶段)

KOTLIN
class InMemoryOrderRepository : OrderRepository {
    private val storage = mutableMapOf<String, Order>()

    override suspend fun save(order: Order) { storage[order.id] = order }
    override suspend fun findById(id: String) = storage[id]
    override suspend fun findAll() = storage.values.toList()
    override suspend fun deleteById(id: String) { storage.remove(id) }
}

class InMemoryEventRepository : EventRepository {
    private val events = mutableListOf`<OrderEvent>`()

    override suspend fun append(event: OrderEvent) { events.add(event) }
    override fun findByOrderId(orderId: String) = events.filter { it.orderId == orderId }
}

6. 集成层

(1) HTTP 客户端接口

KOTLIN
interface PaymentClient {
    suspend fun processPayment(orderId: String, amount: Double): PaymentResult
}

interface InventoryClient {
    suspend fun checkAvailability(sku: String, quantity: Int): Boolean
    suspend fun reserve(sku: String, quantity: Int): ReservationResult
}

sealed class PaymentResult {
    data class Success(val transactionId: String) : PaymentResult()
    data class Failed(val reason: String) : PaymentResult()
}

sealed class ReservationResult {
    data class Reserved(val reservationId: String) : ReservationResult()
    data class Unavailable(val reason: String) : ReservationResult()
}

// Simulated implementations
class MockPaymentClient : PaymentClient {
    override suspend fun processPayment(orderId: String, amount: Double) =
        PaymentResult.Success("TXN-${orderId.substring(4)}")
}

class MockInventoryClient : InventoryClient {
    override suspend fun checkAvailability(sku: String, quantity: Int) = quantity < 10_000
    override suspend fun reserve(sku: String, quantity: Int) =
        ReservationResult.Reserved("RES-${sku.substring(4)}")
}

7. 四层职责对比

职责 依赖方向 典型类 测试策略
领域层 业务规则与实体 无依赖 Order, OrderStatus, StateMachine 纯单元测试
服务层 业务流程编排 → 领域/持久/集成 OrderService Mock 依赖单元测试
持久层 数据存取 → 领域 OrderRepository, EventRepository 集成测试(Testcontainers)
集成层 外部服务调用 → 领域 PaymentClient, InventoryClient 契约测试 + Mock

(1) Repository 实现策略对比

阶段 实现 优点 缺点
开发 InMemory* 零配置、快速 无持久化
测试 Testcontainers 真实 DB、可复现 需要 Docker
生产 Spring Data R2DBC 协程原生、高性能 需配置连接池

(2) 状态转换规则表

当前状态 允许事件 目标状态
Pending Confirmed Confirmed
Pending Cancelled Cancelled
Confirmed Shipped Shipped
Shipped Delivered Delivered
其他组合 非法

8. 订单创建完整时序

100%
sequenceDiagram
    participant Client
    participant Controller
    participant Service
    participant Repo
    participant Events
    participant Payment
    participant Inventory

    Client->>Controller: POST /api/v1/orders
    Controller->>Service: createOrder(request)
    Service->>Inventory: checkAvailability(sku, qty)
    Inventory-->>Service: Available
    Service->>Repo: save(order)
    Repo-->>Service: Saved
    Service->>Events: append(Created event)
    Service->>Payment: processPayment(orderId, total)
    Payment-->>Service: Success
    Service->>Repo: save(order status=Confirmed)
    Service->>Events: append(Confirmed event)
    Service-->>Controller: Order
    Controller-->>Client: 201 Created

9. 完整示例

KOTLIN
// ============================================
// OrderProcessor - End-to-End Implementation
// Feature: Full order lifecycle with state machine
// ============================================

import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking

// --- Domain (from sections above) ---
data class OrderItem(val sku: String, val quantity: Int, val unitPrice: Double) {
    val subtotal: Double get() = quantity * unitPrice
}

sealed class OrderStatus {
    data class Pending(val at: String) : OrderStatus()
    data class Confirmed(val at: String) : OrderStatus()
    data class Shipped(val trackingCode: String, val at: String) : OrderStatus()
    data class Cancelled(val reason: String, val at: String) : OrderStatus()
}

sealed class OrderEvent {
    abstract val orderId: String
    data class Created(override val orderId: String, val total: Double, val at: String) : OrderEvent()
    data class Confirmed(override val orderId: String, val at: String) : OrderEvent()
    data class Shipped(override val orderId: String, val code: String, val at: String) : OrderEvent()
    data class Cancelled(override val orderId: String, val reason: String, val at: String) : OrderEvent()
}

data class Order(
    val id: String, val customerId: String, val items: List`<OrderItem>`,
    var status: OrderStatus, var trackingCode: String?, val createdAt: String, var updatedAt: String?
) { val total: Double get() = items.sumOf { it.subtotal } }

object StateMachine {
    fun next(current: OrderStatus, event: OrderEvent): OrderStatus = when {
        current is OrderStatus.Pending && event is OrderEvent.Confirmed -> OrderStatus.Confirmed(event.at)
        current is OrderStatus.Confirmed && event is OrderEvent.Shipped -> OrderStatus.Shipped(event.code, event.at)
        current is OrderStatus.Pending && event is OrderEvent.Cancelled -> OrderStatus.Cancelled(event.reason, event.at)
        else -> error("Invalid: $current + $event")
    }
}

// --- Repository ---
class OrderRepo {
    private val db = mutableMapOf<String, Order>()
    fun save(o: Order) { db[o.id] = o }
    fun find(id: String) = db[id]
    fun findAll() = db.values.toList()
}

class EventRepo {
    private val events = mutableListOf`<OrderEvent>`()
    fun append(e: OrderEvent) { events.add(e) }
    fun find(orderId: String) = events.filter { it.orderId == orderId }
}

// --- Service ---
class OrderService(private val orders: OrderRepo, private val events: EventRepo) {
    private var counter = 0
    fun create(customerId: String, items: List`<OrderItem>`, at: String): Order {
        val order = Order("ORD-${++counter}", customerId, items, OrderStatus.Pending(at), null, at, null)
        orders.save(order)
        events.append(OrderEvent.Created(order.id, order.total, at))
        return order
    }
    fun confirm(id: String, at: String): Order {
        val order = orders.find(id) ?: error("Not found: $id")
        val event = OrderEvent.Confirmed(id, at)
        order.status = StateMachine.next(order.status, event)
        order.updatedAt = at
        orders.save(order)
        events.append(event)
        return order
    }
    fun ship(id: String, code: String, at: String): Order {
        val order = orders.find(id) ?: error("Not found: $id")
        val event = OrderEvent.Shipped(id, code, at)
        order.status = StateMachine.next(order.status, event)
        order.trackingCode = code
        order.updatedAt = at
        orders.save(order)
        events.append(event)
        return order
    }
    fun cancel(id: String, reason: String, at: String): Order {
        val order = orders.find(id) ?: error("Not found: $id")
        val event = OrderEvent.Cancelled(id, reason, at)
        order.status = StateMachine.next(order.status, event)
        order.updatedAt = at
        orders.save(order)
        events.append(event)
        return order
    }
    fun history(id: String) = events.find(id)
}

// --- Demo ---
fun main() {
    val service = OrderService(OrderRepo(), EventRepo())

    println("=== OrderProcessor Development Demo ===\n")

    // Create orders
    val o1 = service.create("CUST-001", listOf(OrderItem("SKU-A", 3, 9.99), OrderItem("SKU-B", 1, 149.99)), "2026-07-13T10:00:00Z")
    println("Created: ${o1.id} | Total: \$${o1.total} USD | Status: ${o1.status}")

    val o2 = service.create("CUST-002", listOf(OrderItem("SKU-C", 2, 5_000.0)), "2026-07-13T10:01:00Z")
    println("Created: ${o2.id} | Total: \$${o2.total} USD | Status: ${o2.status}")

    // Confirm
    val confirmed = service.confirm(o1.id, "2026-07-13T10:05:00Z")
    println("\nConfirmed: ${confirmed.id} | Status: ${confirmed.status}")

    // Ship
    val shipped = service.ship(o1.id, "TRK-ABC123", "2026-07-13T11:00:00Z")
    println("Shipped: ${shipped.id} | Tracking: ${shipped.trackingCode} | Status: ${shipped.status}")

    // Cancel
    val cancelled = service.cancel(o2.id, "Customer request", "2026-07-13T10:10:00Z")
    println("Cancelled: ${cancelled.id} | Reason: ${(cancelled.status as OrderStatus.Cancelled).reason}")

    // Event history
    println("\n=== Event History: ${o1.id} ===")
    service.history(o1.id).forEach { println("  $it") }
}

输出:

TEXT 📖 仅展示
=== OrderProcessor Development Demo ===

Created: ORD-1 | Total: $179.96 USD | Status: Pending(at=2026-07-13T10:00:00Z)
Created: ORD-2 | Total: $10000.0 USD | Status: Pending(at=2026-07-13T10:01:00Z)

Confirmed: ORD-1 | Status: Confirmed(at=2026-07-13T10:05:00Z)
Shipped: ORD-1 | Tracking: TRK-ABC123 | Status: Shipped(trackingCode=TRK-ABC123, at=2026-07-13T11:00:00Z)
Cancelled: ORD-2 | Reason: Customer request

=== Event History: ORD-1 ===
  Created(orderId=ORD-1, total=179.96, at=2026-07-13T10:00:00Z)
  Confirmed(orderId=ORD-1, at=2026-07-13T10:05:00Z)
  Shipped(orderId=ORD-1, code=TRK-ABC123, at=2026-07-13T11:00:00Z)

❓ 常见问题

Q 领域层应该依赖框架吗?
A 不应该。领域层是纯 Kotlin 代码,不依赖 Spring 或任何框架。这确保领域逻辑可独立测试和复用。
Q Service 层应该返回领域对象还是 DTO?
A Service 返回领域对象,Controller 负责转换为 DTO。分层职责清晰:Service 不知道 HTTP 的存在。
Q 状态机应该放在哪里?
A 放在领域层。状态机是核心业务规则,不应该依赖任何外部层。它用纯 Kotlin 密封类实现。
Q 如何处理并发状态更新?
A 数据库乐观锁(version 字段)或分布式锁。协程层面用 保护共享状态。生产环境推荐数据库锁。
Q 集成层的 HTTP 客户端怎么选?
A Kotlin 项目推荐 Ktor Client(协程原生),Java 生态可用 Spring WebClient。两者都支持 suspend。
Q 如何确保团队按层分工?
A 代码审查规则:Controller 不直接操作 Repository,Service 不知道 HTTP,领域层不依赖框架。ArchUnit 等工具可自动检查。

📖 小节


📝 作业

  1. 基础题(难度⭐):实现 data class 和 密封类,在 块中添加验证。提示:
  2. 进阶题(难度⭐⭐):实现 的 和 ,使用状态机验证转换合法性。提示:
  3. 挑战题(难度⭐⭐⭐):实现完整的四层 OrderProcessor:领域(Order + Event + StateMachine)、服务(OrderService + 协程)、持久(InMemoryRepo)、集成(MockPaymentClient)。提示:参考第 8 节完整示例

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏