Hi. In our Java based project it was a common prac...
# getting-started
e
Hi. In our Java based project it was a common practice to use Mapstruct everywhere. There were some issues, but non-blocking for development. I'd love to hear feedback from people experienced in using Kotlin and Mapstruct. If the game is worth a candle
a
I do not use it in Kotlin. But if you need it why not to use the tool which works for you. I assume you use JavaBeans instead of data classes.
s
I had a similar scenario at where I work. We had big Java projects which we migrated to Kotlin. MapStruct worked okay, but still had some issues, especially with nullability compatibility. There are some existing mapping generators, but none satisfied what I needed. So I want to advertise my own project Mappie, which we use in several project where I work now and did find several nullability issues in the old MapStruct code.
🔥 1
s
I strongly recommend not to use
MapStruct
nor similar java libraries like
ModelMapper
They're not safe, add compilation overhead, often cause reduced readability and I'm pretty all the time saved would be quickly lost on debugging and tracing issues. With Kotlin, you have extension functions and named arguments which gives you all you need to create safe and readable mapping functions. Want to map
UserEntity
to
UserDto
for example? Just define something like below
Copy code
fun UserEntity.toDto() = UserDto(
    name = name, // simple mappings
    lastLoginTime = logins.map { it.time }.max() // in-place computations
    [all other named params]
)
That combined with IDE assistance (I've previously used this plugin, but it looks like it's now built-in to IDE with "Specify all arguments by name" quick action) makes the whole process painless and intuitive. I've jumped in to projects which used
MapStruct
and
ModelMapper
because that's what devs previously used in Java. In all cases moving to Kotlin extensions was a way to go and made life easier. Yes, that technically adds some code, but believe me it's worth it. Not only do you get compile time safety, but the code is easier to reason about (no hidden implicitness). Besides, it actually makes mappings easier to maintain IMO - since that's normal Kotlin code, it's automatically included within IDE refactorings, plus feedback loop is much faster due to instant inspections, warnings etc. I cannot judge on Kotlin-first solutions though as I never felt the need to try them
👀 1
e
@AndreyVanDenHaag The current project is fully Kotlin-based, so no JavaBeans. At least in source code, not talking about libraries
s
I haven't seen the kotlin-fill-class plugin before, that would also work nicely imo. About the compile-time safety, mappie is also fully compile-time safe and works with Kotlin code instead of string references. Unlike MapStruct, which was a major disadvantage as automatic refactorings didn't always work. But there is of course "magic", which can be seen as a downside, yes.
👍 1
Although, do note that shapeshift is full reflection based. So you won't have compile-time safety. I also did some performance measurements a while back were quite bad relative to MapStruct 😅
👍 1
e
@Szymon Jeziorski extension functions are good. But they are static functions. I use Spring + Kotlin. Mapping logic should be in Spring beans rather than static context functions
Because imagine some data must be passed through the sequence of these functions
I was tasked to add Locale. It ended up passing through a chain of these mapping functions. I was writing the code for a week.
@Stefan Koppier thanks for the info. Mappie has beautiful docs. Thanks guys for your opinion
s
> Mapping logic should be in Spring beans rather than static context functions Does it really? Mapping itself is a static action, getting the missing data may not be, plus nothing stops you from either creating extension function within bean class or creating extensions accepting extra arguments which would be my way to go. > I was tasked to add Locale. It ended up passing through a chain of these mapping functions. I was writing the code for a week. But was it really Kotlin's fault there? I've been in situations like this before and with IDE assistance it never felt any close to the pain you mention here. > It is complete shit I never recommend to anyone place logic outside DI context. That is totally subjective and my opinion would be quite opposite. I would always go with static functions for things that don't require DI context in any way - easier to refactor, easier to test, and often easier to reason about due to being side effect free. That's especially true with Kotlin, which makes things like functional programming and working with immutable data much easier. But again, that may be subjective, so no reason to fight about
👍 1
s
@Edgar Avuzi no problem! If you are going to give mappie a try, feel free to send me a DM or ask a question on the GitHub repo if you need any help/have feedback
👍 1
e
@Szymon Jeziorski this pure functional static thing is good at paper unless a requirement comes in to add a side effect in some place of mapping logic. And we end up restructuring all the things
I am talking from the perspective of maintainability and speed of development
No, there is no Kotlin fault. It is up to developer
Static functions are not easier to test
Static functions make the code rigid. There are a ton of books and videos about it. It is upside down. Components are easier to unit test because they are less dependent on each other
Ah ok, sorry, you mentioned to put these extension methods inside an object (I assume not object declaration aka static context singleton shorthand structure). But rather an object of DI component I assume. My fault. Extension methods are not always static
s
It's never black or white and always depends on so many factors. One team may find itself more efficient in one codebase, different one may be more comfortable with something looking entirely different. > Components are easier to unit test because they are less dependent on each other Pure functions do not depend on anything other than the input, how can that be harder to test? > Static functions make the code rigid. > There are a ton of books and videos about it Then how come functional programming is so popular nowadays? Depending on the context it may be a plus or a negative. For example you could say immutable classes for example could also make code more rigid, but with the benefit of thread safety, predictability and easier tracing for example. I could also say there are tons of books praising OOP and tons of books promoting functional programming and limiting mutability at all costs. Both can be fine depending on the context and it's okay to have different approaches, different tools work for different people in different circumstances 🙂
🆗 1
e
Regarding testability. I was writing about testing a code, in which one static function calls other static function and so on
a
@Edgar Avuzi Have you heard about the composability of well tested functions? Also…. chatGPT is not a source.
e
@Arjan van Wieringen Can you expand your thoughts further?
a
You say that testing static functions is hard when they call each other. If you have proper tests for individual functions this shouldnt be a problem. And it is, imho, easier to test. Besides, in test code you ideally shouldnt be aware of the internals of the function under test. So whether or not it calls 0 or a million other functions is irrelevant.
e
Yes, we can use mockk to mock static function calls
And test each function in isolation
You mentioned function composition. Calling static function from other static function is not an example of function composition
a
Why not? You compose more complex functions from simpler functions. That is still composition. Not the pure FP wat but still a form of composition. No need to mock the inner functions if they are well tested. It is even dangerous to do so. If one of the smaller functions changes behavior you will only catch this in the tests of the smaller function and not in the more composite ones. Let alone that you need to modify all the mocks.
e
The idea of unit testing is to test a component (function/ class/ ...) in isolation. Component dependencies are being mocked during unit testing
In case of static func A calls other static func B, func B is a dependency of A
a
Suit yourself then :)
e
This is the fundamentals of unit testing
a
Okay. So you mock ALL inner functions?
e
What you described is rather integration tests
a
I never said unit tests.
e
Ok
a
I said tests
So you mock the kotlin stdlib functions as well?
Since they are a dependency. That is just the fundamentals of unit testing
e
Ok you caught me. I should have excluded std lib small functions
a
Okay, so what about a well tested library of yourself? Of they are a dependency…..
e
I have no open source libs. Only pet projects
a
Doesnt need to be open source. You can have libraries of your own in your project as gradle module
So. You have 1 module which is well tested, which you include in 12 pet projects. You are going to mock all those welk tested functions inside those 12 pet projects because of “the fundamentals”?
e
Sometimes we mock library stuff as well if we need to unit test only our project component. But never std lib small functions
a
But why not always? Thats the fundamentals right?
e
I already said about exclusion. You reiterate
a
Wow, you are friendly. Bye. Enjoy your dogmatism
j
I use mapstruct at work in my java projects I would do mapping manually in kotlinland I never mock mappers. If the dependency returns a simple and intuitive result without hitting external infrastructure like a disk or db or webservice and its easy enough to construct a real version then I don't mock it.
Oh and static functions are fine! If you don't need to inject configuration then there's no reason to put logic in a configurable bean. YAGNI. And if you ever do need it it's very easy to convert to a bean or make a bean that wraps a static function
e
If we have a chain of such static functions A, B, C, D, E, F and we need to put F into the component context, we put all 6 functions as well. Like do 1 enabler MR just to prepare the code
It is a complete waste of time. It would never ever happen if those functions were near context. That's why I put logic into components. Again. Putting them into a component does not make them automatically not pure
Oh and static functions are fine! If you don't need to inject configuration then there's no reason to put logic in a configurable bean. YAGNI. And if you ever do need it it's very easy to convert to a bean or make a bean that wraps a static function
That's exactly the thing that at least I do not know the future. I doubt there is a magician here in this chat as well. Whatever requirement can come in. Keeping the logic in the component autoenables you to implement the exact feature and not restructuring the code as enabler activity. It is time consuming to restructure things. Expesialy when the code is rigid
Business wants us to iterate fast and not juggle the code and do countless refactoring activities as a pre activity to implement features from ticket. Like stretching before the fight VS always being stretched and ready for changes. So there must be rules. Do 1, 2, 3 and you're good. That works for our team

https://youtu.be/kgLS0NGLCHo?si=W8V7xbiK48WmCF4J

j
That video seems like nonsense to me. Not sure what opinion the video exists to argue with but the video itself says things like " you can't pass static functions around". perhaps he never had a chance to use java 8
🆗 1