Sunday, March 25, 2012

Java architecture with DTOs

The DTO Discussion

The DTO (Data transfer objects) pattern is really useful if you intend to achieve a clean separation between the service layer and the presentation layer of the application. DTOs are part of the service layer API and therefore something that is best to have than not to.

DTOs also serve another purpose: Adapt model values, which in most cases are normalized, to view-friendly values, which most of the time presents particular values from an object graph and also calculated values.

Very often, applying the DTO pattern in a given architecture is not an easy task, you earn an application easy to maintain  by spending a lot of time (shallow) copying data from one object to another.

One solution for the data copy problem can be found by using a framework, in this case jDTO Binder. jDTO Binder makes easy for the programmer to achieve some tasks:


  • Receive form values and copy them to target business objects.
  • Create lists with data from particular properties of an object graph.
  • Add to those lists calculated values.
  • Change the format of the data to be more appropriate for display or network transmission.
I've taken the time to create a sample maven project with a very popular setup: Spring Web MVC, Spring Framework and Hibernate. Although you can use this project as a starting point to build a web application with this technologies, this project incorporates jDTO Binder into its ecosystem and demonstrates how it makes your life a lot easier.

To run the project, just unzip the file, open the terminal and type the following commands:

$ cd <path to the unzipped folder>
$ mvn clean install tomcat:run

Project Description and Walkthrough.


Required Skills: To go through this project, basic Spring Web MVC knowledge is required. Even though the important part is written on the service layer, Spring MVC and Spring Context knowledge helps a lot to put all the pieces together.

The DTOs

The following snippets shows the DTOs this project uses and its purpose:

public class PizzaDTO implements Serializable {
    
    @Source("id")
    private Long pizzaId;
    
    
    @Sources(value={@Source("pizzaName"), 
        @Source("price")}, merger=StringFormatMerger.class, mergerParam="%s ($ %.2f)")
    private String description;
    
    private double price;
    private int rating;
    ...
}


The first DTO to analyze is the PizzaDTO, this one is intended to serve in a listing of available pizzas. It has a description attribute which is intended for display and is created from the pizza name and the pizza price in a formatted string.

public class PizzaOrderForm implements Serializable {
    
    @NotEmpty //validation annotation
    @Source("whoOrderedName")
    private String customerName;
    
    @NotEmpty //validation
    @Source("contactNumber")
    private String customerPhone;
    
    @NotEmpty //validation
    private String customerAddress;
    
    @Size(min=1) //validation
    @DTOTransient
    private List<PizzaDTO> pizzas;
    ...
}

The second DTO is really the model of a form to take a pizza order, it does not only have DTO binding annotations but also standard JSR 303 bean validation annotations so the data can be validated automatically.

public class OrderDTO implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Long id;
    
    @Source("whoOrderedName")
    private String customerName;
    
    private String customerAddress;
    
    @Source("contactNumber")
    private String customerPhone;
    
    @Source(value="orderDate", merger=DateFormatMerger.class, mergerParam="MM/dd/yyyy hh:mm")
    private String orderDate;
    
    @Source(value="pizzas", merger=SumMerger.class, mergerParam="price")
    private Double orderPrice;
    ...
}

Next on the line is a DTO intended for a list of pizza orders, it contains most of the data the pizza order has but also it calculates the price of each order so you don't have to do it manually or save the amount in an extra field on your database table.

The Service Class

Now we've got to the interesting part, how the service layer takes advantage of these DTOs to make your life easier. The following is the declaration of the PizzaOrderService, I'll show separately the implementation methods so I can give a little more explanation of each without having you to scroll up and down repeatedly :).

@Service
@Transactional
class PizzaOrderServiceImpl implements PizzaOrderService, Serializable {

    private static final long serialVersionUID = 1L;
    @Autowired
    private HibernateTemplate hibernate;
    @Autowired
    private DTOBinder binder;
    @Autowired
    private Validator validator;
    
    ... //implementation goes here
}


This is a normal service class, it is transactional because it interacts with hibernate, it has injected an instance of the very helpful class HibernateTemplate, also has injected an instance of the DTOBinder (which is configured as a spring bean in applicationContext.xml), finally the JSR 303 standard bean validator to perform validations on objects (also declared as a spring bean).

This class is serializable because it may be serialized for example in a web session.

    @Override
    public List<PizzaDTO> listAvailablePizzas() {

        DetachedCriteria criteria = DetachedCriteria.forClass(Pizza.class);
        criteria.addOrder(Order.asc("pizzaName"));

        return binder.bindFromBusinessObjectList(PizzaDTO.class, hibernate.findByCriteria(criteria));
    }

This method queries the database for pizzas, and returns the list as a list of PizzaDTO by taking advantage of the DTO Binder. Please note that if we hadn't used the DTO framework, we would have had to iterate over the list returned by hibernate template and build the DTO instances ourselves by setting and formatting the values; all is taken care by the framework.

    @Override
    public boolean placeOrder(PizzaOrderForm form) {

        //may validate with hibernate validator
        if (!validator.validate(form).isEmpty()) {
            return false;
        }

        PizzaOrder order = binder.extractFromDto(PizzaOrder.class, form);


        order.setOrderDate(new Date());
        order.setPizzas(new ArrayList<Pizza>());

        for (PizzaDTO pizzaDTO : form.getPizzas()) {
            Pizza pizza = hibernate.get(Pizza.class, pizzaDTO.getPizzaId());
            order.getPizzas().add(pizza);
        }

        hibernate.save(order);

        return true;
    }

This method may look familiar to you if you had to build functionality for adding data to a database in the past :). In this case we get a PizzaOrderForm as a parameter, use the validation framework to check the data, extract the form values into a proper business object using de DTO framework, set data that was not inputted, relate the order with the actual pizzas and finally persist the order!

That's a lot for the few lines of code we've written! If we hadn't had use the DTO framework, then we would have had to manually copy each property of the form into the appropriate property of the business object, which can take several more lines.

    @Override
    public List<OrderDTO> listOrders() {
        DetachedCriteria criteria = DetachedCriteria.forClass(PizzaOrder.class);
        criteria.addOrder(Order.desc("orderDate"));

        return binder.bindFromBusinessObjectList(OrderDTO.class, hibernate.findByCriteria(criteria));
    }

The last method is much the same as the first one so no further discussion is needed.

A couple of final words.

As we may have noticed, this architecture is rather simple but full featured. It uses data validation, transactional behavior and the code is kept really small and clean thanks to the DTO Binding framework.

When used the right way, DTOs are a very powerful tool and makes our application even more bullet-proof by isolating completely the database code (persistent objects included) from the presentation tier.

Another thing to note is that, if you need to change from where data is acquired, add or remove fields is just a matter of configuring the DTO object and nothing else! Compare this to the traditional approach in which you would have added the extra field and then going through each loop when you populated the DTO and add the extra data copy operation.

1 comment:

  1. Hi Can you help me in question?
    http://stackoverflow.com/questions/16852820/java-server-side-form-validation-using-dto-hash-map
    I will be very thankful.

    ReplyDelete