Three Examples of DTOs in Ruby mhz Follow Apr 17 · 3 min read

Objects transferring stuff! Like… Maybe… Data?

As our application became more complex, we needed to expand beyond the default MVC model that Rails offers out of the box. Existing software architecture patterns are a great resource that would help in identifying the best strategy for the need. One of such patterns is called the DTO, or Data Transfer Object.

In this short excerpt I will be summarizing what a DTO is, as well as providing 3 examples of the DTO pattern written in Ruby. The reference to these notes are from Martin Fowler’s Patterns of Enterprise Application Architecture.

Data Transfer Object (DTO)

In summary, it is an object whose sole purpose is to haul data. It’s main benefit is the decoupling of data transfer from server, client, and domain logic. This is great if the amount of data transferred is large, hierarchical, or difficult to manage.

A few points to note about DTOs:

Should be composed of primitive attributes, and/or other DTOs

DTO hierarchy should be simple (if applicable)

Is responsible for serialization and de-serialization of itself (If applicable).(JSON, XML, binary, etc)

Can be mutable — putting in data gradually

Request and Response (to, from server) can use same DTO, or have different DTOs. It depends.

Use an “Assembler object” to create a DTO from the domain model, and to update the domain model using the DTO.

DTO should be easily generated with simple data description.

Server and Client should use the same DTO to avoid diversion.

Method 1: Simple Immutable Object

Method 2: Hierarchical

Caveat [+Edit]

Simple hierarchies are preferred over deeply nested ones. It becomes much easier to construct your DTO if its shallow, and much easier to read it by the receiver.

In Ruby, there are nested classes, but in other languages there may not be. To put the classes above in the same namespaces without nesting classes, modules can be used instead.

Method 3: Mutable

Closing Remarks

Rails

Seems like we can’t talk about Ruby without talking about Rails. When using Rails, ActiveModel::Model can be included in your DTO to leverage existing functionality.

Up-side — things like validations, attribute assignments, potentially some very rudimentary type-checking and such without additional code.

Down-side — “Rails Magic” that makes it harder to read, more coupling to Rails, and functionality bloat you don’t need.

Type checking

It can be noted that on the Ruby examples above, there is no type-checking of variables. According to the rules listed in the book, the attributes should all be primitives, so consequently depending on the language, that may come out-of-the-box. Ruby, being a dynamic language, purposely forgoes that requirement. In addition, the messages attribute in my examples above is an Array , which is not a primitive… Rules were meant to be broken.

[+Edit] This is also one of the reasons why DTOs are preferred instead of Maps. Although in Ruby neither Maps nor DTOs are type checked, in static languages such as Go, Maps require no type checking while DTOs do, making it a safer technique to transfer data between services.

Serialization / de-serialization

Serialization/de-serialization methods were not included because I didn’t see a need to illustrate this in the example. But here a briefly some simple solutions for both:

Serialization — would be an instance method called to_json that deconstructs the object tree into a hash and returns a JSON format.

that deconstructs the object tree into a hash and returns a JSON format. De-serialization — would be a class method called from_json that would construct the DTO based on the JSON input.

[+Edit] The serialized format of the DTO doesn’t need to have the same structure as the object hierarchy of the DTO. It can be deeply nested if needed.

I’m not sure how applicable either would be in reality.