I am making a REST service with Spring Boot, and I am having trouble at the point where I convert JSON requests/DTOs to the corresponding entities, specifically when an entity contains a reference to another entity. Say we have these objects for example:
data class BookEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?,
val name: String,
@ManyToOne
@JoinColumn(name = "author_id")
val author: AuthorEntity,
)
data class AuthorEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Int?,
val name: String,
)
data class BookRequestDto(
val name: String,
val authorId: Int,
)
The application has controller-service-repository layers, and as far as I know I am supposed to perform the DTO -> entity conversion in the controller layer, and then pass the resulting entity to the service layer. However, in order to convert the book request DTO to an entity, I first have to fetch the appropriate Author entity from the repository based on the given authorId
. My question is: where exactly should I do that?
Given that the service layer should only accept entities, it looks like I should do the author fetching in the controller layer. But that would mean that the Book controller would need to have access to the Author service layer, and that's what I'm not sure is good practice.
Here is what I mean (using obvious extension functions to do the DTO/entity conversions)
@RestController
@RequestMapping(path = ["/books"])
class BookController(
private val bookService: BookService,
private val authorService: AuthorService,
) {
@PostMapping
fun createBook(@RequestBody bookDto: BookRequestDto): ResponseEntity<BookResponseDto> {
val author = authorService.get(bookDto.authorId)
val bookEntity = bookDto.toBookEntity(author = author)
val createdBook = bookService.create(bookEntity)
return ResponseEntity(createdBook.toBookResponseDto(), HttpStatus.CREATED)
}
}
Is this the usual way of doing this, or is mixing multiple services in a controller a bad idea? I clearly have to access the author repository somewhere, I just don't know where the best place would be. Is there a better way?
This might get closed as an opinion based question :), but ...
.toBookResponseDto()
type methods, request/response should not know about each other or the database objects at all, that is strictly a service layer thingAuthorRepository
or an AuthorService
. You only need those for the root object (in this case Book
) and then your service and repository would have a findBooksByAuthor
method. You only need the Author
service/repository when the Author
is the root. I.e. listAllAuthors()