Data Transfer Objects and Remote Facades

Data transfer objects (DTOs) are also called value objects. But now most people use the term "value object" to refer to stateless objects that represent values such as vectors and strings as opposed to reference objects that represent entities and events in some application domain.

DTOs are often used with remote facades, so I will treat them as a composite pattern. (It is possible to use one without the other, however.)

Problem

A package provides many short methods that provide clients with fine-grained control over business data and logic. However, if we want to access such a package remotely, we must make lots of remote method invocations and lots of data transfers. This can be very inefficient.

Solution

The Facade pattern is commonly used to turn the fine grained functionality of a package into coarse grained functionality. One motive for doing this is to simplify functionality for clients who don't need all of the functionality provided by the package.

A remote facade provides bulk access operations that allow remote clients to transfer large quantities of data in one method call.

For example, if a package contained Customer, Product, and  Invoice classes with lots of getters and setters for each attribute, then a remote facade would simply provide getter and setter methods for each class. But what would the getter methods return, and how many parameters would the setter methods require?

A data transfer object (DTO) is a collection of public fields. If there are any methods, they are constructors for quickly making the DTO. DTOs do NOT contain logic.

DTOs are often used with a form of data mapper called a DTO assembler.

A record set is a collection of DTOs. A record set is an in-memory representation of a n SQL query.

Example

 

domain package

class Customer {
   private String name, phone, address;
   public Customer(String nm, String ph, String addr) {
      name = nm;
      phone = ph;
      address = addr;
   }
   public String getName() { return name; }
   public String getPhone() { return phone; }
   public String getAddress() { return address; }
   public void setPhone(String ph) { phone = ph; }
   public void setAddress(String addr) { address = addr; }
   // business logic goes here
}

class Product {
   private String name;
   private double price;
   public Product(String nm, double pr) {
      name = nm;
      price = pr;
   }
   public String getName() { return name; }
   public double getPrice() { return price; }
   public void setPrice(double pr) { price = pr; }
   // business logic goes here
}

class Invoice {
   private Customer customer;
   private List<Product> cart;
   private static final double SALES_TAX = .07;
   public Invoice(Customer c) {
      customer = c;
      cart = new ArrayList<Product>();
   }
   public void add(Product p) { cart.add(p); }
   public Customer getCustomer() { return customer; }
   public List<Product> getCart() { return cart; }
   public double total() {
      double result = 0;
      for(Product p: cart) {
         result += p.getPrice();
      }
      result *= SALES_TAX;
      return result;
   }
   // more business logic goes here
}

// for demo purposes only:
class Domain {
   public static List<Customer> customers = new ArrayList<Customer>();
   public static List<Product> products = new ArrayList<Product>();
   public static List<Invoice> invoices = new ArrayList<Invoice>();
}

dto package

class CustomerDTO implements Serializable {
   public String name;
   public String phone;
   public String address;
}

class ProductDTO implements Serializable {
   public String name;
   public String price;
}

class InvoiceDTO implements Serializable {
   public CustomerDTO customer;
   public ProductDTO[] cart;
   public String salesTax;
}

package facade

The assemblers

class CustomerDTOAssembler {
   public static CustomerDTO makeCustomerDTO(Customer c) {
      CustomerDTO result = new CustomerDTO();
      result.name = c.getName();
      result.phone = c.getPhone();
      result.address = c.getAddress();
      return result;
   }

   public static void updateCustomer(CustomerDTO dto) {
      Customer target = null;
      for(Customer c: Domain.customers) {
         if (dto.name.equals(c.getName())) {
            target = c;
            break;
         }
      }
      if (target != null) {
         target.setAddress(dto.address);
         target.setPhone(dto.phone);
      }
   }

   public void makeCustomer(CustomerDTO dto) {
      Customer result = new Customer(dto.name, dto.phone, dto.address);
      Domain.customers.add(result);
   }
}

class ProductDTOAssembler {
   public static ProductDTO makeProduct(Product p) {
      ProductDTO result = new ProductDTO();
      result.name = p.getName();
      result.price = "" + p.getPrice();
      return result;
   }
   public static void updateProduct(ProductDTO dto) {
      Product target = null;
      for(Product p: Domain.products) {
         if (p.getName().equals(dto.name)) {
            target = p;
            break;
         }
      }
      if (target != null) {
         target.setPrice(Double.parseDouble(dto.price));
      }
   }
   public static void makeProduct(ProductDTO dto) {
      Product p = new Product(dto.name, 0);
      p.setPrice(Double.parseDouble(dto.price));
      Domain.products.add(p);
   }
}

class InvoiceDTOAssembler {
   public InvoiceDTO makeInvoiceDTO(Invoice inv) {
      InvoiceDTO result = new InvoiceDTO();
      result.customer =
         CustomerDTOAssembler.makeCustomerDTO(inv.getCustomer());
      result.cart = inv.getCart().toArray();
   }

   public void updateInvoice(InvoiceDTO dto) {

   }
   public void makeInvoice(InvoiceDTO dto) {

   }
}

The Facade

public class RemoteFacade {

   public void makeCustomer(CustomerDTO dto) {
      CustomerDTOAssembler.makeCustomer(dto);
   }
   public void makeProduct(ProductDTO dto) {
      ProductDTOAssembler.makeProduct(dto);
   }
   // etc.
}