https://kotlinlang.org logo
#arrow
Title
# arrow
c

CLOVIS

03/23/2023, 9:12 AM
When you're representing error cases as a sealed class, what's your preferred solution to send them to the client? I don't want to duplicate the entire hierarchy for serialization.
d

dave08

03/23/2023, 10:07 AM
Why don't you just serialize the sealed class and it's classes?
I do that for my logs (with a wrapper class to provide more context...)
c

CLOVIS

03/23/2023, 10:08 AM
They're in the domain layer, not the HTTP layer.
d

dave08

03/23/2023, 10:09 AM
But the HTTP layer has access to them, no?
c

CLOVIS

03/23/2023, 10:09 AM
Internally yes, but it's not allowed to send them via the network.
d

dave08

03/23/2023, 10:10 AM
I don't want to duplicate the entire hierarchy for serialization.
sounds like you need to send all of their contents anyways...?
c

CLOVIS

03/23/2023, 10:11 AM
If I serialize the domain classes, then they are part of the HTTP API. I don't want to worry about breaking the HTTP API when I edit the domain layer.
d

dave08

03/23/2023, 10:16 AM
Do you have a standard way of representing errors that you need to return to apps/etc.. (like
data class Error(val code: Int, val name: String, message: String)
)? (How would you manage your contract with them otherwise?... I mean w/o copying over the while hierarchy... since it's not "just for serialization", but acting as an actual contract...) If so, it's just a matter of adding those fields to the base of the sealed class as an interface, and then just serializing that...
e

Emil Kantis

03/23/2023, 10:30 AM
I think I'd add something like this in the HTTP layer, I wouldn't like to add HTTP status codes in the domain
Copy code
fun BusinessError.toHttpBody() = when(this) {
  is UserNotFound -> Error(404, "USER_NOT_FOUND", "User $id was not found")
  is OperationNotAllowed -> Error(403, "SOMETHING", "...")
}
d

dave08

03/23/2023, 10:40 AM
Yeah, you're right... that's better for http status codes, but for an error code that's more linked to the business logic, it could still be there, and then transformed to an http status code on the http layer Depending on how complex your structure is (and if you're using KotlinX Serialization), you could always convert the domain errors to JsonElement on the http layer and maybe even use https://github.com/nomisRev/kotlinx-serialization-jsonpath to map results. (if there's some kind of common structure in them... otherwise, it might just be easier to do what Emil proposed...)
c

CLOVIS

03/23/2023, 10:42 AM
@Emil Kantis I'm doing something similar, the problem is converting them back to domain errors client-side.
e

Emil Kantis

03/23/2023, 10:45 AM
Is it a service-to-service call? Frontend to backend? In general I would use the
Error::code
and have a separate set of domain errors on the other side
c

CLOVIS

03/23/2023, 10:45 AM
Backend–frontend
They share the domain layer, though.
So you could think of it as a local service delegating to a remote service.
e

Emil Kantis

03/23/2023, 10:47 AM
Why are you not allowed the share the domain errors via network then, if they're shared to begin with? 😕
c

CLOVIS

03/23/2023, 10:48 AM
I'm allowed to share the domain error, not the specific classes they are used to implement. What if someone renames a field in the domain? It shouldn't be a breaking HTTP change when the HTTP layer wasn't even modified.
I guess there's no other option than duplicating everything.
e

Emil Kantis

03/23/2023, 10:52 AM
Is your frontend Kotlin-based? 😄
c

CLOVIS

03/23/2023, 10:52 AM
Yes, but a third-party is also consuming the HTTP API.
d

dave08

03/23/2023, 10:54 AM
Or having a custom serializer that does the conversion? Then it would break if something was changed in the domain. If anyways you'd have to map from the domain to the copied http layer classes, might as well just to it straight to json...
c

CLOVIS

03/23/2023, 10:55 AM
I guess the HTTP layer could register serializers for the domain classes and maintain them.
Thinking about it, maybe that's easier than having a bunch of DTOs. Harder to document, though.
d

dave08

03/23/2023, 10:58 AM
How would you document with DTOs?
c

CLOVIS

03/23/2023, 10:58 AM
DTOs are regular classes, they can have Kdoc
d

dave08

03/23/2023, 10:58 AM
But that's not what you would release as http api docs?
c

CLOVIS

03/23/2023, 11:09 AM
Indeed. I don't have proper API docs currently.
d

dave08

03/23/2023, 11:27 AM
All that dokka really gives you (apart from the text you wrote there) are the serialized properties and the hierarchy... I'd maybe consider some better alternative... otherwise, you could avoid a bunch of complexities there that an api user would never really need to know about.
most server frameworks have some kind of openspec generators...
c

CLOVIS

03/23/2023, 12:41 PM
Ktor doesn't, sadly.
c

CLOVIS

03/23/2023, 12:42 PM
That's for hosting a file you wrote yourself.
c

CLOVIS

03/23/2023, 12:44 PM
That's a custom API over Ktor that is configured via annotations and doesn't use the regular Ktor methods
d

dave08

03/23/2023, 12:45 PM
e

Emil Kantis

03/23/2023, 12:45 PM
I think OpenAPI support is on the Ktor roadmap
c

CLOVIS

03/23/2023, 12:45 PM
I know how to do a Google search, you don't have to copy-paste all results you see.
@Emil Kantis It's not. They released an IntelliJ Ultimate plugin that can generate OpenAI specs if you use the regular Ktor methods statically (I don't, I have my own lib that uses Ktor internally)
e

Emil Kantis

03/23/2023, 12:50 PM
Oh, okay.. unfortunate 😞
d

dave08

03/27/2023, 9:14 AM
A little late to add this @CLOVIS, but maybe this might be good for you (if you'd want another dependency to your project...). It's more explicit/dsl-like than a regular serializer, and breaks if you domain does: https://github.com/uberto/kondor-json
It can even generate json schema (possibly making documentation easier...)
c

CLOVIS

03/27/2023, 9:16 AM
That's essentially the same as KotlinX.Serialization but without the compiler generator, isn't it?
Ah, JSON Schema is interesting
d

dave08

03/27/2023, 9:18 AM
No it's not the same as KotlinX Serialization... because you still need to convert from domain objects to http layer objects if you use the compiler generator... here, you explicitly define the mapping to the Json...
6 Views