Hi folks, i'm curious how you are (de)serialisatin...
# announcements
t
Hi folks, i'm curious how you are (de)serialisating DTOs/data classes into/out of your microservices. Currently we're using a custom Jackson mapper in our ktor client, http/graphql servers and kmongo. We are using Jackson annotations and allowing the (de)serialization to occur implicitly, but after troubleshooting a tricky bug that result from this, I had the bright idea of explicitly converting our internal types into Dtos inside a DDD type anti corruption layer. For example
data class Person(val name: Name, val age: Age)
would have a corresponding
data class PersonDto(val name: String, val age: Int)
and then inside an ACL there would be explicit conversions between the two ... maybe something like this
Person.toDto()
and
Person.fromDto()
. As we've started moving away from allowing types to be converted implicitly, I'm beginning to fell that the slight increase in transparency isn't worth the additional boilerplate. Any thoughts on this one way or another?
t
I 100% recommend doing this everywhere. For me, the lack of additional boilerplate you get from explicit conversion isn’t worth the potential headaches when things go wrong. It also enables the domain the grow differently to the DTO/View, and I have the knowledge that adding a field to the domain doesn’t automatically make it available elsewhere. The mindset I take is that the DTO/view and the domain model are not the same thing even though they look the same a lot of the time.
👍 3
t
That's reassuring to hear! I especially like the final para .. great way to demonstrate the why. What approach do you take in terms of converting a domain object into a Dto and vice versa? Currently we're following a package structure like this:
Copy code
domain - business logic/domain specific stuff
infrastructure - mostly repositorys/things that are driven by the domain
interfaces
   - acl
   - dto
usecases - the functionalities exposed by the service
So all of the dto acl logic is in the same common area and the domain is never exposed to it. And basically inside a use case we do the conversions to/from domain objects/dtos. Do you take the same approach/view inside your db logic interms of using and ACL/Dtos or do you rely on implicit conversion there?
Also, would be interested in any counter views out there!?!? 🙏
t
I organise packages by functionality as much as possible, so all objects live side-by-side, so it would in fact be more like:
com.business.domain.feature.feature_a
and that would have usecases, domain objects, views etc. all in there
The idea being that if you change the location of something you can lift n shift the whole package - you’re not picking from several different places
☝️ 1
t
Ah that's interesting 🤔
t
It makes life easier when (nearly) all of the code you need for a specific feature is co-located
t
I think there's a name for that approach ... Ducks?
Generally with our current approach all the code inside a microservice should be related to the functionality of that service and so quiet focused even though it's split across domain,infra,interfaces,usecase. Then once you're inside the domai package, there's only domain specific code and objects. So no mention of say an http request is ever found inside the domain packages.
👍 1
t
In Java land it’s just called “package by feature”. I don’t know of any other name
t
Ahh k cool. Ya ducking is from react/redux iirc
t
That’s quite standard (and the way I used to organise things). Once someone told me about package by feature though it seem so intuitive and I would struggle to go back.
There’s arguments to be made for both approaches, it’s now just the way I prefer to organise my code
t
Fair enough! It sounds like an investigation is worthwhile
Ya so many approaches!
Just for clarity, would you consider Login and Logout separate features, or part of an account feature?
t
Re: initial question/answer, I would agree with Colin. Binding two separate models that serve different purposes (domain/DTO) together will be a long term headache because you will inevitably need to split them anyways.
👍 1
t
How about db serialisation ... Do you explicitly covert to a dto and then serialise or allow for implicit serialisation then? Seems to me schema versioning and migration would be easier with explicitly dto conversion
t
I do actually like your approach of splitting concerns apart into separate packages. I wouldn't necessarily recommend the following if the context isn't a massive application, but if a product is large enough I might even split the various layers into separate modules or projects. That's only useful if you end up having teams of people working on it & you want the ability to swap out layers, though - it can be overkill until you need it. Package-level divisions are good enough as they keep your code well organized in terms of dependencies but don't have the overhead of the multi-module approach
Any implicit serialization is a trade-off - if you ever need explicit control of something, you'll need to either: 1) Gradually add control of an implicit layer, complicating your implicit mechanisms in the process 2) Refactor the implicit piece to an explicit piece. This is normal in software, though - you'll always eventually need to add more control to handle use cases. And implicit behavior can go a long way towards getting the rest of your project done. If explicitness seems valuable to you now, maybe it's a good idea. If it seems like a distraction and an implicit/automatic solution seems to work just fine, maybe wait. Unless the latter is prohibitively difficult to convert to the former.
🙏 1
t
re: login/logout - yes, absolutely different features
t
👍
Brilliant thanks for sharing your thoughts and experiences!