REST development backend DTO vs frontend DTO - java

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.

Related

Returning Entity from Service method is a bad practice?

I've heard when you want to return some object from a service method, you have to define a DTO object (or POJO object generated with JSON Schema) instead of using an Entity.
To make it clear, here is the example:
We have an entity and a jpa repository for it:
#Data
#Entity
#Table(name = "tables")
public class Table {
#Id
private Long id;
private String brand;
}
This is a bad practice:
#Service
public class MyService {
#Autowired
private TableRepository tableRepository;
#Transactional
public Table create() {
Table table = new Table();
// Some logic for creating and saving table
return table;
}
}
This is a good practice:
#Service
public class MyService {
#Autowired
private TableRepository tableRepository;
#Transactional
public TableDTO create() {
Table table = new Table();
// Some logic for creating and saving table
// Logic for converting Table object to TableDTO object
return tableDTO;
}
}
Why is this so?
Thank you!
Probably you mean a DTO (Data Transfer Object), not DAO (Data Access Object). Let me clarify this:
Data Transfer Object:
A Pojo that represents a piece of information. Usually it has aggregated data in it.
Data Access Object:
An object that performs access to some kind of persistence storage for retrieving information, someone considers it a synonim of Repository, someone not.
Entity:
An object that represents data that has been retrieved from the database.
Why is returning an Entity from the Service considered a bad practice?
The reason is that the Entity is something that is very close to the database. It contains primary key, someone could guess your database structure from it and the set of the data in case of query can be verbose. Hence, it is preferable to have some kind of logic, usually a mapper, that hides primary key and aggregates data to be less verbose and to not expose the db structure. Also, while the Entity is built on the table structure, the DTO can be customized in base of caller needs. Usually it contains exactly the data that is needed for some action and nothing more than this. Suppose you have thirdy party software that calls your backend services: you should not expose the db structure (Entities) to this service. It is better to define a contract, with the minimal information needed for this thirdy party service to operate, and expose only this part of the information, hiding all the rest.
Hope that's a little bit more clear now.
Edit:
Of course there are other good reasons for using DTOs instead of Entities, this is only an introductory explanation to the subject.

How can I map/change/implements methods from one layer to another?

Good evening, I'm learning about using layers in the spring and I've encountered some problems that I don't know how to solve...
I know that the entity is exposed in the Repository, where we use Hibernate to perform operations.
In the service layer we have to use an Object to "hide" the entity, but how do we map Classes / Entities by methods?
I mean, we have this example, but I want to use JPA, and I can't use a ProductData id to search to a Product Repository, for example, how can I modify these methods to work without errors, please?
public ProductData findById(Integer id){
ProductEntity product = productRepo.findById(id);
ProductData data = new ProductData(product.getId(), product.getName(), product.getCeva());
return data;}

What is the proper way to create and validate an Entity Model and its DTO in a RESTful API?

I am developing my first RESTful API from scratch and with Spring Boot.
I have already created the endpoints, models and JPA repositories for "standalone" entities. But now that I started linking them together and after doing some research I got to the conclusion that I may have to create DTOs. I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know). I am thinking about creating a DTO by just replacing the class relations with just IDs.
This is how my entity is currently defined:
#Data
#Entity
#Table(name = "Orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#NotBlank
#NotNull
private String description;
#NotBlank
#NotNull
private Status status;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "employee_id_fk"))
private Employee employee;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "customer_id_fk"))
private Customer customer;
protected Order() {}
public Order(String description) {
this.description = description;
this.status = Status.IN_PROGRESS;
}
}
And my endpoint (this is what I must change):
#PostMapping("/orders")
ResponseEntity<EntityModel<Order>> createOrder(#Valid #RequestBody Order order) {
order.setStatus(Status.IN_PROGRESS);
Order newOrder = repository.save(order);
return ResponseEntity
.created(linkTo(methodOn(OrderController.class).getOrder(newOrder.getId())).toUri())
.body(assembler.toModel(newOrder));
}
Now, how should I validate the requests with this format?
Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.). Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests. I also can't remove the validations from the entity model because I'm using Hibernate to map them to tables.
Great questions!
I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know).
You're right. It's not because we can save bits and bytes (as it may look like), but because the lesser information you can ask from the client, the better the experience he/she would get (whether it's an external integrator or front-end/back-end application within the same company). Fewer amounts of data to encompass = easier to comprehend and less room for an error. It also makes your API cleaner from the design perspective. Is it possible to process your request without the field? Then it shouldn't be in your API.
Now, how should I validate the requests with this format? Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.).
You can also use #Valid to kick in validation for DTO inside the controller within the method mapped to endpoint. But as you mentioned correctly, all validated fields within DTO should be annotated with #NotNull, #NotBlank, etc. As a solution, to the "duplication" problem, you can create a base class and define all validations in there and inherit DTO and Entity from it. But please, don't do that!
Having the same fields and validation rules within DTO and Enity isn't considered duplication since they are separate concepts and each one of serves its specific purpose within its layer (DTO - top tier, Entity - most often lowest, Data tier). There are a lot of examples demonstrating it (e.g. here and here)
Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests.
It's a best practice to validate the request and a lot of projects are following it. In your example, it's very straightforward (direct mapping from DTO to Entity), but very often you would have a service layer that does some business logic before handing it off to a data layer (even in your example I recommend moving out your code from controller to a service layer). You don't want malformed request pass beyond the controller to handle it later with excessive if statements, null checks (that leads to a defensive code that's hard to follow and it's also error-prone).
Another note: you shouldn't sacrifice client experience and tell them or force yourself to add two more fields because it allows having one Object serving as DTO and Entity and simplifies development.
The last note: To map fields from DTO to Entity you can use one of the object mapper libraries.

Safe embedded entity with objectify

I have two entities.
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
#Entity
public class Step {
#Id
private Long id;
private String instruction;
}
And the following Clound Endpoint
#ApiMethod(
name = "insert",
path = "recipe",
httpMethod = ApiMethod.HttpMethod.POST)
public Recipe insert(Recipe recipe) {
ofy().save().entities(recipe.getSteps()).now(); //superfluous?
ofy().save().entity(recipe).now();
logger.info("Created Recipe with ID: " + recipe.getId());
return ofy().load().entity(recipe).now();
}
I'm wondering how do I skip the step where I have to save the emebedded entity first. The Id of neither entity is set. I want objectify to automatically create those. But if don't save the embedded entity I get an exception.
com.googlecode.objectify.SaveException: Error saving com.devmoon.meadule.backend.entities.Recipe#59e4ff19: You cannot create a Key for an object with a null #Id. Object was com.devmoon.meadule.backend.entities.Step#589a3afb
Since my object structure will get a lot more complex, I need to find a way to skip this manual step.
I presume you are trying to create real embedded objects, not separate objects stored in the datastore and linked. Your extra save() is actually saving separate entities. You don't want that.
You have two options:
Don't give your embedded object an id. Don't give it #Entity and don't give it an id field (or at least eliminate #Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
Allocate the id yourself with the allocator, typically in your (non-default) constructor.
Assuming you want a true embedded entity with a real key, #2 is probably what you should use. Keep in mind that this key is somewhat whimsical since you can't actually load it; only the container object can be looked up in the datastore.
I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.
The concept of the embedded is that the embedded content is persisted inside the main entity.
Is this the behaviour you are trying to configure?
The default behaviour of a Collection (List) of #Entity annoted class is to refer them instead of embed them. As you current configuration, the List<Step> variable does not have any annotation to override the default configuration, which is a different entity related to another one.
The error you are getting is because Objectify, when it saves the recipe entity, is trying to get the key of each step to create the relationship (and save them in the recipe entity), but if the entity step is not saved yet on the datastore, does not have a key
If you are trying to persist the steps inside the recipe entity, you need to setup objectify like this
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
public class Step {
private Long id;
private String instruction;
}
As you can see, I removed the #Id annotation (an embedded Entity does not require an ID because is inside another entity) and the #Entity from the Step class. With this configuration, Objectify save the step entities inside the recipe entity
Source: https://code.google.com/p/objectify-appengine/wiki/Entities#Embedded_Object_Native_Representation

Mapping JSON object to Hibernate entity

I'm going to start a project of a REST application managed with Spring and with Hibernate for my model.
I know that Spring allows you to get Java object from the HTTP Request (with #Consumes(JSON) annotation). Is there any conflict if this Java object is also a Hibernate entities? And is nested object working (like #ManyToOne relation)?
Maven dependency
The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
Domain model
Now, if you are using PostgreSQL, you need to use the JsonType from Hibernate Types.
In order to use it in your entities, you will have to declare it on either class level or in a package-info.java package-level descriptor, like this:
#TypeDef(name = "json", typeClass = JsonType.class)
And, the entity mapping will look like this:
#Type(type = "json")
#Column(columnDefinition = "json")
private Location location;
If you're using Hibernate 5 or later, then the JSON type is registered automatically by the Postgre92Dialect.
Otherwise, you need to register it yourself:
public class PostgreSQLDialect extends PostgreSQL91Dialect {
public PostgreSQL92Dialect() {
super();
this.registerColumnType( Types.JAVA_OBJECT, "json" );
}
}
The JsonType works with Oracle, SQL Server, PostgreSQL, MySQL, and H2 as well. Check out the project page for more details about how you can map JSON column types on various relational database systems.
Yes, this wouldn't be a problem and is actually a fairly common practice.
In the recent years I have come to realize that sometimes, however, it is not a good idea to always build your views based on your domain directly. You can take a look at this post:
http://codebetter.com/jpboodhoo/2007/09/27/screen-bound-dto-s/
It is also known as "Presentation Model":
http://martinfowler.com/eaaDev/PresentationModel.html
The idea behind that is basically the following:
Imagine you have the domain entry User, who looks like that :
#Entity
#Data
public class User {
#Id private UUID userId;
private String username;
#OneToMany private List<Permission> permissions;
}
Let's now imagine you have a view where you wanna display that user's name, and you totally don't care about the permissions. If you use your approach of immediately returning the User to the view, Hibernate will make an additional join from the Permissions table because event though the permissions are lazily loaded by default, there is no easy way to signal to the jackson serializer or whatever you are using, that you don't care about them in this particular occasion, so jackson will try to unproxy them (if your transaction is still alive by the time your object is put for json serialization, otherwise you get a nasty exception). Yes, you can add a #JsonIgnore annotation on the permissions field, but then if you need it in some other view, you are screwed.
That a very basic example, but you should get the idea that sometimes your domain model can't be immediately used to be returned to the presentation layer, due to both code maintainability and performance issues.
We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.
However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.
So we just put #JsonIgnore annotations to relations mappings like #OneToMany or #ManyToOnemaking them transient.
Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.
You can map the json request without using any library at REST web-services (Jersy)
this sample of code:
This hibernate entity called book:
#Entity
#Table(name = "book", schema = "cashcall")
public class Book implements java.io.Serializable {
private int id;
private Author author; // another hibernate entity
private String bookName;
//setters and getters
}
This web-services function
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String addBook(Book book) {
String bookName=book.getName();
return bookName;
}
This is sample json request:
{
"bookName" : "Head First Java",
"author" : {
"id" : 1
}
}
Since you are just starting, perhaps you could use Spring Data REST?
This is the project: http://projects.spring.io/spring-data-rest/
And here are some simple examples:
https://github.com/spring-projects/spring-data-book/tree/master/rest
https://github.com/olivergierke/spring-restbucks
As you can see in the examples, there are no extra DTOs beyond the #Entity annotated POJOs.

Categories

Resources