Search code examples
c#asp.net-corearchitecture3-tieronion-architecture

Can I Return DTOs From DAL


I have started using an onion architecture because I wasn't satisfied with my previous project structure. My main question is, can I return DTOs from the data access layer?

This is my current structure:

/Core
 - Application
 - Domain
/Infrastructure
 - Persistence
 - Identity
/WebApi
 - WebApi

Quick explanation:

  • Application layer is where I have all my business logic
  • Domain layer is where all the entities/models are defined
  • Persistence layer is where I query the database
  • Identity layer is for user management
  • WebApi layer is where I have my controllers

The data flow is the following:

Client <- WebApi Layer <- (DTO) Application Layer <- (Entity) Persistence Layer

As of right now, the persistence layer returns the actual database entities which are defined in the domain layer which the application layer transforms to DTOs.

My problem here is that often, the persistence layer actually needs to perform different queries which will make the type different from the entity type. For example, I could join different tables, apply groupings, etc. Thus, I cannot return an entity type.

Am I allowed to return DTOs from the persistence layer (data access layer)? If yes, where do I defined these DTOs? Defining them along with the other DTOs in the application layer and using these DTOs in the persistence layer would make it dependend on the application layer (not so great in my opinion since we want to minimize couplings). Another option would be to create them in the domain layer along with the entities (probably the better approach)? For consitency's sake, should I only return DTOs from the application layer or is it fine to return entity types as well as DTOs?

Lots of questions that I couldn't find. Just trying to become a better programmer:)


Solution

  • My main question is, can I return DTOs from the data access layer?

    Yes. Noting that:

    1. Yes, because who's going to stop you?
    2. Yes, as in it's a common enough recognized pattern, and generally speaking no kittens will die if you take that approach.

    Am I allowed to return DTOs from the persistence layer (data access layer)? If yes, where do I defined these DTOs?

    My default "in the absence of a reason not to" architecture for layered apps is to use DTO's, which I typically define in a common library. Detailed write-up of that here (pdf). The Data access, business logic and UI all share this as a common vocabulary.

    This makes them useful for where you use Dependency Injection to abstract out your data access - the interfaces you define will also have to use the DTOs in their signatures.

    If you want to, you could define DTO's that only the data access layer, and it's consumers know about - the question is why would you want to? Not to say you can't - only that you should only do that if you have thought it through and have a meaningful reason to do it.

    My problem here is that often, the persistence layer actually needs to perform different queries which will make the type different from the entity type. For example, I could join different tables, apply groupings, etc. Thus, I cannot return an entity type.

    Sometimes you'll have scenarios where the DTO's you create are very scenario specific (don't get reused). Only you can say if that's acceptable or not. It's a trade-off between how much code you want to maintain, and how fit-for-purpose DTO's are. I don't think having scenario specific DTO's is bad in of itself, if it means the architecture remains clean and how you implement the scenario is sensible.