Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can DTO's have nested DTO's?

I have the following domain model:

public class Playlist
{
    public long Id { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Song> Songs { get; set; }
}

public class Song
{
    public long Id { get; set; }
    public string Name { get; set; }
    public virtual Playlist Playlist { get; set; }
    public virtual ICollection<Catalog> Matches { get; set; }
}

public class Catalog
{
    public long Id { get; set; }
    public string Title { get; set; }
}

My service has the following code:

public PlaylistResult FindByPlaylistId(long id)
{
    Playlist playlist = playlistRepository.GetById(id);

    foreach (var song in playlist.Songs)
    {
        song.Matches = catalogRepository.GetMatches(song.Name).ToList();
    }

    return new PlaylistResult(new PlaylistDTO(playlist), playlist.Songs.Select(x => new SongDTO(x)));
}

My service gets a playlist and songs from the database and then for each song in the playlist it fires a query to get additional matches from the database (using SQL Server full text search) specific to that song.

The data is then converted into DTO's, added to a result object and passed back to the controller. The code looks like:

public class PlaylistResult
{
    public PlaylistResult(PlaylistDTO playlist, IEnumerable<SongDTO> songs)
    {
        Playlist = playlist;
        Songs = songs;
    }

    public PlaylistDTO Playlist { get; private set; }

    public IEnumerable<SongDTO> Songs { get; private set; }
}

The problem:

The PlaylistResult object has worked great so far but the recent introduction of matches has made things a bit more complicated. It looks like I have no other choice than to modify my SongDTO to take into account matches and look like this:

public class SongDTO
{
    public SongDTO(Song song, IEnumerable<CatalogDTO> matches)
    {
        Id = song.Id;
        Name = song.Name;
        Matches = matches;
    }

    public long Id { get; private set; }

    public string Name { get; private set; }

    public IEnumerable<CatalogDTO> Matches { get; private set; }
}

But doesn't this violate the purpose of DTO's? It is my understanding that DTOs are a flattened representation of data and this approach is not flattened. On the other hand, I don't see how else to do this since each match is specific to each song.

I am aware that I could make this easier on myself and throw out the DTO's and pass the domain model to the controller directly and call it a day. But I don't want to do that since the whole purpose is to learn how to work with DTO's.

Any input is greatly appreciated.

like image 235
Thomas Avatar asked Jun 29 '11 16:06

Thomas


Video Answer


2 Answers

DTO's are not a flattened representation of data although they can be.

That's the beauty of them - you can structure them as you need to, as opposed to how the database defines things. Also they are a means of separating out data from behaviour.

I wouldn't put a reference to the Domain object within the DTO at all. (You have it in the constructor) Use a factory to build the DTO's so your clients only need reference the DTO's and not the Domain objects.

 Song mySong;
 SongDTO = DTOFactory.GetSong(mySong);

If your clients need to reference the Domain objects, then they may as well use them!

like image 99
BonyT Avatar answered Sep 22 '22 02:09

BonyT


What you have done is correct. I think that they are essentially flattened representation when you are passing data to/from the data layer of your application.

like image 26
Dhruv Avatar answered Sep 24 '22 02:09

Dhruv