What is the best approach when developing (JSON-based) REST services in terms of Data objects.
Should I split database model (back-end) from the front-end model?
Is it a good practice to always keep the DB related JPA Entities up to a specific layer and then convert them into a set of DTOs for the front-end?
For example, 3 layer architecture:
Controller
Service
Repository
Should I confine the DB entities (annotated with JPA annotations) to Repository
and Service
layers
And then make the Controller
only operate with another set of UI 'entities' (DTOs)?
This would require some kind of mapping between the 2 either automatic or 'manual'.
This allows for 'thin' front end entities.
For example in Backend we have the JPA annotations only and we have the owner as an Account
reference:
@Entity
public class Job {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Account owner;
But in the front-end layer I would have Jackson specific annotations. I don't need the whole Account
object, but only its id in the UI:
class Job {
long id;
String name;
long ownerId;
}
Update After experimenting with "manual" mapping, the conclusion is that it quickly becomes a mess.
In my case, I wanted the Repository
layer to return Entity (JPA) and the Service
layer to do the mapping and return the Dto. So for getting data from the DB, this seems pretty affordable, there is only 1 mapping involved (from Entity to Dto). But, when it comes to creating / saving entities, the problem is bigger with composite objects. For example:
Let's say I POST
a UserDto
(which contains UserProfileDto
as a composite object) from the API client to the Controller
. Now the Service
layer will accept UserDto
, but it will have to find the UserProfileEntity
corresponding to that UserProfileDto
.
Also, Repository's .save()
method returns the newly persisted Entity. Now this has to be mapped to Dto and then back to Entity if I want to use it further as part of another object (otherwise I will get the object references an unsaved transient instance - save the transient instance before flushing
error).
For example, if I do:
repository.save(profileEntity)
this will return a newly persisted ProfileEntity
, but now I need to map it to ProfileDto
in order to make it part of UserDto
before mapping again UserDto
to UserEntity
and persisting.
Note: Dto here are objects I am planning to use as a response to the client (with JSON related annotations). These live in the Service
and Controller
layers, whereas Entity are JPA related objects that only live in the Repository
and Service
layers.