1

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:)

Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102
  • 1
    This question is probably opinion-soliciting. I prefer to keep all the models including DTOs together in a core/abstractions library project, so they are organized together. But "it depends" is probably the only right answer. – Noah Stahl Aug 03 '21 at 00:57
  • 1
    That's the problem with the architecture tag in general, and an area where I think bending the rules (within limits) is not a bad thing. Architecture is about balancing trade-offs; in the absence of full context (questions with excessive amounts of detail) resorting to opinions based on limited info is probably the best we have. I'd argue that the differing opinions is what makes architecture questions valuable - acknowledging that it makes answer allocation problematic. – Adrian K Aug 03 '21 at 22:26
  • Totally agree with you @AdrianK! Pretty much all architectural questions entail opinion based solutions. I was hesitating posting this question but I was trying to give as much information as possible and if I cannot ask a question like this on StackOverflow, I don't know where else. I don't think questions like this lower the quality that StackOverflow is trying to maintain, instead, it gives guidance to programmers. – Tom el Safadi Aug 03 '21 at 23:22

1 Answers1

5

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.

Adrian K
  • 9,880
  • 3
  • 33
  • 59
  • That looks very interesting! Do you have a GitHub repo by any change to look at a specific example that implements what you showcase in the pdf file? – Tom el Safadi Aug 03 '21 at 22:15
  • Ahhhh, unfortunately not. I had examples on CodePlex (R.I.P) back in the day. Once I get my act together on GitHub I'll post the link here. – Adrian K Aug 03 '21 at 22:22
  • Perfect, thanks so much! The 5-layer architecture you describe looks very neat and not too complicated – Tom el Safadi Aug 03 '21 at 22:25
  • Here you go Tom - https://github.com/AdrianKearns/Morphfolia-v2.5.0.0 – Adrian K Aug 04 '21 at 01:27