0

I'm wondering: how complex a mapper abstraction can be?

Let's say I have a controller's action

ActionResult Find(QueryInputModel query)

with the model looks like this

class QueryInputModel
{
    public string Text {get;set;}
    public IEnumerable<string> RegionCodes {get;set;}
}

Among other things, it's the action's responsibility to turn an input model into a view model

class QueryViewModel
{
    public string Text {get;set;}
    public IEnumerable<Region> Regions {get;set;}
}

class Region
{
    public string Name {get;set;}
    public string Code {get;set;}
}

where region names in a view model should be taken from db using the codes in a input model. Right now it's done by the QueryMapper.Map(src) method which looks like this

public QueryViewModel Map(QueryInputModel source)
{
    var regions = regionRepository.Get(source.RegionCodes);
    var result = new QueryViewModel {Text=source.Text, Regions=regions};
}

Is it correct to call such an abstraction a Mapper? Is it OK to have straight-forward mapping and db querying mixed in a single method?

vorou
  • 1,869
  • 3
  • 18
  • 35
  • 1
    It is good that you have doubts about this. But if you have doubts, you do not need our "go-ahead" to change it. Just change it into something that feels cleaner to you. – Jan Dörrenhaus Jul 03 '13 at 15:22

2 Answers2

1

Well, here is my take on it. From what you're saying, you have a use case "Find region details from region codes". This use case has two input ports and one output port:

  • The way for the user to input her query is one input port.
  • The mapping in DB from region codes to region details is another input port.
  • The way to give the results back to the user is an output port.

All the logic you implemented in your Map function is your use case logic. The function should really look like this.

public void FindRegionDetailsFromRegionCodes(IUserQuery userQuery,
                                             IRegionMapper regionMapper,
                                             IUserDisplay userResult) {
    var regions = regionMapper.Get(userQuery.RegionCodes);
    userResult.ShowResult(regions);
}

With:

interface IUserQuery {
  IEnumerable<string> RegionCodes { get; }
}

interface IRegionMapper {
  IEnumerable<Region> Get(IEnumerable<string> regionCodes);
}

interface IUserDisplay {
  void ShowResult(IEnumerable<Region> regions);
}

This way, the use case code is only concerned with the logic of the execution of the action, and not the details of how each of the things are done. You can expose your service through a TCP network port (by appropriately implementing IUserQuery and IUserDisplay), or calculate details by throwing a pair of D20 and lookup a random file on C:\ and breeding hamsters (by appropriately implementing IRegionMapper).

Now, the truth is that you're exposing this use case in a controller action? Good. Therefore your controller action may call FindRegionDetailsFromRegionCodes by passing it:

  • Your QueryInputModel, implementing IUserQuery
  • Your QueryViewModel, implementing IUserDisplay
  • Your RegionRepository (I suppose it's the name of the class), implementing IRegionMapper

Copying the Text from the input to the view is not your use case's concern. It should be done by your action. (as a supporting argument to my thesis, if you have at some point to copy another field, this should not change the use case code)

Hope this helps. To answer your question, the real mapping happens in DB, that's why I only called the "DB" port IRegionMapper.

Laurent LA RIZZA
  • 2,905
  • 1
  • 23
  • 41
-1

Your mapping between ViewModel and DataModel is Manual mapping, try to use some favor mapping 3rd party like Automapper or Valueinjecter, both also can install by Nuget to your project file. Personally prefer Valueinjecter as it more easy to code and understanding.

Harris Yer
  • 281
  • 2
  • 10