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.