4
votes

This is a follow-up for my question from here.

First of all, I am not using DDD in my project.

I have a WCF service with 3 layers:

  • The service layer (only holds operations and calls BL methods)
  • The business logic layer that holds all the business logic classes and methods
  • The data access layer that hold the DbContext (LINQ-TO-EF) and POCO entities

The WCF service needs to return DTO objects, and I am having trouble figuring out the best place to put the 'Translator' class that translates my POCO entities to DTOs.

I had 2 options for this:

.

.

METHOD A

Have the business logic methods return entities to the service layer, and the service layer has a translator class in it that translates entities to DTOs.

PROS:

  • Business logic layer does what it has to do - validation and CRUD operations
  • Business logic layer does not need to know about DTOs at all

CONS:

  • Service layer now has to include a reference to the 'data access layer' assembly, because it receives entities from the business logic layer. This seems to break the 3-tier concept, which says that the service layer needs to reference the BL layer only, and the BL layer needs to reference the DAL only.
  • This is the worst problem : the translator class needs to create DTOs from entity objects. Because it receives the entity objects from the BL after the DbContext was disposed of, it cannot access anything that was not loaded with the 'Include' extension. This means that the BL methods needs to return entities back to the service layer, with everything that would be needed by the translator in order to create DTOs. This is a problem because it requires the BL to know what the translator needs, and second - it will fetch a lot of unnecessary data from the database ! (maybe the translator needs to return a 'UserDto' object that one of it's fields is 'total number of orders' - why would I want to fetch all the orders from the database just to make a 'Count()' call in the translator ?

.

.

METHOD B

Have the 'Translator' class that translates from entity objects to DTOs placed in the 'business logic layer' itself. In this mechanism - the BL methods return DTOs already.

PROS:

  • The BL method perform the BL code, and then calls the 'translate_to_dto' appropriate message to translate the results to a DTO that is returned. This is all done inside the 'DbContext' which means that when calling the translator class to translate an entity, it can still access the child objects, and no call to 'Include' is needed. This means that only the data that is needed to create the DTO is fetched from the database.

CONS:

  • Now the 'translator' class that is in the 'business logic layer' is the one who needs to know about DTOs, even though it is the responsibility of the service layer to know about them only !
  • Each method in the BL now perform pure BL (validity checks, CRUD operations etc), and in addition calls the translator method in order to return a DTO. This break the 'Single Responsibility Rule' that says that a method (in the BL) should only do one specific thing.

.

Can anyone tell me where the right place is to perform the 'Entity ==> DTO' conversion ?

.

[Update - Added Example]

Business Logic layer has a manager class called UserManager that has a BL method like this:

public UserTasksDto     GetUserInfoWithTasks(Guid userId)
{
    if (userId == Guid.Empty)
        throw new ArgumentException("User ID cannot be empty");

    using (IMyDBEntities entities = _contextFactory.GetContext())
    {
        // Get POCO Object from DbContext
        User user = entities.Users.Find(userId);
        if (user == null)
            throw new EntityNotFoundException("User was not found in the database");

        if (user.Tasks.Count() == 0)
            throw new Exception("User does not have any tasks !");

        // Call 'Translator' static method to translate POCO to DTO
        Translator.TranslateUserToUserTasksDto(user);
    }
}

As you can see above - the BL method calls a 'translator' method to turn POCO to DTO. This is done inside the 'entities' context, so that the translator can still access the user's 'Tasks' children.

Here is what the 'Translator' method looks like:

class Translator
{
    public static UserTasksDto   TranslateUserToUserTasksDto ( User userPoco )
    {
        UserTasksDto dto = new UserTasksDto
        {
            UserId        = userPoco.Id,
            Username      = userPoco.Username,
            CreationDate  = userPoco.CreationDate,

            // Accessing a related entity, this is why this 'translate' method 
            // needs to be called inside the DbContext, otherwise it will except
            // (or we load all related entities using 'Include' just for the 'Count' purpose)
            Supervisor    = userPoco.Supervisor.Username,    
            NumOfTasks    = userPoco.Tasks.Count(),
            FirstTaskDate = userPoco.Tasks.OrderBy(task => task.Date).Take(1),
        }

        return dto;
    }
}

As you can see above - the 'Translate' method 'builds' a 'UserTasksDto' from a 'User' POCO object. This is done by mapping some fields from the 'User' object and it's related entities to the DTO. If this method was not inside the BL method's ObjectContext - I would get an exception saying I am trying to access entities without a context.

I hope my problem now is more clear...

2

2 Answers

2
votes

Though question,

If you need your service layer to be independent of the BL/DAL data entities, then, in my opinion you have to have a new abstraction layer (dll assembly) for independent models.

Your BL and/or DAL now will not return Entities, but rather Model objects from this new assembly.

Your Service layer will not require a reference to the DAL for the entities, but a reference to the new Models assembly.

This is a similar pattern to the view models.

Now the service layer's job is to transform the models to DTO if it chooses to do so.

PROS: Will release you of BL/DAL dependency

CONS: Models abstraction layer will look as if redundent


EDIT:

Oh, I did not mean to return DTOs from the business layer. What I meant was to return independent models from the business logic so that they will really be independent from all the layers, and whichever layer that works with them afterwards can use them or transform them to whatever form needed.

For example the service layer may include a model to DTO translater, a presentation layer may transform them into view models, a repository may choose to transform them to XML .... and so on and so forth. Now each layer will have its own Model to X transformer, BL will only have single responsibility, and all layers will be independent from the DAL entities.

N.B. Some layers may choose to use them directly, this I believe was your concern, and yes if they do so they will act as if BL returned a DTO/VM, but this was not my intention.

Hope this clears up things now...


UPDATE:

Think of it that you are a developer who will not write any layer other than the BL.

Would you return the entities of DAL. NO.

Will you have any notion of how will your library be used, and what layers will use it. NO.

You will create a new abstraction on top of the DAL entities and return these to WHICHEVER layer that will use your library.

So if developer X comes along and uses your library to create an ACME WCF service, I am pretty sure X will not use your model object as his DTO, rather X will create DTOs using your models as a start.

Developer Y comes along and uses your library to create an ACME ASP.NET MVC 3 application, I am pretty sure Y will not use your model object as his VMs (View Models), rather Y will create VMs using your models as a start.

0
votes

There probably is no right place, but the most logical place would be with your Business Logic.