kotlinintermediate

Sealed Hierarchies for Domain Modeling

Use sealed interfaces and classes for exhaustive domain modeling with when expressions.

kotlin
// Sealed interface allows multi-level hierarchies
sealed interface PaymentMethod {
    data class CreditCard(
        val number: String,
        val expiry: String,
        val cvv: String
    ) : PaymentMethod

    data class BankTransfer(
        val accountNumber: String,
        val routingNumber: String
    ) : PaymentMethod

    sealed interface DigitalWallet : PaymentMethod {
        data class PayPal(val email: String) : DigitalWallet
        data class ApplePay(val deviceToken: String) : DigitalWallet
        data class GooglePay(val token: String) : DigitalWallet
    }

    data object Cash : PaymentMethod
}

// Processing with exhaustive when
fun processPayment(method: PaymentMethod, amount: Double): String = when (method) {
    is PaymentMethod.CreditCard -> "Charging $$amount to card ending ${method.number.takeLast(4)}"
    is PaymentMethod.BankTransfer -> "ACH transfer of $$amount"
    is PaymentMethod.DigitalWallet.PayPal -> "PayPal charge to ${method.email}"
    is PaymentMethod.DigitalWallet.ApplePay -> "Apple Pay: $$amount"
    is PaymentMethod.DigitalWallet.GooglePay -> "Google Pay: $$amount"
    PaymentMethod.Cash -> "Cash payment: $$amount"
}

fun fee(method: PaymentMethod): Double = when (method) {
    is PaymentMethod.CreditCard -> 0.029
    is PaymentMethod.BankTransfer -> 0.005
    is PaymentMethod.DigitalWallet -> 0.015  // covers all digital wallets
    PaymentMethod.Cash -> 0.0
}

// UI State modeling
sealed interface UiState<out T> {
    data object Loading : UiState<Nothing>
    data class Success<T>(val data: T) : UiState<T>
    data class Error(val message: String, val retry: (() -> Unit)? = null) : UiState<Nothing>
    data object Empty : UiState<Nothing>
}

fun <T> UiState<T>.render(): String = when (this) {
    is UiState.Loading -> "Loading..."
    is UiState.Success -> "Data: $data"
    is UiState.Error -> "Error: $message"
    is UiState.Empty -> "No data"
}

fun main() {
    val payments = listOf(
        PaymentMethod.CreditCard("4111111111111111", "12/25", "123"),
        PaymentMethod.DigitalWallet.PayPal("user@test.com"),
        PaymentMethod.BankTransfer("123456", "021000021"),
        PaymentMethod.Cash
    )

    payments.forEach { method ->
        println(processPayment(method, 99.99))
        println("  Fee rate: ${fee(method) * 100}%")
    }

    // UI state
    val states: List<UiState<String>> = listOf(
        UiState.Loading,
        UiState.Success("Hello"),
        UiState.Error("Network error"),
        UiState.Empty
    )
    states.forEach { println(it.render()) }
}

Use Cases

  • Payment processing with type-safe methods
  • UI state management patterns
  • Exhaustive domain event handling

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.