CLOVIS
12/01/2023, 5:32 PMinterface Users {
context(Raise<Failure.Get>)
suspend fun get(id: UserId): User
context(Raise<Failure.GetMe>)
suspend fun getMe(): User
sealed interface Failure {
sealed interface Get : Failure
sealed interface GetMe : Failure
data class NotFound(val id: UserId) : Failure,
Get
}
}
but, because we don't have unions, I end up with the relationship being marked the wrong way around: the global errors have to refer to the specific ones:
sealed interface CommonFailures
data object Unauthorized : CommonFailures,
Users.GetMe,
Foo.bar,
Foo.other
I guess this has the benefit that it's possible to easily view which are the routes that require authorization, but I would've preferred the opposite (the requirement being close to the route declaration).
This pattern has the benefit that all errors are really explicit, but it's quite verbose and hard to navigate if you're new to the codebase. IDEA also isn't great at showing "all the possible errors that this function can return".simon.vergauwen
12/02/2023, 4:29 PMGet
and GetMe
do in this example.
I like that the errors are grouped / nested inside of the interface, I've also tried all errors of an application in a single file and have everything split the same as before. This helps finding all errors in a single place, but doesn't couple them to the interface .. 🤷CLOVIS
12/02/2023, 4:30 PMUsers.Failure.GetMe
is “the super type of all possible errors returned by `Users.getMe`”.
I guess if we had unions it would be one.CLOVIS
12/02/2023, 4:31 PM