-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider adding a transactEither
method
#75
Comments
Yes, this would be nice. |
I haven't put too much thought into it yet, but something that rolls back the transaction on a Left() like
So it would still throw if for example a SqlException occurred. Would love to have your feedback / thoughts. |
Personally, I like to embrace exceptions with my User-defined / recoverable error types. My error hierarchy looks like
And I still compose / validate with Either (or a type similar to Cats Validated), but then throw if Left. My webserver then has a root-level exception handler that catches the Errors subclasses, and produces the error message, returning 500 for all others. |
cc @adamw |
Ah! Sorry should have looked in the open issues :) So there's two aspects here:
The downside of
Now, you might imagine having a |
@lbialy you might also be interested :) |
oh niiice |
Good argument Adam. I know my philosophy is hard-core direct style, so bear with me;
I find that business validations are not typically recoverable, except at (1) the immediate callsite, or (2) the root exception handler, where they are translated into, for example, HTTP error codes. Threading Either or Try vertically through the application quickly becomes tiresome, with little benefit if we never recover in the intermediate layers. Either becomes viral like any other monad, which results in more work for service methods to call other service methods. This breaks a programmer's zen flow. Exceptions let us bypass this issue entirely. While there's a stigma against Exceptions for control flow, extending ControlThrowable nearly eliminates the overhead (https://shipilev.net/blog/2014/exceptional-performance/). Note that I'm not arguing for removing Either entirely, but rather to localize its usage (ie, don't return Either from service methods). Here's an example of how this would look in Bootzooka. First, Fail would become throwable: abstract class Fail extends ControlThrowable Then we remove Either from the signature of service methods like UserService.changePassword; def changePassword(userId: Id[User], currentPassword: String, newPassword: String)(using DbTx): ApiKey =
???
val user = either {
validateUserPassword(userId, currentPassword).ok()
validateNewPassword().ok()
}.toTry.get
updateUserPassword(user, newPassword)
invalidateKeysAndCreateNew(user) Finally in the controller private val changePasswordEndpoint = authedEndpoint.post
.in(UserPath / "changepassword")
.in(jsonBody[ChangePassword_IN])
.out(jsonBody[ChangePassword_OUT])
.handle { id => data =>
try
val apiKeyResult = db.transact(userService.changePassword(id, data.currentPassword, data.newPassword))
Right(ChangePassword_OUT(apiKey.id.toString))
catch case f: Fail => Left(f)
} However, I understand that this is very much a preference, and I'm not opposed to having a value-based |
SqlExceptions may be transient (connection interruption, etc), and therefore recoverable. However, it greatly depends on the context so I wouldn't want to punish users by returning an Either or Try. When consuming external REST APIs it can make a lot of sense to use Try + exponential retries, but your DB should have a reliable connection to your application; therefore I argue that it's better to fail fast on a connection issue. The user can always retry again in the very rare cases of transiency, meanwhile retries may actually serve to mask an infrastructure issue. One exception is serialization anomalies, which might make sense to retry in the application. However that can be handled explicitly with a library like Ox. |
Ha yes, error handling is an eternal and eternally unsolved problem ;). No approach is perfect: checked exceptions (for many reasons), unchecked exceptions (unreliable, "lying" method signatures), Eithers. But, to provide some counter-points:
|
As described in https://softwaremill.com/direct-style-bootzooka-2024-update/#database-access
The text was updated successfully, but these errors were encountered: