Someth Victory

Ruby, Rails, and Javascript Developer

Data Transfer Object in Ruby

A Data Transfer Object(DTO) is an object which is used to encapsulate data. It is commonly used in the Services layer which request data from third party API, or from the system itself. The benefit of DTOs is to convert the raw data in an object and reduce unnecessary information. It also makes a great model in MVC. Moreover, DTO makes the code very easy to maintain and test.

Given that we are writing some code to perform domain check from thrid party API.

Let’s see the example below:

Request data to an API
1
result = API::Domain.check('some-domain.com')

The response is looked like this:

Response result from an API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"response": {
  "status": {
    "code": 200,
    "message": "OK"
  },
  "headers": {
    "Date": {
      "Fri, 20 Jun 2014 02:41:57 GMT"
    },
    "Content-Type": {
      "text/json"
    }
  },
  "body": {
    "type": "domain",
    "name": "some-domain.com",
    "price": "11.00",
    "status": "Available"
  }
}

In order to access the attributes of the response, it will requires to go through all the hierarchy key structure:

Access field name of the response
1
2
3
4
5
result = JSON.parse(result)
result['response']['body']['name']
# => 'some-domain.com'
result['response']['body']['type']
# => 'domain'

Let’s say that we want to check whether or not the domain is available(domain is available only if its status is ‘Available’ and price is less than 15)

The code will look like this:

1
2
result['response']['body']['status'] == 'Available' &&
result['response']['body']['price'].to_f < 15

As you can see, the code above is not really efficient at all, in case of readability and scalability.

In addition, what would happen when some fields in the hierarchy structure needs to be changed? Let’s say field result['response']['body'] changed to result['response']['data']. As a result, the code above will be no longer work, so, the code need to be changed everywhere the result object is called.

Perform with DTO

First, create a new class called DTO::Domain. This class is responsible for translating the data returned from the API into an object.

DTO::Domain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DTO::Domain
  attr_reader :type, :name, :price, :status

  def initialize(data)
    @type   = data['body']['type']
    @name   = data['body']['name']
    @price  = data['body']['price']
    @status = data['body']['status']
  end

  def available?
    status == 'Available' && price < 15
  end

  def taken?
    status == 'Taken'
  end
end

With DTO::Domain, we could instantiate a new object by passing the data responded from the API.

1
2
result = JSON.parse(result)
domain = DTO::Domain.new(result['response'])

All the information responded form the API is encapsulated inside a DTO::Domain class. Only needed information with some additional domain logic is included in the object. In some case that the field in the API is changed, only DTO::Domain class alone needs to be changed.

1
2
3
4
5
6
7
8
domain.name
# => 'some-domain.com'
domain.taken?
# => false
domain.available?
# => true
domain.price
# => 11.0

The code now is much cleaner, maintainable, testable, and scalable.