Data transfer objects: you either hate them or you loathe them. They give off that kind of smell that sets the alarm bells ringing and has you reaching for the disinfectant. DRY? Sorry, not today m’am. Let’s face it, anything that has you duplicating fields and performing straight copies between objects is deeply suspicious.
Despite that, DTOs still persist (pardon the pun). When you want to serialise data over RPC, they’re often one of the few options available to you. GWT-RPC is a case in point, and the reason for the Grails DTO plugin. Gilead allows you to transparently serialise Hibernate domain instances, but this only works if the domain class can be loaded by the client. Since GORM domain classes are typically Groovy, that’s not an option with GWT. Your typical Grails domain class also includes a bunch of stuff that the client is hardly going to be interested in, like the custom mappings. So we need a set of Java classes that the client can access – in other words, DTOs.
So what are the main issues with DTOs? First of all, you have to write them. In fact, you pretty much end up duplicating your domain classes! There’s little that’s more depressing. Second, you have to write code to copy the data from your domain instances to DTO instances. When done manually, this can be particularly error-prone as well as laborious.
What if we can mitigate those issues? That’s what the DTO plugin is for. It provides two features that can eliminate the labour involved with creating and maintaining DTOs. Let’s look at the first one.
Creating DTO classes
Your average DTO class is a copy of its corresponding domain class, minus all the mapping information and non-transient data. So the plugin provides a command that does this for you:
The above will not only create a MyDomainDTO class for you that has the same persistent fields as MyDomain, but it will also create DTO classes for all related classes, such as those declared via hasMany or belongsTo. In fact, if you’re not careful you can end up with DTOs for your whole domain model!
grails generate-dto org.example.MyDomain
Alternatively, if that’s what you want (DTOs for your whole domain model), then you can use
More control can be exercised with the --non-recursive option, which disables the default behaviour and means that the command will only create DTOs for the named domain classes. In other words, it doesn’t follow the relations.
grails generate-dto --all
Of course, once you have generated the DTO classes, you don’t have to stop there. If you want to limit the amount of information that will be transferred to a client, simply edit the classes and remove any fields or relationships you want. Or replace relations with a different field. For example, you may not want to return a whole user object associated with a blog post, but you might want to send the user ID or the username.
Generating the DTO classes is only half the story, though. You have to actually convert the domain instances returned by queries and the like into DTO instances.
Converting domain instances to DTOs
It’s possible to do the conversion manually if you want. You could even use Apache Commons or Spring to copy the fields from one object to another. But why? The plugin makes it easy to do the conversion by applying some Groovy magic. Given a domain instance, simply use one of the following options:
def post = Post.get(somePostId) def dto = post as DTO // or dto = post.toDTO()
The first option, as DTO, fits in nicely with the look and feel of Grails converters, but you do have to remember to import the grails.plugins.dto.DTO class. The second option doesn’t require the import, but doesn’t look quite as funky either.
With the soon-to-be-released version 0.1.2 of the plugin, you can also serialise collections and maps of domain classes:
def posts = Post.findAllByCategory("music") def dto = posts as DTO
That’s all there is to it. However, one thing to bear in mind is that things get more complicated if you edit the DTO classes. In such cases, you probably need to provide some custom mappings to ensure that the data is copied correctly. I’ve not tried it myself, but since the domain instance -> DTO mapping is done by Dozer, you should be able to provide a Dozer mapping file. Once thing I would like to add in future is easy custom mappings using Groovy instead of the Dozer XML files. ‘Til then, it’s XML I’m afraid.
For those people who find themselves in need of DTOs for whatever reason, I hope this plugin makes your lives just a bit easier. Happy coding!