Can DTO's have nested DTO's?
D

2

18

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.

Danford answered 29/6, 2011 at 16:35 Comment(2)
Be aware that you might be creating an anemic domain model. You're domain objects don't seem to expose any behaviour - it's just public getters and setters (which is dangerous).Provincial
I kept it simple for the sake of the example.Danford
L
17

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!

Linin answered 29/6, 2011 at 16:44 Comment(2)
Thanks. So in other words, nesting DTO's is an acceptable approach?Danford
Absolutely - just don't mix and match DTO's with Domain Objects!Linin
I
2

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.

Inedited answered 30/6, 2011 at 9:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.