How to separate the dto mapping logic - java

I'm facing some problems to organize my mapping methods in my code. I'm doing the mapping logic in the controller layer but some entities needs diferents dtos to each operation (insert, update e delete).
I created a generic controller with 2 types: the original entity and the dto type. But this stucked it in this case with multiple dto representations. I'm not sure if create 3 generic types is a good way to handle this.
Another problem is that my controller layer is getting bigger with many mapping methods. Even using ModelMapper as automapper, some cases I prefer do it by myself than create complex converter classes.
How can I organize my dto mapping code and don't overload my controller with lots of mapping methods?
P.S: My project is a rest api with jax-rs, cdi and jpa

I suggest you to follow the following rules:
Separate the DTO that serve the API from the other domain classes, and use naming convention to quickly identify it (ex. XxxAPI). You may organize in a resource dedicated package : the controller, DTO and mapper classes.
Don't be afraid to write a lot of code, especially in mapper classes, you may use some IDE tricks to generate and test them.
Be careful using automapper, too much magic is dangerous, you may consider Builder patterns to facilitate your DTO / Domain mapping
Regards.

A common approach is to split the DTO conversion logic into its own class. Depending on the size of the project, creating a repository class may also be useful. This leaves us with three classes:
Controller: Performs the REST operations
Repository: Obtains the DTOs from a database or Data Access Object (DAO)
DTO Mapper: Converts the DTO to a domain object
The repository allows us to hide the DTO from the controller entirely. Instead, the controller will only deal with domain objects and is made unaware that a conversion from a DTO to a domain object occurred at all.
The repository (let's say for Foo domain objects created from FooDto objects) is:
public Foo {
private long id;
private String name;
// ...getters & setters...
}
public interface FooRepository {
public List<Foo> findAll();
public Optional<Foo> findById(long id);
public Foo create(long id, String name);
public Foo update(long id, String name);
public void delete(long id);
}
The DTO conversion logic would be:
public class FooDto {
private long id;
private String name;
// ...getters & setters...
}
public class FooDtoMapper {
public Foo fromDto(FooDto dto) {
Foo foo = new Foo();
foo.setId(dto.getId();
foo.setName(dto.getName();
return foo;
}
public FooDto toDto(Foo foo) {
FooDto dto = new FooDto();
dto.setId(foo.getId();
dto.setName(foo.getName();
return dto;
}
}
With the FooDtoMapper created, we can create a FooRepository implementation:
public class DatabaseFooRepository implements FooRepository {
#Inject
private DatabaseConnection dbConnection;
#Inject
private FooDtoMapper mapper;
#Override
public List<Foo> findAll() {
return dbConnection.getAllFromCollection("FOO", FooDto.class)
.stream()
.map(mapper::fromDto)
.collect(Collectors.toList());
}
// ...implement other methods
}
The dbConnection object is an abstraction of the database from which the DTOs are being pulled from. In this example, we can assume that getAllFromCollection("FOO", FooDto.class) returns a List<FooDto>, which we then stream and convert to a List<Foo> using the FooDtoMapper object (mapper). In your project, this will likely be replaced with JPA-specific code, but the principle still remains the same: Obtain DTOs from the JPA interfaces and convert them into domain objects using the mapper object.
This results in the following controller logic:
#Path("foo")
#Controller
public class FooController {
#Inject
private FooRepository repository;
#GET
public Response findAll() {
List<Foo> foos = repository.findAll();
Response.ok(foos);
}
// ...other controller methods...
}
Using this pattern, we abstract the logic for converting from DTOs into its own class and the controller is only responsible for dealing with domain objects.
In general, it is preferable to have many, simple classes that do one thing rather than putting all of the logic in a single class (like your original controller) in hopes of reducing the number of classes. For example, the FooDtoMapper is responsible for only converting from FooDto objects to Foo objects, and vice-versa. The DatabaseFooRepository is responsible only for getting DTOs from the database and using the FooDtoMapper to convert the DTOs to domain objects (i.e., getting domain objects from the database). And lastly, the FooController is only responsible for getting domain objects from the FooRepository (i.e., DatabaseFooRepository at run-time) and providing the necessary REST API metadata (i.e., HTTP status OK).
Note that in this case, the Foo and FooDto objects are identical and there isn't much of a reason to have separate Foo and FooDto objects (i.e., why not just store Foo objects in the database instead of FooDto objects?), but that is not always the case. Generally, the domain object and the DTO will vary. For example, the domain object may have a currency amount or date that must be converted to a String or other data structure that can be stored in the database (the DTO would have this String field while the domain object would have the actual field, such as a currency or date). In this case, I made the domain object and DTO the same for the sake of simplicity.

Related

How to customize the API metadata of springdoc-openai?

I'm trying to customize the springdoc-openapi, make it can work with my framework, but I meet two problems.
1. How to treat methods that do not start with is/get as properties of Model?
If users use my ORM framework by Java language, the property getters in the entity interface can either start with is/get like a traditional Java Bean, or don't start with is/get like a Java record, for example
#Entity
public interface Book {
#Id
long id();
String name();
int edition();
BigDecimal price();
#ManyToOne
BookStore store();
#ManyToMany
List<Author> authors();
}
Here, the wording that does not start with is/get is used, which looks like a java record, not a traditional java bean.
However, doing this will cause swagger-ui to think that the model doesn't have any attributes. So I have to change the behavior of swagger.
After some research, I found that this behavior can be changed using io.swagger.v3.core.converter.ModelConverter, which is the most likely solution.
However, springdoc-openapi does not explain in detail how to use ModelConverter in the documentation. Ultimately, this goal was not achieved.
2. How to control the shape of dynamic objects in HTTP response?
My ORM is GraphQL-style, its entity objects are dynamic so that data structures of arbitrary shapes can be queried, just like GraphQL does. For example
#RestController
public class BookController {
#AutoWired
private JSqlClient sqlClient;
// Query simple book objects
#GetMapping("/books")
public List<Book> books() {
return sqlClient.getEntities().findAll(Book.class);
}
// Query complex book objects
#GetMapping("/books/details")
public List<Book> bookDetails() {
return sqlClient.getEntities().findAll(
// Like the request body of GraphQL
BookFetcher$
.allScalarFields()
.store(
BookStoreFetcher.$.allScalarFields()
)
.authors(
AuthorFetcher.$.allScalars()
)
);
}
}
The first query returns a list of simple book objects in the format {id, name, edition, price}
The second query returns a list of complex book objects in the format {id, name, edition, price, store: {id, name, website}, authors: {id, firstName, lastName, gender}}
Dynamic objects can vary in shape, and these are just two special cases.
I expect swgger to tell the client the shape of the object returned by each business scenario. So, I defined an annotation called #FetchBy. It should be used like this
#RestController
public class BookController {
private static final Fetcher<Book> BOOK_DETAIL_FETCHER =
BookFetcher$
.allScalarFields()
.store(
BookStoreFetcher.$.allScalarFields()
)
.authors(
AuthorFetcher.$.allScalars()
);
#AutoWired
private JSqlClient sqlClient;
#GetMapping("/books")
public List<Book> books() {
return sqlClient.getEntities().findAll(Book.class);
}
#GetMapping("/books/details")
public List<#FetchBy("BOOK_DETAIL_FETCHER") Book> bookDetails() {
return sqlClient.getEntities().findAll(BOOK_DETAIL_FETCHER);
}
}
Declare the shape of the complex object as a static constant.
The #FetchBy annotation uses the constant name to tell swgger the shape of the returned dynamic object.
After some research, I found that this behavior can be changed using org.springdoc.core.customizers.OperationCustomizer, which is the most likely solution.
However, I found that the schema tree of swagger is not consistent with the generic type definition tree in the java language. For example, Spring's ResponseEntity<> wrapper will be ignored by swagger and will be not parsed as a node of schema tree. Theoretically speaking, this ability of swagger can be customized infinitely, so the two trees may not always be consistent and difficult to analyze.

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.

Can we convert entity to dto/pojo object patially?

Is there any approach to convert entity to dto object partially? Or are there any best practices for using the entity, pojo/dto and response object in MVC pattern?
You have couple of approaches. I assume you have a web project.
Manual mapping approach. Create Dto map it manually to your entity. This would involve all the boilerplate code around it - Builders, all argument Constructor, getters, setters whatever approach you will use to populate the DTO.
The different hydration Technics apply in different situations. Builder pattern presumes you would have an immutable objects. Getters and Setters are supported out opf the box by frameworks as Jackson. You may decide to minimize your boilerplate code by using lombok, imutables or other frameworks.
Mapping framework. Another option would be to use a supplied mapper like ObjectMapper, or FastMap there may be other choices. This would remove large portion of manual mapping you would need to establish following aproach 1.
Deserializer. Example of such aproach would be spring-data-rest where your repository returns the entities straight which are than serialized in JSON . Using normal spring MVC this would be to return your entity straight to your controller layer and based on annotations or other means (serializers) map it to your network format JSON, XML whatever you use.
These are the different options. Which one is best depends on your usecase.
One of possible approaches is copy constructor. The example below.
// entity annotations ...
public class EntityExample {
private Long id;
private String name;
private Integer score;
// getters and setters ...
}
public class DtoExample {
private Long id;
pricate String name;
// For serialization and deserialization
public DtoExample() {
}
public DtoExample(EntityExample entity) {
this.id = entity.getId();
this.name = entity.getName();
}
// getters and setters ...
}

DTO conveter pattern in Spring Boot

The main question is how to convert DTOs to entities and entities to Dtos without breaking SOLID principles.
For example we have such json:
{ id: 1,
name: "user",
role: "manager"
}
DTO is:
public class UserDto {
private Long id;
private String name;
private String roleName;
}
And entities are:
public class UserEntity {
private Long id;
private String name;
private Role role
}
public class RoleEntity {
private Long id;
private String roleName;
}
And there is usefull Java 8 DTO conveter pattern.
But in their example there is no OneToMany relations. In order to create UserEntity I need get Role by roleName using dao layer (service layer). Can I inject UserRepository (or UserService) into conveter. Because it seems that converter component will break SRP, it must convert only, must not know about services or repositories.
Converter example:
#Component
public class UserConverter implements Converter<UserEntity, UserDto> {
#Autowired
private RoleRepository roleRepository;
#Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleRepository.findByRoleName(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
....
Is it good to use repository in the conveter class? Or should I create another service/component that will be responsible for creating entities from DTOs (like UserFactory)?
Try to decouple the conversion from the other layers as much as possible:
public class UserConverter implements Converter<UserEntity, UserDto> {
private final Function<String, RoleEntity> roleResolver;
#Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleResolver.apply(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
}
#Configuration
class MyConverterConfiguration {
#Bean
public Converter<UserEntity, UserDto> userEntityConverter(
#Autowired RoleRepository roleRepository
) {
return new UserConverter(roleRepository::findByRoleName)
}
}
You could even define a custom Converter<RoleEntity, String> but that may stretch the whole abstraction a bit too far.
As some other pointed out this kind of abstraction hides a part of the application that may perform very poorly when used for collections (as DB queries could normally be batched. I would advice you to define a Converter<List<UserEntity>, List<UserDto>> which may seem a little cumbersome when converting a single object but you are now able to batch your database requests instead of querying one by one - the user cannot use said converter wrong (assuming no ill intention).
Take a look at MapStruct or ModelMapper if you would like to have some more comfort when defining your converters. And last but not least give datus a shot (disclaimer: I am the author), it lets you define your mapping in a fluent way without any implicit functionality:
#Configuration
class MyConverterConfiguration {
#Bean
public Mapper<UserDto, UserEntity> userDtoCnoverter(#Autowired RoleRepository roleRepository) {
Mapper<UserDto, UserEntity> mapper = Datus.forTypes(UserDto.class, UserEntity.class)
.mutable(UserEntity::new)
.from(UserDto::getName).into(UserEntity::setName)
.from(UserDto::getRole).map(roleRepository::findByRoleName).into(UserEntity::setRole)
.build();
return mapper;
}
}
(This example would still suffer from the db bottleneck when converting a Collection<UserDto>
I would argue this would be the most SOLID approach, but the given context / scenario is suffering from unextractable dependencies with performance implications which makes me think that forcing SOLID might be a bad idea here. It's a trade-off
If you have a service layer, it would make more sense to use it to do the conversion or make it delegate the task to the converter.
Ideally, converters should be just converters : a mapper object, not a service.
Now if the logic is not too complex and converters are not reusable, you may mix service processing with mapping processing and in this case you could replace the Converter prefix by Service.
And also it would seem nicer if only the services communicate with the repository.
Otherwise layers become blur and the design messy : we don't know really any longer who invokes who.
I would do things in this way :
controller -> service -> converter
-> repository
or a service that performs itself the conversion (it conversion is not too complex and it is not reusable) :
controller -> service -> repository
Now to be honest I hate DTO as these are just data duplicates.
I introduce them only as the client requirements in terms of information differ from the entity representation and that it makes really clearer to have a custom class (that in this case is not a duplicate).
personally, converters should be between your controllers and services, the only things DTOs should worry about is the data in your service layer and how which information to expose to your controllers.
controllers <-> converters <-> services ...
in your case, you can make use of JPA to populate roles of your users at the persistence layer.
I suggest that you just use Mapstruct to solve this kind of entity to dto convertion issue that you are facing. Through an annotation processor the mappings from dto to entity and vice versa are generated automatically and you just have to inject a reference from your mapper to your controller just like you normally would do with your repositories (#Autowired).
You can also check out this example to see if it fit your needs.
That's the way I'd likely do it. The way I'd conceptualize it is that the User converter is responsible for user / user dto conversions, and as such it rightly shouldn't be responsible for role / role dto conversion. In your case, the role repository is acting implicitly as a role converter that the user converter is delegating to. Maybe someone with more in-depth knowledge of SOLID can correct me if I'm wrong, but personally I feel like that checks out.
The one hesitation I would have, though, would be the fact that you're tying the notion of conversion to a DB operation which isn't necessarily intuitive, and I'd want to be careful that months or years into the future some developer doesn't inadvertently grab the component and use it without understanding the performance considerations (assuming you're developing on a larger project, anyways). I might consider creating some decorator class around the role repository that incorporates caching logic.
I think the way to do it cleanly is to include a Role DTO that you convert to the RoleEntity. I might use a simplified User DTO in case that it is read only. For example, in case of unprivileged access.
To expand your example
public class UserDto {
private Long id;
private String name;
private RoleDto role;
}
with the Role DTO as
public class RoleDto {
private Long id;
private String roleName;
}
And the JSON
{
id: 1,
name: "user",
role: {
id: 123,
roleName: "manager"
}
Then you can convert the RoleDto to RoleEntity while converting the User in your UserConverter and remove the repository access.
Instead of creating separate convertor clas, you can give that responsibility to Entity class itself.
public class UserEntity {
// properties
public static UserEntity valueOf(UserDTO userDTO) {
UserEntity userEntity = new UserEntity();
// set values;
return userEntity;
}
public UserDTO toDto() {
UserDTO userDTO = new UserDTO();
// set values
return userDTO;
}
}
Usage;
UserEntity userEntity = UserEntity.valueOf(userDTO);
UserDTO userDTO = userEntity.toDto();
In this way you have your domain in one place. You can use Spring BeanUtils to set properties.
You can do the same for RoleEntity and decide whether to lazy/eager load when loading UserEntity using ORM tool.

Is it ok to pass interface of DTO to DAO

It's about passing interface of DTO to DAO.
For example I have following code
public interface User {
String getName();
}
public class SimpleUser implements User {
protected String name;
public SimpleUser(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
// Mapped by Hibernate
public class PersistentUser extends SimpleUser {
private Long id;
// Constructor
// Getters for id and name
// Setters for id and name
}
I'm using generic DAO. Is it ok if I create DAO with using interface User instead PersistentUser?
User user = new PersistentUser(name);
UserDao.create(user);
I read a lot of topics on stack but not figured out is this approach ok or no. Please help me. Maybe this is stupid and I can achive only problems.
About separating beans.
I did this because some classes I want to share via API module, that can be used outside to create entities and pass them to my application. Because they uses interface I developed so I can pass them to my DAO for persisting.
Generally, I would say it is ok, but there are a few hidden problems. A developer could cast the object down or access some state via a toString method that shouldn't be accessible. If you don't be careful, it could happen that state is serialized as JSON/XML in webservices that shouldn't be serialized. The list goes on.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(PersistentUser.class)
interface User {
String getName();
}
Querying could look like this
List<User> dtos = entityViewManager.applySetting(
EntityViewSetting.create(User.class),
criteriaBuilderFactory.create(em, PersistentUser.class)
).getResultList();

Categories

Resources