Fun with Generics – From Repository to DTO

December 5th, 2007

Oren posted a pattern for converting Domain entities to DTO’s using a delegate. He made a comment in his post:  “I did try to make it into a framework, but even I can’t make it more complex than this.”

I started laughing to myself because I actually tried something similar a few weeks ago, and was going to post, but I didn’t cuz I’m fairly new to this blog stuff and I wussed out… Time to be bold I suppose.

The project I am working on now has a large Service Layer on top of Repositories. You can only write so many services with translation before you start to see the commonalities and look for a way to abstract and reuse. (framework)

So, I thought I’d geek out and get carried away with some Fowler P of EEA. Basically, what I wanted to do is *try* and put some standardization to a Service Layer,  which returns DTO’s, which are assembled from a Domain Model, which are accessed from Repository. So DTO <-> Service Layer <-> Assembler <-> Repository <-> Domain Model.  Sweet. I am going to use the classic Fowler Album and Artist.

 

Abstractions

The Repository interface:

public interface IRepository<TDomainEntity>
{
   IEnumerable<TDomainEntity> Find(Func<TDomainEntity, bool> filter);
   TDomainEntity GetById(long Id);
   void Add(TDomainEntity domainEntity);
   void Delete(TDomainEntity domainEntity);
   void Update(TDomainEntity domainEntity);
}

The abstract Assembler:

public abstract class Assembler<TDto, TDomainEntity>
{
   public abstract TDomainEntity DtoToDomainEntity(TDto dto);
   public abstract TDto DomainEntityToDto(TDomainEntity domainEntity);
 
   public List<TDto> DomainEntitiesToDtos(IEnumerable<TDomainEntity> domainEntityList)
   {
       List<TDto> dtos = Activator.CreateInstance<List<TDto>>();
       foreach (TDomainEntity domainEntity in domainEntityList)
       {
           dtos.Add(DomainEntityToDto(domainEntity));
       }
       return dtos;
   }
 
   public List<TDomainEntity> DtosToDomainEntities(IEnumerable<TDto> dtoList)
   {
       List<TDomainEntity> domainEntities = Activator.CreateInstance<List<TDomainEntity>>();
       foreach (TDto dto in dtoList)
       {
           domainEntities.Add(DtoToDomainEntity(dto));
       }
       return domainEntities;
   }
}

And a base Service:

public class Service<TDto, TDomainEntity>
{
   Assembler<TDto, TDomainEntity> _assembler;
   IRepository<TDomainEntity> _repository;
 
   public Service(Assembler<TDto, TDomainEntity> assembler,
       IRepository<TDomainEntity> repository)
   {
       _assembler = assembler;
       _repository = repository;
   }
 
   public Assembler<TDto, TDomainEntity> Assembler { get { return _assembler; } }
   public IRepository<TDomainEntity> Repository { get { return _repository; } }
 
   public virtual List<TDto> Find(Func<TDomainEntity, bool> filter)
   {
       return _assembler.DomainEntitiesToDtos(_repository.Find(filter));
   }
 
   public virtual TDto GetById(long Id)
   {
       return _assembler.DomainEntityToDto(_repository.GetById(Id));
   }
 
   public virtual void Add(TDto dto)
   {
       _repository.Add(_assembler.DtoToDomainEntity(dto));
   }
 
   public virtual void Update(TDto dto)
   {
       _repository.Update(_assembler.DtoToDomainEntity(dto));
   }
 
   public virtual void Delete(TDto dto)
   {
       _repository.Delete(_assembler.DtoToDomainEntity(dto));
   }
}

Implementations

Now that I have some structure down, I think hammering out the implementations should go fast. I have more or less templated how the objects should interact.

The Domain Entities:

public class Artist
{
   public string Name { get; set; }
}
 
public class Album
{
   public long Id { get; set; }
   public string Title { get; set; }
   public Artist Artist { get; set; }
}

The DTO:

public class AlbumDto
{
   public long Id { get; set; }
   public string ArtistName { get; set; }
   public string Title { get; set; }
}

The Repository: (With some hacked data in a List)

public class AlbumRepository : IRepository<Album>
{
   #region IRepository<Album> Members
   List<Album> _albums = new List<Album>
   {
       new Album {Id=1, Title="Rock Out", Artist=new Artist{Name="Rock Dudes"}},
       new Album {Id=2, Title="Rock On", Artist=new Artist{Name="Rock Dudes"}},
       new Album {Id=3, Title="Yee haw", Artist=new Artist{Name="The Hicks"}}
   };
 
   public IEnumerable<Album> Find(Func<Album, bool> filter)
   {
       return _albums.Where(filter);
   }
 
   public Album GetById(long Id)
   {
       return _albums.Find(a => a.Id == Id);
   }
 
   public void Add(Album domainEntity)
   {
       _albums.Add(domainEntity);
   }
 
   public void Delete(Album domainEntity)
   {
       _albums.Remove(domainEntity);
   }
 
   public void Update(Album domainEntity)
   {
       Album album = _albums.Find(a => a.Id == domainEntity.Id);
       _albums.Remove(album);
       _albums.Add(domainEntity);
   }
 
   #endregion
}
The trusty Assembler:
public class AlbumAssembler : Assembler<AlbumDto, Album>
{
   public override AlbumDto DomainEntityToDto(Album domainEntity)
   {
       return new AlbumDto
       {
           Id = domainEntity.Id,
           ArtistName = domainEntity.Artist.Name,
           Title = domainEntity.Title
       };
   }
 
   public override Album DtoToDomainEntity(AlbumDto dto)
   {
       Album album = new Album { Id = dto.Id, Title = dto.Title };
       album.Artist = new Artist { Name = dto.ArtistName };
 
       return album;
   }
}

And of course the Service: (Super easy now)

public class AlbumService : Service<AlbumDto, Album>
{
   public AlbumService()
       : base(new AlbumAssembler(), new AlbumRepository()) { }
 
   public AlbumDto FindByAlbumTitle(string title)
   {
       return Find(a => a.Title == title).First<AlbumDto>();
   }
}

I went ahead and added another method using Find, but its nice to see all the standard interactions already there. If I need to extend the Service I can as all the methods defined are virtual and the Repository and Assembler are exposed as properties:

image

 

So there it is. I am not sure how I quite feel about it yet. I have a side project going and I think Ill try it out and see how it goes. It was certainly fun to do, and I’m definitely interested in your thoughts.

Though I posted all the code, you can find it here if your interested.

 

 

 

  • http://thecodemonkey.net Joe K

    How about in your abstract assembler doing the following?

    public IEnumerable DomainEntitiesToDtos(IEnumerable domainEntityList)
    {
    foreach (TDomainEntity domainEntity in domainEntityList)
    {
    yield return DomainEntityToDto(domainEntity);
    }
    }

    public IEnumerable DtosToDomainEntities(IEnumerable dtoList)
    {
    foreach (TDto dto in dtoList)
    {
    yield return DtoToDomainEntity(dto);
    }
    }

  • http://www.elegantcode.com Jarod Ferguson

    I dig it. Much cleaner. I was initially thinking I would want list functionality returned from the assembler to the service, but that is really not a concern of the assembler. Could always do a new List in the service if ever needed (actually cant think of a good reason), defer it to the latest possible moment.

    Thanks for the suggestion!

  • Pingback: 8 Links Today (2007-12-12)

  • Pingback: Elegant Code » DTO’s or Serialized Domain Entities?

  • Pingback: links for 2008-04-28 « godiane

  • http://gschuager.blogspot.com Germán Schuager

    Good stuff.
    Thank you for sharing.