Skip to content

Instantly share code, notes, and snippets.

@pakoito
Last active January 11, 2021 19:20
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pakoito/0b942c6f68441b76756a7a761cb9272d to your computer and use it in GitHub Desktop.
Save pakoito/0b942c6f68441b76756a7a761cb9272d to your computer and use it in GitHub Desktop.
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