Data Retrieval Patterns: The Data Transfer Object (DTO) Pattern
One of the foremost drives of developers and architects for the last 3 decades has been the divide-and-conquer concept of isolationism - 'black-box development', as we coined it in the 80's.
These days, we use objects and messages. A DTO or Data Transfer Object (read also, DAO or Data Access Object) is pure data used to pass cohesive information between functionally separate parts of the system.
Unlike the rest of the software development world where we strive to reuse code, it is considered bad form to use a DTO in more than one transfer. For example, a request for some database data may involve a DTO between the persistence and service tiers and a second between service and GUI. As you can imagine, this methodology causes more transfer of data between objects, but it goes a long way to providing a level of isolation that would otherwise be impossible.
Since the DTO used by the persistence layer is never passed to the GUI it can't accidentally be changed and written back by the GUI. The down-side is a lot of deep copying of data. This is not as annoying as it sounds (touch wood!), since each black-box usually needs a different view. The persistence layer can have differed DTOs for different tables. The service layer may combine, extract and apply business rules to that data before sending a result back to the GUI as a different type of DTO.
Because a DTO has a limited life as a message between two well-known components, continued consistency of its contents can be the responsibility of the receiver. For this reason any of the earlier data access patterns can be used for the DTO, including the Public Data Access Pattern.
class MyFirstDTO
{
public int integer;
public String string;
}
Strengths
- Provides true isolation between modules/tiers/objects/systems.
- Used correctly it will present the requester with only the information they require.
- Information can be provided using names clearly readable in the context in which they are used. Often an item named for what it is in one location is better known for why elsewhere. Thus, a persistence tier may name a column "upper_case_description", while the service tier could name it by preference, for example "descriptionToSearchOn".
- Minimises the need for empty fields. If the DAO has an entry you should be able to assume you have the data. Use the class container method below to minimise source files.
Weaknesses
- Source File Explosion: Many DTOs means many objects means a full source tree, but use the class containers technique below to reduce the source file count.
- Your data gets copied like a bag full of rabbits. However, all this data copying is not usually time-consuming as DTOs hold mostly immutable data - such as Strings where the copy can be just a pointer.
- Code bloat: the server side of a client-server object pair can end up with a lot of code, even just loading DTO messages to be sent to the client - often just a transfer from another DTO after communicating with another tier or object. This is unfortunately a necessary part of a DAO architecture model. So don't clutter business logic with DAO transfer code, but place it in a separate transfer object or method. It's even acceptable with a consolidator DAO to provide it with DAOs from another tier in the constructor, to allow it to populate itself.
class ConsolidatorDAO { public ConsolidatorDAO( FirstDAO firstDAO, SecondDAO secondDAO) { integer = firstDAO.integer; string = secondDAO.string; } public int integer; public String string; }
Uses
- SOE (Service Oriented Architecture): Where the GUI layer calls a service layer for all business logic. The service layer will also in it's turn call a persistence layer for database and other data storage or retrieval mechanisms.
Hints - Class Containers
One of the problems of using DTO to transfer data between components is the proliferation of files in the source tree. The temptation is to use a common DTO with only some fields populated, which is bad practise at the best of times. Where possible, variations should have their own DAO objects fully populated and ready for use, but to reduce the source tree and keep those common DTO types together, use the class container pattern.
public class CarDAO
{
int engineCapacity;
String colour;
String manufacturer;
String model;
int year;
public static class Sports extends Car
{
String roofType;
String suspensionType;
}
public static class OffRoad extends Car
{
boolean constant4WD;
int clearanceInCM;
boolean snorkel;
}
}
//...
CarDAO.Sports sportsCar = new CarDAO.Sports();
CarDAO.OffRoad fourWheelDrive = new CarDAO.OffRoad();
CarDAO oldCar = new CarDAO();
Here we have a transfer object for Car information. If we are dealing with a sports car we can create and read with CarDTO.Sports - and all the common Data Transfer Objects are in a single file.








0 Comments:
Post a Comment
<< Home