I am familiar with several approaches to how this problem can be solved, but probably I miss something really useful:
I have an API entity:
data class SomeApiEntity(
val id: Long? = null,
@field:NotBlank
val name: String,
val someIntData: Long,
)
And Spring JPA Entity:
@Entity(name = "some_entity")
class SomeEntity(
@field:NotBlank
@Column(name = "name", nullable = false)
val name: String,
@Column(name = "some_int_data", nullable = false)
val someIntData: Long,
) {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
var id: Long? = null
}
Now I need to come up with a way to convert these entities from one to another.
The most popular solution is to implement methods in these entities which should look like this:
This one in SomeApiEntity
fun toApiEntity(): SomeApiEntity = SomeApiEntity(
id,
name,
intData,
)
And this one in SomeEntity class.
fun toEntity(): SomeEntity = SomeEntity(
name,
intData
)
But I find this solution unsuccessful and poorly scalable.
Large entities, especially with conversion logic, and especially if other Spring Beans are used there, it becomes possible to test. Well, small entities, like here, become unified with large ones.
I know the solution with implementing Converter<S, T> interface in Spring, but it is also bad because it solves only one-way conversion and gives us a small overhead in terms of the amount of code that will have to be written in the future.
I'm not sure if there's a best practice particular on Spring, but on Android development, I tend not to create those methods on the class itself (e.g. a method to convert a Entity to a DTO on the Entity class). The reason why I don't do it is that I will tightly couple both classes together and I also don't think is the Entity's reposability to know how to map itself to a domain and vice versa.
What I do is I usually create a extension function that does the same, but on a separate file, like so:
fun SomeDTO.toApiEntity() = SomeApiEntity(
id = id,
name = name,
intData = intData,
)
You can test it just like any other method, if you want:
@Test
fun `GIVEN SomeDTO object WHEN toSomeApiEntity called THEN must return SomeEntity with same values`() {
// GIVEN
val name = "Jonh"
val id = 1L
val intData = 254
val someDTO = SomeDTO(id = id, name = name, intData = intData)
val expectedSomeEntity = SomeEntity(id = id, name = name, intData = intData)
// WHEN
val actualSomeEntity = someDTO.toSomeApiEntity()
// THEN
Assert.AsserEquals(expectedSomeEntity.id, actualSomeEntity.id)
Assert.AsserEquals(expectedSomeEntity.name, actualSomeEntity.name)
Assert.AsserEquals(expectedSomeEntity.intData, actualSomeEntity.intData)
}
If your SomeApiEntity class is a data class and all it's atributes are data classes as well, you can simplify the assertion, like so:
Assert.AsserEquals(expectedSomeEntity, actualSomeEntity)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With