Last active
January 11, 2021 19:20
-
-
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"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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