2

I am working on a WebAPI application which follows the layered approach like Controller > Service Layer > Repository Layer > Entity Framework Core (SQL / Cosmos) The view is in Angular.

In many of our API, we have some transformation required on the Request object (DTO - request send by UX) to a domain entity that my repository understands. This transformation usually is handled in the Service layer. This is the standard approach I think.

Now I have an Request object (DTO - request sent by UX, shown below) which is a simple class and I don't need any transformation from DTO to Domain entity. Infact I have a DbSet matching exactly this and the database table has exactly these 3 columns only. In this case, I end up doing the transformation from DTO to Domain entity unnecessarily.

    public class BookDTO //Received in the API request
    {
        public string Name { get; set; }
        public string Author { get; set; }
        public decimal price { get; set; }
    }

To avoid this pointless mapping / transformation I can use the same DTO across all the layers (Controller to Service to Repository), But I feel this is not the right way to do. (Do let me know if there is nothing wrong with this approach)

Essentially either I will be doing a transformation of DTO to domain model when they have absolutely same attributes or I will end up referencing the DTO in all layers including the repository as well.

I am not sure if these are the only two options for me or there is a gap in my understanding.

1 Answer 1

2

It might be helpful to understand why the Repository and Service Layers exist.

The purpose of your Repository layer is to provide CRUD operations.

The Service Layer is there so that you can provide endpoints that return aggregate business objects instead of making multiple (expensive) CRUD calls to your Web API and assembling a complete object on the client.

An example of a CRUD object would be things like Products, Customers, and Addresses.

An example of an aggregate business object would be an Invoice. An Invoice object contains a Customer object, two Address objects (billing address, shipping address), and multiple Product objects.

The Service Layer assembles an Invoice object by making multiple calls to the database and sending the entire object to the client in one go. This improves efficiency by reducing the overhead of multiple HTTP calls and simplifying the API.


So what happens if you really do just need a CRUD object?

You have three choices:

  1. Retrieve the CRUD object by calling the Repository directly, or
  2. Pass the CRUD object through the Service Layer directly, or
  3. Map the CRUD object to a new object, and return that.

The reason choice 3 might be desirable is because it provides isolation. The Service Layer object can be modified independently of the CRUD DTO, and there are no surprises later on when someone finds out (in options 1 and 2) that changes to a DTO object unexpectedly broke something else in the system.

Further Reading
Too many Use Case Classes

1
  • Thanks for taking your time to respond. I went through the link you had shared. Service layer handles aggregation; example Invoice (composition of Books, Customer) which is the right thing. Still, there will be a use case where I am just going to do a CRUD on a simple entity; example create a Book. In this case, I will be doing a mapping from DTO to Domain model just for transforming (mapping) from BookDTO type to BookEntity type (Domain model) and nothing more as no aggregation is needed in this case. (Choice #3 in your answer.) a small cost for better isolation. Thank you. Commented May 23, 2020 at 2:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.