addressalign-toparrow-leftarrow-leftarrow-right-10x10arrow-rightbackbellblockcalendarcameraccwcheckchevron-downchevron-leftchevron-rightchevron-small-downchevron-small-leftchevron-small-rightchevron-small-upchevron-upcircle-with-checkcircle-with-crosscircle-with-pluscontroller-playcredit-cardcrossdots-three-verticaleditemptyheartexporteye-with-lineeyefacebookfolderfullheartglobe--smallglobegmailgooglegroupshelp-with-circleimageimagesinstagramFill 1languagelaunch-new-window--smalllight-bulblightning-boltlinklocation-pinlockm-swarmSearchmailmediummessagesminusmobilemoremuplabelShape 3 + Rectangle 1ShapeoutlookpersonJoin Group on CardStartprice-ribbonprintShapeShapeShapeShapeImported LayersImported LayersImported Layersshieldstar-shapestartickettrashtriangle-downtriangle-uptwitteruserwarningyahooyoutube

Re: [ljc] DTO as an anti-pattern?

From: Ged B.
Sent on: Tuesday, January 15, 2013, 12:42 PM
Hi Kevin,

I completely agree with you.  Everything you say about the DTO pattern and the way it is implemented in dynamic languages is exactly right.

The thing is that Java is not a dynamic language and this has consequences that affect the design. If you are writing in Java then these Java artefacts cannot be ignored.

This isn't about the concept, this is about how we go about translating the abstract concept into an concrete implementation.

If we look at the classic J2EE description of the Transfer Object we see there are consequences, both positive and negative: 
  • Simplifies Entity Bean and Remote Interface 
  • Transfers More Data in Fewer Remote Calls 
  • Reduces Network Traffic 
  • Reduces Code Duplication 
  • May Introduce Stale Transfer Objects 
  • May Increase Complexity due to Synchronization and Version Control 
  • Concurrent Access and Transactions 

If we consider one of the apparant benefits, we still see trade-offs that need to be considered.  consider for example the apparent benefit of "reduces code duplication":

By using the Entity Inherits Transfer Object Strategy and the Transfer Object Factory Strategy, it is possible to reduce or eliminate the duplication of code between the entity and its Transfer Object. However, with the use of Transfer Object Factory Strategy, there could be increased complexity in implementation. There is also a runtime cost associated with this strategy due to the use of dynamic reflection. In most cases, the Entity Inherits Transfer Object Strategy may be sufficient to meet the needs.

So when we are using patterns it isn't a simple case of "choose a pattern" then "implement a pattern."  Patterns don't tell us what to implement, they help us to understand the problem we are trying to solve and the forces at play in the potential solutions.  To quote the gang of four:

No discussion of how to use design patterns would be complete without a few words on how not to use them.  Design patterns should not be applied indiscriminately.  Often they achieve flexibility and variability by introducing additional levels of indirection, and that can complicate a design and/or cost you some performance. A design pattern should only be applied when the flexibility it affords is actually needed.  The Consequences sections are most helpful when evaluating a pattern's benefits and liabilities.

It's all about costs and benefits.  The language being used as a huge impact on costs.  In a dynamic language objects are cheap and reflection is easy.  In java objects are expensive and reflection is tricky.

What is an object in a dynamic language?  Often it is just a map pointing to the fields and methods.  In dynamic languages methods are just data like strings and integers so you can do that.

A DTO is an object with no methods.  In a dynamic method an object without behaviour is really just a map.  I'm sure things are more complicated in Scala.  Does it optimise away short lived objects to avoid generating unnecessary objects?  

What happens when we use reflect in Java?  You call getMethods to get an array of methods (or perhaps Spring's getPropertyDescriptors).  You then use the array to generate the serialised form.  You create a java class with just properties and compile it into a class file just so you can get the JVM to read the classfile and then populate an array.  That isn't a very efficient way to populate an array.

Patterns are never deprecated or harmful.  They are applicable in some circumstances and not applicable in others.  Over time the distribution of circumstances may change but the pattern remains constant.  

When choosing a pattern the question is not: is this a good or bad pattern?  

The question is: do my circumstance mean that the benefits of this pattern outweigh the costs.

Regards, 



Ged


On Monday, 14 January 2013, Kevin Wright wrote:
That's more an artefact of Java than it is of DTOs as a concept.  For example, the following comes straight from a codebase I'm working on:

object UserPageviewActivityInfo { implicit lazy val jsonFormat = jsonFormat14(this.apply) }

case class UserPageviewActivityInfo(
  timestamp: String,
  brandTitle: String,
  pageUrl: SplitUrlInfo,
  pageviews: Int,
  previousPageUrl: String,
  visitCount: Int,
  visitLength: Int,
  timeOnPage: Int,
  timeOnSite: Int,
  visits: Int,
  version: String,
  userid: String,
  authenticated: String,
  activity: String = "page view"
)

This isn't Java, it's Scala using spray-json, but that's beside the point.  What counts is that *this is an object* for the sole purpose of transferring data to a JavaScript client.  In the web app layer I simply call instance.toJson.compactPrint and send the output as the response body.  A case class is nothing more than a class in which property accessors, hashCode, equality, and toString are implemented for you by the compiler - making them perfect for exactly this kind of task.  If I mess up, the compiler will tell me.

This is every bit as clean as a Map, plus it's more type safe, decoupled from other logic, and you can even use assertions in the constructor or inherit from other types to add utility and remove duplication.  It also helps that there's now a obvious place to put factory methods for creating these things.  There's very little boilerplate, and no need for code generation or external files to specify interfaces.  I didn't mention Scala to plug it, but because it genuinely helps me focus on the essence of the concept.

Once you look past the fact that one particular implementation of DTOs in Java needs a lot of boilerplate, and understand that this isn't globally true, it makes a lot of sense.  Nor is this some special trick in Scala, you can do similar in JRuby, Groovy, or Clojure.  The same is even possible in raw Java using something like the Lombok library and a spot of reflection.


People in this
group are also in: