How to gracefully transform entity into DTO in Kotlin?

1k Views Asked by At

I am working on Kotlin + SpringBoot web service, in which I want to transform DTOs into entities in the most convenient way.

Entities:

@Entity
data class Shop(
   @Id
   @GeneratedValue
   val id: Long,
   val name: String
   @OneToOne
   val owner: User,
   ...
)

@Entity
data class User(
   @Id
   @GeneratedValue
   val id: Long,
   val name: String,
   ...
)

DTO:

data class ShopDTO(
   val id: Long,
   val name: String,
   val ownerId: Long,
   val ownerName: String,
   ...
)

So when someone wants to create a new Shop, my service gets a ShopDTO(name, ownerId) as request body, then I need to transform it into Shop object to be able to save it to the DB. Now here is how my mapper function looks like:

fun fromDTO(source: ShopDTO) = Shop(
   id = source.id,
   name = source.name,
   owner = ???,
   ...
)

To be able to store a Shop with an owner I only need an id. It would be enough to create a new User with the given ownerId. To achive this I tried these solutions:

  • Add default value to the fields in the User class.
  • Make the fields nullable.
  • Add a secondary constructor. This also needs default values.
  • Use some reflection magic to create an empty object and then set the id.
  • Call a findById method on the UserRepository with the given id.

I want to keep the non-null, immutable fields of my entities and do not want to use reflection. Also do not want to run an unnecessary select DB query just to get back the user by the id.

Could you please suggest me other options? How would you handle this situation? Is there any good mapper framework in Kotlin which can solve this problem?

1

There are 1 best solutions below

0
Tanzim Kabir On

Firstly, your question says you want to do entity -> DTO, but actually you want to do DTO -> entity, so you should clear that up. Secondly, you are getting the shop name and owner Id in the ShopDTO. But you are assigning the owner Id to the shop Id in the your fromDTO(source: ShopDTO) function. Changing it up would be sufficient.

fun fromDTO(source: ShopDTO) = Shop(
    name = source.name,
    owner = ownerRepo.findById(source.ownerId)
)

Obviously, if you're using JPA, then you have to make a DB call to get the owner first. If your business logic doesn't ensure that a User with that Id exists, then you could write a method like this to make a user.

fun getOrCreateUser(ownerId: Long) = 
    ownerRepo.findUserById(ownerId) ?: User(
        id = ownerId,
        name = "Some random DefaultName"
    ).run(ownerRepo::save)

This would get a User by the Id if it exists, or create a new user with some generic name.

Do let me know if this solves your issue!