Conversion of Entity to DTO Using ModelMapper

In this post, I will show how we can achieve the conversion of entity to DTO using the ModelMapper library.  We will basically create a simple REST API for orders while showing the transformation of Entity to DTO and vice versa.

Understanding Enterprise Architecture

In most enterprise architecture, you will have REST APIs. A consumer of these APIs sends a request and the server responds with a response. The transformation of request to response happens behind the API. You perform business logic and modify these objects.

Traditionally, there are three layers in the architecture. Web layer, business layer, and database layer.

So, your object in the database layer will be completely different from the same object in the web layer. Database entities from the database layer contain certain fields that you don’t need in the web layer. More so, any object from the web layer should be user-friendly. Users don’t have to guess what they are dealing with. It should be self-explanatory. This will be more clear when I show the implementation of this.

 

Separation of Layers between Entity and DTO

Data Transfer Objects (DTO) are the objects that move from one layer to another. These objects are more user-friendly and contain only the most required fields.

On the other hand, database entities represent database tables. A lot of auto-generated fields can be unnecessary for users to know about. Nevertheless, they are part of database entities. In DTO, we ignore these fields. Since these fields are auto-generated, our database layer code can handle that.

But when the object travels from the web layer to the database layer, it needs to be transformed for that layer to use. In the next section, I will show how we can achieve this conversion from entity to DTO using the ModelMapper library.

The Entity to DTO Using ModelMapper

ModelMapper library provides an easier way to convert an entity object to DTO and vice versa.

In this demo, I have a scenario where a customer orders an item. An order for the item gets created. We save order details, customer details, and the address of the customer.

To able to use this library in our application, add the dependency as follows:

implementation 'org.modelmapper:modelmapper:2.3.0'

Also if we want to use ModelMapper library functions, we will add a bean for the same as follows:

        @Bean
	public ModelMapper modelMapper()
	{
		return new ModelMapper();
	}

Previously, I stated that a customer will be able to order. So, we will implement this by having a REST API that will create Order details, Customer details.

Domain Layer

In this architecture, we have orders that customers order at certain addresses.

In a database entity diagram, it will look like below:

Entity to DTO using ModelMapper

A customer can order multiple items, so multiple orders. Multiple orders can go to a single address.

Our domain objects will look like below, starting with Order:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;

@Entity(name = "Order")
@Table(name = "orders")
public class Order implements Serializable
{
    private static final long serialVersionUID = 7385741327704693623L;

    public Order()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column(name ="order_item")
    private String orderItem;

    @Column(name = "description")
    private String description;


    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;


    @ManyToOne
    @JoinColumn(name = "address_id")
    private Address address;
    
    // Getters and setters omitted for demo purposes


}

The address will be:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity(name = "Address")
@Table(name = "address")
public class Address implements Serializable
{
    private static final long serialVersionUID = -439961851267007148L;

    public Address()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column
    private String street;

    @Column
    private String city;

    @Column
    private String state;

    @Column
    private String country;

    @Column
    private int zipcode;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List orderList = new ArrayList<>();


}

And Customer will be:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity(name = "Customer")
@Table(name = "customer")
public class Customer implements Serializable
{
    private static final long serialVersionUID = -2205735699915701334L;

    public Customer()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column
    private String email;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List orderList = new ArrayList<>();


}

These three objects order, customer, and address represent our database entities and part of the database layer. The rest of the persistence is straightforward with repositories.

Web Layer

Web layer mostly focuses on the controllers that we create for our APIs. These controllers are responsible for receiving the request from the client. Also, the objects that we will expose through APIs will be DTO object. This DTO Object for Order will look like below:


package com.betterjavacode.modelmapperdemo.dtos;

public class OrderDTO
{
    String orderItem;
    String orderDescription;
    String customerFirstName;
    String customerLastName;
    String customerEmail;
    String streetAddress;
    String cityAddress;
    String stateAddress;
    String countryAddress;
    int zipcodeAddress;

   // Getters and Setters omitted for demo

}

This DTO object includes fields from Order, Customer, and Address. Our API will receive this object in POST request, we will transform that DTO object to an entity object using ModelMapper library and then pass that entity object to our Service class to process further.

OrderController will be as follows:


package com.betterjavacode.modelmapperdemo.controllers;

import com.betterjavacode.modelmapperdemo.dtos.OrderDTO;
import com.betterjavacode.modelmapperdemo.models.Order;
import com.betterjavacode.modelmapperdemo.service.IOrderService;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/v1/betterjavacode/orders")
public class OrderController
{
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private IOrderService orderService;

    @Autowired
    private ModelMapper modelMapper;

    @PostMapping
    public OrderDTO createOrder(@RequestBody OrderDTO orderDTO)
    {
        Order order = convertToEntity(orderDTO);
        Order orderCreated = orderService.createOrder(order);

        return convertToDTO(orderCreated);
    }

    @GetMapping("/{customerId}")
    public List getAllOrders(@PathVariable("customerId") long customerId)
    {
        List orderList = orderService.getAllOrdersForCustomer(customerId);
        List orderDTOs = new ArrayList<>();
        for(Order order : orderList)
        {
            orderDTOs.add(convertToDTO(order));
        }
        return orderDTOs;
    }


    private Order convertToEntity (OrderDTO orderDTO)
    {
        LOGGER.info("DTO Object = {} ", orderDTO);

        Order order = modelMapper.map(orderDTO, Order.class);

        return order;
    }

    private OrderDTO convertToDTO (Order order)
    {
        OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
        return orderDTO;
    }
}

We have a POST API to create orders and a GET API to retrieve orders for a customer.

ModelMapper Library

In our controller, we are using ModelMapper bean to convert DTO object to entity and entity object to DTO.

How does the ModelMapper library actually achieve this?

When a mapper calls the map method, it analyzes the source and destination types to determine which properties to match. It uses a matching strategy and configuration to map these properties. Once, the properties are mapped, it will map the data.

So if we look at our DTO class, we have properties like customerFirstName, customerLastName that match to Customer Entity object, while properties like streetAddress, cityAddress will match to properties from Address object.

ModelMapper also offers a way to explicitly map the properties if you choose to do that.


modelMapper.typeMap(Order.class, OrderDTO.class).addMappings(mapper -> {
  mapper.map(src -> src.getBillingAddress().getStreet(),
      Destination::setBillingStreet);
  mapper.map(src -> src.getBillingAddress().getCity(),
      Destination::setBillingCity);
});

The library offers three types of matching strategies:

  1. Standard – In this strategy, the library matches the source properties to destination properties intelligently. This strategy is configured by default. All destination property name tokens must match.
  2. Loose – Properties of source and destination are matched loosely. If the property hierarchies of source and destination objects are dissimilar, then the loose strategy can work. The last destination property name must have all tokens matched.
  3. Strict – Source properties should strictly match destination properties. Tokens match in a strict order. This strategy allows no ambiguity.

A Complete Demo of Entity to DTO using ModelMapper

We have shown our REST Controller and Domain objects. Now, I will show how we can using postman to call this REST API by passing a DTO object to POST API.

We will create an order of an item that a customer orders.

In the request, I passed a DTO object that contains information for order, customer, and address.

In our service layer, we process converted entity objects, validate business rules and save this information to create the order.

Avoiding Technical Debt

Understanding the concept of DTO and Entity objects is important. When to use what kind of object can help you avoid technical debt. From personal experience, I have seen a lot of junior developers make the mistake of using entity objects in a web layer. Depending on your application, this can increase the complexity of the system.

Conclusion

In this post, I showed how we can convert entity to DTO using modelmapper library.  You can download the modelmapper library here. The code for this demo is available in my GitLab repository. If you enjoyed this post, consider subscribing to my blog here.

References

  1. Model Mapper Library – ModelMapper