Do you have an opinion on how to structure failure...
# arrow
c
Do you have an opinion on how to structure failure types? I tend to structure my applications as multiple interfaces that each represent an independent part of the overall system. I have a few shared error types that are common to all endpoints (stuff like "the other side isn't responding", "you are not authorized to make this request", …). Currently, I nest the error types with their service:
Copy code
interface 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:
Copy code
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".
s
I follow an extremely similar pattern, except I am not sure what
Get
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 .. 🤷
c
Users.Failure.GetMe
is “the super type of all possible errors returned by `Users.getMe`”. I guess if we had unions it would be one.
Here's a real example: https://gitlab.com/opensavvy/notes/kotlin-fullstack/-/blob/main/core/src/commonMain/kotlin/Account.kt?ref_type=heads (it also has the service/reference split, which is a pattern I love, but is not directly related to Arrow) (it doesn't have context receivers because KMP)