Software Development

Veil Objects to Replace DTOs

Here is a new idea I discovered just a few days ago while working with Codexia, a Ruby web app. I had to fetch data rows from PostgreSQL and return objects to the client. It’s always been a problem for me, how to do that without turning objects into DTOs. Here is the solution I found and gave a name: Veil Objects.

Constantine (2005) by Francis Lawrence

Let’s say I fetch the list of projects from PostgreSQL:

1
2
3
4
5
class Projects
  def fetch
    @pgsql.exec('SELECT * FROM project')
  end
end

The method exec() on @pgsql (I’m using the pgtk gem) returns an array of Hashes, which look like this, if we convert them to JSON:

1
2
3
4
5
[
  {"id": 1, "name": "foo", "author": "yegor256"},
  {"id": 2, "name": "bar", "author": "yegor256"},
  {"id": 3, "name": "zoo", "author": "yegor256"}
]

It would be great to make the method fetch() return an array of objects, not an array of Hashes. So my class Project looks like this:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
class Project
  def initialize(pgsql, id)
    @pgsql = pgsql
    @id = id
  end
  def name
    pgsql.exec(
      'SELECT name FROM project WHERE id=$1',
      [@id]
    )[0]['name']
  end
  def author
    pgsql.exec(
      'SELECT author FROM project WHERE id=$1',
      [@id]
    )[0]['author']
  end
end

It’s perfectly designed for single-project manipulations:

1
2
3
p = Project.new(pgsql, 123)
name = p.name
author = p.author

Two SQL requests here is not a big deal. However, if I convert the list of Hashes to Projects like this, I will have serious performance problems:

1
2
3
4
5
6
7
class Projects
  def fetch
    @pgsql.exec('SELECT * FROM project').map do |r|
      Project.new(@pgsql, r['id'].to_i)
    end
  end
end

This is what will kill me, performance-wise:

1
2
3
projects.fetch do |p|
  puts "#{p.name} is created by #{p.author}"
end

This code will generate too many redundant SQL requests. We will do round-trips to PostgreSQL to fetch the data we had a few milliseconds ago, while we were doing SELECT * FROM project.

The easiest and the most obvious solution, which many of you might suggest, is to encapsulate the retrieved Hash into the Project object. In other words, turn Project into a DTO, a holder of data. Well, in this case we might not even need an object, but can instead return the Hash with the data. But this is not how we want our object-oriented software to be designed. We want to deal with objects, not data structures. And, at the same time, we don’t want objects to be stupid enough to go back to the database for the same data we had a second ago. Here is the solution I’m proposing:

01
02
03
04
05
06
07
08
09
10
11
12
require 'veils'
class Projects
  def fetch
    @pgsql.exec('SELECT * FROM project').map do |r|
      Veil.new(
        Project.new(@pgsql, r['id'].to_i),
        name: r['name'],
        author: r['author']
      )
    end
  end
end

This new Veil object is a decorator of Project. It behaves like a Project, but some of the methods on it are redefined: name() and author(). When they are called, the calls won’t reach the encapsulated Project. Instead, the data stored in the Veil will be returned.

It is called a “veil” because it acts like one: the preset data is returned only until some other method is called, which was not preset. If this happens, the veil is pierced and the Veil object becomes fully transparent, sending all method calls through.

Thus the efficiency of DTO is combined with the elegance of OOP.

I’m using these new veil objects in yegor256/codexia, so you can see how they work.

P.S. I also create an Unpiercable class, which acts exactly like a Veil, but can never be pierced. It is very useful, when you don’t expect any data-modifying interactions to happen with the object and just want some of its methods to be pre-calculated.

Published on Java Code Geeks with permission by Yegor Bugayenko, partner at our JCG program. See the original article here: Veil Objects to Replace DTOs

Opinions expressed by Java Code Geeks contributors are their own.

Yegor Bugayenko

Yegor Bugayenko is an Oracle certified Java architect, CEO of Zerocracy, author of Elegant Objects book series about object-oriented programing, lead architect and founder of Cactoos, Takes, Rultor and Jcabi, and a big fan of test automation.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
nagendra.raja
nagendra.raja
3 years ago

Project.

new

(

@pgsql

, r[

'id'

].to_i),
        

name: r[

'name'

],
        

author: r[

'author'

]
 
What is the Project has many many to one and one to many relationships.
If you can auto generate DTO of veil object automatically, that would be the best, wish all the JPA generate DTO object properly.
 
Basically In your code above, name: r[

'name'

],
        

author: r[

'author']
 
this code has to be come redundant to see the true power.
 

Back to top button