Data Transfer Object em Ruby

18 Jun 2019 - Raul Fernando

Data Transfer Object é um design pattern usado para encapsular dados em um objeto. O benefício dos DTOs é converter os dados em um objeto e reduzir informações desnecessárias. Além disso o DTO torna o código muito fácil de manter e testar.

Supondo que estamos escrevendo um código para obter o endereço de um fornecedor.

Vejamos o exemplo abaixo:

response = Faraday.get('http://example-domain.com/provider')

A resposta é assim:

{
  "provider": {
    "name": "Fornecedor",
    "address": {
      "street": "Av. Paulista",
      "number": "1612",
      "complement": "-",
      "zipcode": "02460100",
      "district": "Centro",
      "city": "São Paulo",
      "state": "SP"
    }
  }
}

para acessarmos os attributos da resposta precisaremos fazer algo como

result = JSON.parse(response)
result['provider']['address']['street']
# => 'AV Paulista'
result['provider']['address']['city']
# => 'São Paulo'

Como você pode ver, o código acima não é realmente eficiente, em caso de legibilidade e escalabilidade. Além disso, o que aconteceria quando alguns campos na estrutura da hierarquia precisassem ser alterados?

Implementando o pattern de DTO

Primeiro, crie uma nova classe chamada DTO::Address. Essa classe é responsável por traduzir os dados retornados da API para um objeto.

class Address
  attr_reader :street, :number, :complement, :zipcode, :district, :city, :state

  def initialize(data)
    @street     = data['street'],
    @number     = data['number'],
    @complement = data['complement'],
    @zipcode    = data['zipcode'],
    @district   = data['district'],
    @city       = data['city'],
    @state      = data['state']
  end

  def full_address
    "#{street}, #{number} - #{district} - #{city} - #{state}, #{zipcode}"
  end
end

Com DTO::Address, poderíamos instanciar um novo objeto passando os dados respondidos da API.

result = JSON.parse(result)
address = DTO::Address.new(result['provider']['address'])

Todas as informações respondidas da API são encapsuladas dentro do DTO::Address. Apenas informações necessárias com alguma lógica de domínio adicional são incluídas no objeto por exemplo um metodo para retornar o endereço completo formatado do fornecedor.

address.full_address
# => 'Av. Paulista, 1612 - Centro - São Paulo - SP - 02460100'

O código agora é muito mais limpo, sustentável, testável e escalável.