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:

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:

[
{"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:

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:

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:

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:

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...

Top