Question

Background:

I am writing a social network-esque web application using ASP.NET MVC. My project is layed out as follows:

  1. Presentation layer - Views and front-end framework. Data is housed in Viewmodels mapped from BOs.
  2. Business Layer - BO manipulation and aggregation for use on presentation layer as well as hydration of BOs from Data Layer.
  3. Data Layer - Repositories live here as well as code for retrieving data from db. POCOs are defined here.

Previously the project was using SQL and Dbcontext to hydrate BOs created from POCO classes defined in the Data Layer. However due to the nature of the project(as it has grown) requirements have outgrown the SQL based architecture for storing data. I made the decision to switch to Redis but am now having difficulty mapping between Redis and my POCO classes.

Root of the problem:

I have elected to use Service Stack's Redis Client for interacting with the Redis db. The client provides a Strongly-Typed Client that allows you to specify the object being store/retrieved from the server and serializes/deserializes it for you(which is wonderful). The problem with this though is that any property that has a public getter will be serialized along with the object being stored.

For me this defeats the point of using Redis as I do not want child objects to be stored in an aggregate manner -- I want them to be stored relationally so that each object is independent but related to some other object.

To do this I created two sets of objects:

Domain Objects(BOs)

public class Participant : Base
{
    public string ForwardConnections { get; set; }
    public string ReverseConnections { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
}

and DTOs

public class Participant : Base
{
    public AceOfSets<Connection> ForwardConnections { get; set; }
    public AceOfSets<Connection> ReverseConnections { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
 }

Base

public class Base
{
    public long Id { get; set; }
    public DateTimeOffset CreatedOn { get; set; }
    public string Key { get; set; }
}

Using this design I actually store DTOs in the Redis DB. Any property on a Domain Object that is an object is stored on its respective DTO as a string key. Every object(both DTO and DO) inherit from a Base class which has a Key property. This way I can store the relationships between object properties without aggregating and duplicating each object.

Where I'm running into trouble:

To move between DO and DTO I am using AutoMapper and a suite of custom ITypeConverter classes that map between string and whatever class has the same name on the respective DO property(and vice versa, class to string) where string is the key for the object to retrieve in the Redis DB. This should work well however I now have two sets of methods in my Data Layer:

  1. Basic operations for getting domain objects which reside in my repository -- these are the methods I want my Business Layer to interact with.
  2. Basic operations for getting DTOs which reside in a separate class and are only used to access the Redis db through the client.

I want these two sets of operations to be mapped so that operating on a DO from the repository performs the necessary operation on the DTO after transferring data between the two.

In essence I want repositories to be almost ignorant of DTOs. Ideally this would be the flow for store/retrieval operations.

Retrieval Get(Member DTO) from Redis -> Map to (Member BO) -> return to repository

Store Store(Member BO) from app -> Map to (Member DTO) -> store to Redis

However I have not found a decent way to design this mapping process and am at a loss as what to do now.

What I have done in the interim is use reflection with generics in the basic operation methods in the repository to match up the two sets of classes like this.

public List<T> GetSetByKey<T>(string key) where T : Base
{
   Type a = RepositoryMapperHelper.MapClass(typeof(T)); //Matches up class type to respective DTO class
   var obj =
   typeof(RepositoryMapper).GetMethod("GetSetByKey") //RepositoryMapper class contains operations for retreiving DTOs from Redis DB using RedisClient
   .MakeGenericMethod(a)
   .Invoke(RepoMapper, new object[] { key });

    return Mapper.DynamicMap<List<T>>(obj); //Determines types at run-time and uses custom ITypeConverter (in conjunction with RepositoryMapper) to hydrate DO properties
 }
 public static class RepositoryMapperHelper
 {
    public static Type MapClass(Type t)
    {
        if(t == typeof(Connection))
        {
            return typeof (RedisModel.Connection);
        }
 ....
 }

This is a terrible workaround. I don't like anything about it but I cannot think of another way to do it. What I need is a new design idea for handling the mapping interaction -- or the whole thing in general. Are there any mapping libraries that could be useful for mapping between methods or classes like I'm trying to do? How can I fix this problem?

TLDR How can I map between Domain objects and DTOs in a way that is transparent to my Data Layer?

EDIT:

Here is the read operation as it currently stands:

//Make a request to a repository for an object
ParticipantRepository repo = new ParticipantRepository();
repo.GetById(theId);

//My BaseRepository has all generic methods.
//There is a BaseRepository<T> class that inherits from BaseRepository and allows me to call methods without needing to specify T because it is specified when  you instantiate the repository.

//In BaseRepository
public virtual T GetById<T>(long id) where T : Base
{
    Type a = RepositoryMapperHelper.MapClass(typeof(T));
    var obj =
    typeof(RepositoryMapper).GetMethod("GetById")
    .MakeGenericMethod(a)
    .Invoke(RepoMapper, new object[] { id }); //Builds the Generic Method using the respective DataModel.Type returned from MapClass

     return Mapper.DynamicMap<T>(obj); ///Dynamically maps from source(DataModel) to destination type(DomainModel T)
}

 //In RepositoryMapper
 public virtual T GetById<T>(long id) where T : DataModel.Base
 {
     using (var o = RedisClient.As<T>())
     {
       return o.GetById(id);
     }
 }

 //In AutoMapper Configuration
protected override void Configure()
   {
   //An example of how Automapper deals with conversion from key -> object
   Mapper.CreateMap<string, Participant>().ConvertUsing<KeyToBaseConverter<Participant, DataModel.Participant>>();
   }

//The conversion
public class KeyToBaseConverter<T, U> : ITypeConverter<string, T>
    where T : Base
    where U : DataModel.Base
{
   public RepositoryMapper Repository = new RepositoryMapper();
   public T Convert(ResolutionContext context)
   {
       //Get the actual DTO using the Key or Id
       var datamodel = Repository.GetByKey<U>(context.SourceValue.ToString()); 
       return Mapper.DynamicMap<U, T>(datamodel);
   }
}

Using Psuedo-code what i'd like to have happen

//Read Operation
//in domain repository
public T GetByKey<T>(string key) where T : Base
{
   U type = DataModelMapper.Map(T);
   return DataModelRepo.GetByKey<U>(string key);
}

//in DTO repository(facing Redis)
public GetByKey<U>(string key) where U : DataModel.Base
{
  using(var client = RedisClient.As<U>())
   {
      var obj = client.GetByKey(key);
      T type = DataModelMapper.ReverseMap(U);
      return Mapper.Map<T>(obj);
   }
}

//Write Operation
//in domain repository
public void Save<T>(T Entity) where T : Base
{
   U type = DataModelMapper.Map(T);
   DataModelRepo.Save<U>(Entity);
}

//in DTO repository(facing Redis)
public void Save<U>(T Entity) where U : DataModel.Base where T : Base
{
  var obj = Mapper.Map<U>(Entity);
  using(var client = RedisClient.As<U>())
   {
      client.Store(obj);
   }
}

So it's very similar to what I'm already doing, the barrier im running up against is translating between the two types of models and passing the generic type parameter to RepositoryMapper and back again.

Since I wrote this question I've thought about investing more into my AutoMapper implementation, I may be able to use it as the sole go-between for the two repositories -- so basically called Map between the two generics and then lets AutoMapper decide how to get and populate the return object based on some more rules I'd put in place in configuration...

Was it helpful?

Solution

In my opinion, the code is way too complicated. How would you structure your model layer if you were building for the first time using Redis?

I would let go DTO's and AutoMapper (you are using it as an "ORM" for Redis) and model my classes with Redis data structures in mind.

For example, my Participant would be something like:

public class Participant : Base
{        
    public string Name { get; set; }
    public string City { get; set; }
}

where my key for a Participant in Redis would be something like "urn:participant:1". An in my repo I would have:

public Participant GetById(string id)
{       
   return this.Redis.GetById<Participant>(id);
}

public List<Connection> GetForwardConnections(string participantId)
{       
   return this.Redis.GetTypedClient<Connection>().Lists["urn:participant:1:forwardconnections"].ToList();
}

public List<Connection> GetReverseConnections(string participantId)
{       
   return this.Redis.GetTypedClient<Connection>().Lists["urn:participant:1:reverseconnections"].ToList(); // you could also use sets 
}

With that you achieve simplicity by removing many abstractions, mappings, dto's and still get your domain right.

Hope I could help,

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top