5

I'm creating a multilayered application, having an asp.net mvc application as the highest layer.

The architecture is the following (---> means references):

Presentation layer ---> Service Layer ---> Business layer ----> Data access layer ----> Database

Furthermore, there is a layer called "Infrastracture" which is referenced by all layers.

Each layer has it's own entities. For example: in the presentation layer we might have UserViewModel, in the service layer UserDTO, in the business layer UserBDO and finally, in the data access layer, User.

The automapper is used to automate the conversion between different types.

I read that some developers suggest to place the mappings creation in Global.asax, but it's obvious that if you have a multilayered application you can not create all mappings there. You can't map User with UserBDO in the presentation layer.

So, I'm asking the cleanest way to manage the mapping centralization in a multilayred application. You can even suggest changes in the architecture.

Errore Fatale
  • 978
  • 1
  • 9
  • 21
  • do you have any layer such as utility or framework, there you can expose a function which can type fromTYPE and ToTYPE and convert – Vinay Pratap Singh Bhadauria Sep 28 '15 at 09:00
  • You need a utility project or something like this. Where do you keep your enums (nomenclatures)? – Razvan Dumitru Sep 28 '15 at 09:01
  • @Vinary Singh So, are you suggesting to change the architecture? The business layer can not reference all types, but only UserBDO and User. It doesn't know UserDTO and UserViewModel. – Errore Fatale Sep 28 '15 at 09:01
  • @Razvan Dumitru I don't know what enums have to do with Automapper, but if you are asking where is the code that can be used by all layers... then the reply is: in the Infrastracture. – Errore Fatale Sep 28 '15 at 09:05
  • Whay you have in infrastructure layer? Is strange to refer infrastructure project from presentation. Is better to refer utils project. – Razvan Dumitru Sep 28 '15 at 09:12
  • In my case, the infrastracture is what here is defined as "cross cutting": http://www.uml-diagrams.org/examples/package-diagrams-example-model.png In my case, it contains some useful classes for validation, common interfaces and other... – Errore Fatale Sep 28 '15 at 09:19
  • You can create static class in each project called `AutoMapperConfiguration` with single method `Configure()` where you perform mappings. Then in `Global.asax` or in any other possible bootstrapping place call these `Configure()` methods. – xZ6a33YaYEfmv Sep 28 '15 at 10:09
  • @ieagile: the presentation layer, where Global.asax stay, doesn't references all other projects, but only the service layer. So, how can I call Configure methods from the presentation layer? – Errore Fatale Sep 28 '15 at 11:55
  • 13
    Oooooooone quick note, if you have only one deployed application, don't create all those projects. Just use folders inside a single MVC project. – Jimmy Bogard Sep 28 '15 at 13:53
  • @JimmyBogard Can we have two? One for that yucky MVC project type and the other with real code in it? – Darrel Miller Sep 28 '15 at 14:14
  • @Jimmy Bogard later we might have the same application for other platforms. – Errore Fatale Sep 28 '15 at 14:16
  • 1
    @Errore Fatale If it is only a prediction, there is still no justification for creating n-layers architecture :) – kamil-mrzyglod Sep 28 '15 at 14:27
  • It's not a prediction. It's a project. – Errore Fatale Sep 28 '15 at 14:33
  • 1
    Projects should be viewed as units of physical reusability. If nothing else is sharing all these projects, then merge them into a single project. Refactor as requirements change, not before. – btt Sep 28 '15 at 18:46
  • Consider creating a new protect called "IvoryTower" and place all the translation code in there. Then you can reference IvoryTower from all of your other projects. – Neal Sep 28 '15 at 22:16
  • What you write is senseful, but it was not exactly my decision to adopt a n-tier architecture, it was imposed. I can take some decisions about the architecture, but I can't collapse it to a single-tier architecture. – Errore Fatale Sep 29 '15 at 09:42
  • n-tier architecture has nothing to do with project structure. You can create n-tier architecture with Java, and have everything in a single deployed JAR. – Jimmy Bogard Sep 29 '15 at 12:11
  • Ok, I'll use other words: it was imposed that the application must be modular, so that the business logic can be used with different front-ends. The point "refactor when needed" is valid according to me, but it was not exactly my decision to immedietely start with a modular architecture. – Errore Fatale Sep 29 '15 at 13:09

2 Answers2

1

I suggest you already have the answer - if you cann't access all your types in single place - just split the mapping configurations into several parts. This solution is more maintainable in large projects (just imagine 100 or 1000 mappings in one configuration). For example, we use specific mapper to convert third-party WCF contracts to custom DTOs. This mapper is located in separate project together with WCF client. So contracts and mappings are not accessible from outside.

Ilya Chumakov
  • 23,161
  • 9
  • 86
  • 114
  • So, do you call the mappings in the constructor of the class which will convert one type to an other? Or do you have a different approach? – Errore Fatale Sep 28 '15 at 12:01
  • Yes, we don't call `Mapper` methods directly. Instead a class-wrapper is used with its own `Map` method (simple wrapper for `Mapper.Map`). This class also have a static constructor containing `Mapper.CreateMap` calls (so we sure the configuration is created before `Mapper.Map` call, and it is created only 1 time). – Ilya Chumakov Sep 28 '15 at 12:43
  • Do you need all these layers? Try starting simple with just Entities and your MVC view models/models. Try collapsing your projects into folders to use namespaces instead. Do you lose anything meaningful by doing this. Using automapper in a greenfield project is usually an indication things are already too complex. – DalSoft Sep 29 '15 at 08:26
  • @Ilya Chumakow in the context of a WCF service, would you call the CongifureAllMappings() method in the constructor of the service? The problem is that if the service is stateless the ConfigureAllMappings() method would be called each time the service is called. Is this necessary? What is the "life cycle" of mappings? They are static resources, right? So they are alive until the process which hosts the service is alive, right? – Errore Fatale Sep 29 '15 at 09:44
  • @ErroreFatale, yes, mappings are static (by default) and static resources are created once per AppDomain http://stackoverflow.com/questions/4984358/static-variables-in-wcf. Also, it is possible to manage a lifecycle of WCF service http://www.codeproject.com/Articles/86007/ways-to-do-WCF-instance-management-Per-call-Per – Ilya Chumakov Sep 29 '15 at 11:13
  • I wrote an anwser which might be similar to what you suggest. Feel free to give me e feedback. – Errore Fatale Sep 29 '15 at 12:23
0

I try to answer to my own question: feel free to correct me if there is anything which doesn't sound good to you.

I will use the following words:

  • layer: in this context a layer is a module of the application in the vertical sense (from the database to the user interface)

  • module: an area of your application in the horizontal sense, for example "CRM", "Product", Accounting",... each one having it's code in different layers.

If you have an application divided in n layers and n modules, the follwing could be an example for a specific class of a particular module and layer.

public static class ProductMapper
{
    static ProductMapper()
    {
        MapProductBDOToDTO();
        MapProductDTOToBDO();

        MapProductCategoryBDOToDTO();
        MapProductCategoryDTOToBDO();

        MapIvaBDOToDTO();
        MapIvaDTOToBDO();

        MapProductSupplierBDOToDTO();
        MapProductSupplierDTOToBDO();

        MapProductPictureBDOToDTO();
        MapProductPictureDTOToBDO();

        MapProductNoteBDOToDTO();
        MapProductNoteDTOToBDO();

        MapStockProductBDOToDTO();
        MapStockProductDTOToBDO();

        MapTagBDOToDTO();
        MapTagDTOToBDO();
    }


    public static TTargetType Convert<TToConvert, TTargetType>(TToConvert toConvert)
    {
        return Mapper.Map<TTargetType>(toConvert);
    }


    private static void MapProductDTOToBDO()
    {
        Mapper.CreateMap<ProductDTO, ProductBDO>();
    }

    private static void MapProductBDOToDTO()
    {
        Mapper.CreateMap<ProductDTO, ProductBDO>().ReverseMap();
    }

    private static void MapProductCategoryDTOToBDO()
    {
        Mapper.CreateMap<ProductCategoryDTO, ProductCategoryBDO>();
    }

    private static void MapProductCategoryBDOToDTO()
    {
        Mapper.CreateMap<ProductCategoryBDO, ProductCategoryDTO>();
    }

    private static void MapIvaDTOToBDO()
    {
        Mapper.CreateMap<IvaDTO, IvaBDO>();
    }

    private static void MapIvaBDOToDTO()
    {
        Mapper.CreateMap<IvaBDO, IvaDTO>();
    }

    private static void MapProductSupplierDTOToBDO()
    {
        Mapper.CreateMap<ProductSupplierDTO, ProductSupplierBDO>();
    }

    private static void MapProductSupplierBDOToDTO()
    {
        Mapper.CreateMap<ProductSupplierDTO, ProductSupplierBDO>().ReverseMap();
    }

    private static void MapProductPictureDTOToBDO()
    {
        Mapper.CreateMap<ProductPictureDTO, ProductPictureBDO>();
    }

    private static void MapProductPictureBDOToDTO()
    {
        Mapper.CreateMap<ProductPictureDTO, ProductPictureBDO>().ReverseMap();
    }

    private static void MapProductNoteDTOToBDO()
    {
        Mapper.CreateMap<ProductNoteDTO, ProductNoteBDO>();
    }

    private static void MapProductNoteBDOToDTO()
    {
        Mapper.CreateMap<ProductNoteDTO, ProductNoteBDO>().ReverseMap();
    }

    private static void MapStockProductDTOToBDO()
    {
        Mapper.CreateMap<StockProductDTO, StockProductBDO>();
    }

    private static void MapStockProductBDOToDTO()
    {
        Mapper.CreateMap<StockProductDTO, StockProductBDO>().ReverseMap();
    }

    private static void MapTagDTOToBDO()
    {
        Mapper.CreateMap<TagDTO, TagBDO>();
    }

    private static void MapTagBDOToDTO()
    {
        Mapper.CreateMap<TagDTO, TagBDO>().ReverseMap();
    }

As you can see it's a static class with a static constructor, which means that the constructor will be called not more than ONCE during the application life time. The first time you call Convert method, the constructor is called and all mappings are created. The second, third, fourth... n time you will cal the Convert method during the same session, the constructor will not be called.

Advantages:

  • A particular mapping can not be created more than once
  • Mappings are created only when you need them (in this case, when you work with Products). You don't create 1000 mappings at application start for then using only 5 or 6 of them.

Disadvantages:

  • A developer can call the generic Convert method with all types he/she wants, types are not checked. By the way using a generic method requires less code, and if mappings have not been created yet an exception will be thrown by the AutoMapper.
Errore Fatale
  • 978
  • 1
  • 9
  • 21