Philipp Mayer
03/25/2022, 10:00 AMdata class MyDomainObject(val message: String)
//some adapter package
data class MyResponse(val localizedMessage: String)
fun MyDomainObject.toMyResponse(locale: Locale) : MyResponse {
/* imagine a lot of mapping here, calling other pure functions in the same file */
return MyResponse(this.message + locale.toString())
}
It’s a complex object, so placing the extension function inside the MyResponse
file would hinder readability.
I couldn’t really find anything regarding that in the kotlin docs.
What’s your take on it? I would love to hear different opinions!Sam
03/25/2022, 10:06 AMStrings.kt
for String
extension functions.ribesg
03/25/2022, 10:18 AMextensions
and using the extended type name as file name for simple QoL extensions (with not much logic, just shortcuts or little cool wrappings)
In your example, I understand what you want to do, but I would still define a proper MyDomainObjectMapper (or something like that) class containing the mapping function, or probably multiple functions. I would then have your extension above the class, calling it’s “entry point”. You could even hide the class as internal/private depending on what you do
I don’t really like “floating” extensions with complex behaviors, but maybe that’s from my Java background. I’d rather have a class, maybe even with an interface to eventually make it mockable for testingade
03/25/2022, 11:38 AMtherealbluepandabear
03/25/2022, 12:01 PMMyDomainObject+toMyResponse
. That's what I use all the time for my Android apps/other projects (learned it originally from Udemy):Matteo Mirk
03/25/2022, 12:14 PMtherealbluepandabear
03/25/2022, 12:36 PMMatteo Mirk
03/25/2022, 2:12 PMephemient
03/25/2022, 3:17 PMRichard Gomez
03/25/2022, 4:31 PMHere are some suggestions on how to organize Kotlin code, in particular for extensions which I think is a well-thought advice.
https://arturdryomov.dev/posts/kotlin-code-organization/@Matteo Mirk: IIRC a major downside to placing your extensions in ``src/main/kotlinX/third/party/` is that it will break IntelliJ's Command + Click (Go to Implementation?) for anything under that package and take you to your source set instead.
Matteo Mirk
03/25/2022, 6:30 PMtherealbluepandabear
03/26/2022, 12:05 AMPhilipp Mayer
03/26/2022, 10:05 AMfun DomainObject.toDto()
?
@ribesg I think you already brought up the point yourself and the Java/OO roots can be seen there. Honestly I see that this makes sense but I’d ask why you would not just use a function when that’s all that is necessary. 🙂
That reminded me actually of this article. Great read!
@Matteo Mirk overall a great read! That gives me a lot of ideas that I have to think about, not just only about extension functions.
Overall I see that I should’ve been more precise.
FWIW, here’s my view on it:
Our scenario are backend services, DDD and hexagonal oriented, meaning that the code (files,classes,functions) should all be readable without much technical knowledge, which also results in slicing the application in the business contexts. This basically denies the use of something like Strings.kt
or Extensions.kt
or an extra /extensions
package/dir/whatever.
Right now we still use nouns as file names, e.g. MyRepresentationCreator.kt
that actually holds fun MyRepresentation.Companion.from(someDomainObject)
.andylamax
03/27/2022, 5:19 AMDomaimObjectBuilders.kt
• for extension functions I used to have files called DomainObject.ktx.kt
but I grew out of that into DomainObjectUtils.kt
• If I have many extension functions, I would group them with the DTOs they are trying to interact with, something in the lines of DomainObjectDTOUtils.kt
ephemient
03/27/2022, 6:14 AMFoos.kt
for extensions on or relating to Foo
, although sometimes it's foos.kt
(lowercase) or FooExtensions.kt
. but commonly extensions are mixed with other declarations in the same file - e.g. MyDomainObject
, MyResponse
, and MyDomainObject.toMyResponse
all in the same file, instead of being split upPhilipp Mayer
03/27/2022, 4:11 PMTim Schraepen
03/28/2022, 3:45 PMMyResponse
used in so many different other classes then?
In my projects it's typically not used over many other classes, but can be contained within one class, so then the extension function also lives there, inside that one class, at the very bottom. Then again, those transformations are simple enough.
Maybe if there's a lot of "mapping" logic going on, I'd put it in a class called SomethingSomethingMapper
and probably unit test it if there's enough logic in there.
You can still have the extension function in that mapper (and probably unit test through there instead of via the mapper itself). I guess doing "mapping" is more specific than doing "building". Building is just creational, whereas mapping indicates more clearly that there's some translation going on (between boundaries, not language).Philipp Mayer
03/28/2022, 8:48 PMTim Schraepen
03/29/2022, 1:35 PMPhilipp Mayer
03/29/2022, 1:49 PMPhilipp Mayer
03/29/2022, 1:50 PMephemient
03/29/2022, 3:52 PMMyDomainMappers.kt
or similar file. if it doesn't cleanly belong to either type then at least it should be descriptive. but injecting a class to handle mapping is something that may make sense in some casesTim Schraepen
04/11/2022, 1:35 PM