One of the reasons I think this discussion comes up repeatedly is because it seems like a serious pain-in-the-ass to take an object with all the data you need and convert it to an object that looks identical or nearly identical to the one you are handing off.
It's true, it's a PITA. But there are a few reasons (besides those enumerated above) for doing so.
- Domain objects can get very heavy and contain a lot of useless information for the call. This bloat slows down the UI because of all the data transmitted, marshalled/unmarshalled and parsed. When you consider a FE will have numerous links referring to your webservices and being called with AJAX or some other multi-thread approach you will quickly make your UI sluggish. All this gets to the general scalability of webservices
- Security can easily be compromised by exposing too much data. At a minimum you could expose email addresses and phone numbers of users if you don't eliminate them from the the DTO result.
- Practical considerations: For 1 object to parade as a persisted domain object AND a DTO it would have to have more annotations than code. You will have any number of issues with managing the state of the object as it passes through layers. In general this is be much more of a PITA to manage then simply doing the tedium of copying fields from a domain object to a DTO.
But, you can manage it fairly effectively if you encapsulate the translation logic into a collection of converter classes
Have a look at lambdaJ where you can do 'convert(domainObj,toDto)' there is an overload of this for use with collections. Here is an example of a controller method that makes use of it. As you can see, it doesn't look so bad.
@GET
@Path("/{id}/surveys")
public RestaurantSurveys getSurveys(@PathParam("id") Restaurant restaurant, @QueryParam("from") DateTime from, @QueryParam("to") DateTime to) {
checkDateRange(from, to);
MultiValueMap<Survey, SurveySchedule> surveysToSchedules = getSurveyScheduling(restaurant, from, to);
Collection<RestaurantSurveyDto> surveyDtos = convert(surveysToSchedules.entrySet(), SurveyToRestaurantSurveyDto.getInstance());
return new RestaurantSurveys(restaurant.getId(), from, to, surveyDtos);
}