Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Final code example for "A domain driven approach to Kotlin's new types"
data class UserInfo(val id: String)
data class UserInfoDto(var id: String?)
// Open Inheritance
interface IViewState { }
class Idle: IViewState
class Loading(val percent: Int): IViewState
// Closed inheritance:
// Limit possible states and make illegal states unrepresentable
sealed class ViewState: AndThen<ViewState> {
class Idle: ViewState()
class Loading(val percent: Int): ViewState()
class Success(val userInfo: UserInfo): ViewState()
class Error(val message: String): ViewState()
override fun value() = this
}
// Encode behaviour in types
interface AndThen<T> {
fun <U> andThen(func: (T) -> AndThen<U>): AndThen<U> = func(value())
fun value(): T
}
sealed class Result<T>: AndThen<Result<T>> {
class SuccessWithValue<T>(val result: T): Result<T>()
class Success<T>(): Result<T>()
class Failure<T>(val error: Exception): Result<T>()
override fun value() = this
}
// Move validation to edges
sealed class UserInBusinessLayer: AndThen<UserInBusinessLayer> {
class ValidUser(val value:UserInfo): UserInBusinessLayer()
class InvalidUser(val value:UserInfoDto): UserInBusinessLayer()
class AbsentUser(val error:String): UserInBusinessLayer()
override fun value() = this
}
// And now everything together
fun main(args: Array<String>) {
val finalResult =
doAction()
.andThen { validate(it) }
.andThen { toUiState(it) }
.value()
System.out.println(printValue(finalResult));
}
// Create response from network
fun doAction(): Result<UserInfoDto> =
Result.Failure(RuntimeException("Fail!"))
// Make sure the user is valid
fun validate(value: Result<UserInfoDto>): UserInBusinessLayer =
when (value) {
is Result.SuccessWithValue<UserInfoDto> -> {
val id: String? = value.result.id
if (id != null)
UserInBusinessLayer.ValidUser(UserInfo(id))
else
UserInBusinessLayer.InvalidUser(value.result)
}
is Result.Success<*> ->
UserInBusinessLayer.AbsentUser("No user")
is Result.Failure<*> ->
UserInBusinessLayer.AbsentUser(value.error.message!!)
}
// Transform into a UI state
fun toUiState(user: UserInBusinessLayer): ViewState =
when (user) {
is UserInBusinessLayer.ValidUser ->
ViewState.Success(user.value)
is UserInBusinessLayer.InvalidUser ->
ViewState.Error("Invalid User")
is UserInBusinessLayer.AbsentUser ->
ViewState.Error(user.error)
}
// Helper to transform state into display string
fun printValue(viewState: ViewState): String =
when (viewState) {
is ViewState.Idle -> "Idle"
is ViewState.Loading -> "Loading"
is ViewState.Success -> viewState.userInfo.toString()
is ViewState.Error -> viewState.message
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.