0

I am learning how to implement onion architecture using .Net core API. I understand that the focus of Onion architecture is more on flow of dependencies rather on how the layers interact with each other. But I found that domain entities are still accessible in the presentation layer (API layer). This is because the presentation layer has a project dependency on the application layer and the application layer depends on the domain layer where domain entities are defined as public. We cannot define it as internal because the entities will not be accessible by the application layer. This way anyone in the team mistakenly access the domain entities instead of the corresponding DTO.

One solution proposed on Udemy Q&A that we can define domain entities as internal and we can specify the assemblies that can access these internal entities by using the below build configuration

<ItemGroup>
  <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
    <_Parameter1>NameOfProjectToGainAccess</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>

But this is causing error in the MyApplicationDbContext file in the Infrastructure project. The compiler flags error in the below code

public DbSet<MyEntity> MyEntity {get; set;}

It says that the DbSet is less accessible than the public property MyEntity.

I get the similar error in the repository contract definition as base interface is less accessible.

public interface IMyEntityRepository : IGenericRepository<MyEntity>

I want domain entities should not be accessible in the presentation/API layer. This layer should only access DTOs.

Please advise if there is way to do this.

peelaazgar
  • 21
  • 3

3 Answers3

0

One option to address the access problem is to package by component. This technique is described by Simon Brown in the clean architecture book, chapter "the missing chapter", page 318.

The main idea is to use the access modifiers to ensure the architectural constraints. Simon Brown puts all the "use case" related stuff in one package. He can then reduce the access modifiers for all classes and interfaces within the component to hide them from the outside. This would also apply to the entities, even they are not explicitly shown in the diagram.

I guess (I'm a Java programmer) that in C# you would use assemblies to implement the package by component, because the access modifier internal is used to control access to classes within the same assembly. I hope that this is correct.

Questions I anserwed that might also help:

Another option is to use tools that scan your source code to ensure architectural constraints or code reviews. Finally I guess we have to ensure it using code reviews and tools, because even with the package by component approach programmers can change the source code and replace the internal access modifier with public. They usually prefer the fast implementation approach over the long lasting and you often have to do a lot of effort to convince them.

René Link
  • 48,224
  • 13
  • 108
  • 140
0

If you have your base repository i.e. IGenericRepository<MyEntity> internal, then you can't have the extended one public, your IMyEntityRepository, will also be internal to fix your compiler issue, and same goes for your DbSet, the class is internal, how can you make the fields public? Nobody can reach the class, hence it is less accessible than the DbSet itself.

Edit comments space ran out so here...

Move your Dbmodels inside your Persistence project i.e. the project that only has your DbContext and Entity Configs, now your infrastructure project depends on persistence, which only needs the DbModels and not the DTOs(since DTOs are in Core Project, DbContext don't have access to it either way). Now your consumer e.g. WebApi/WPF don't have DbModels, but only Models(DTOs/ViewModels).

To Sum it up: Infrastructure depends on Persistence and Core.

Core has all the Models(DTOs/ViewModels), repositories and/or Commands/Queries(in case of mediator pattern), custom exceptions, or even fluent assertion classes.

Infrastructure implements Core repositories, Commands/Queries(in case of mediator pattern), and repositories are designed in a way to only return Models from the core.
Persistence project has the DbContext, EntityConfigurations, and the DBModels(entities).

Nothing depends on Infrastructure(basically the whole point of following this structure lol), Consumer(Web Api/WPF) and Infrastructure depend on Core, and persistence only need DBModels and/or efcore so it depends on nothing, infrastructure depends on Core and Persistence.

Now do some creative coding to register all of this in your DI ;)

AMunim
  • 992
  • 6
  • 13
  • I agree with your reasoning. My question is how I can hide the inner details like domain entities with presentation layer so that only DTOs are visible to this layer. – peelaazgar Sep 20 '22 at 15:21
  • check my answer now, accept it to let me know you understand it – AMunim Sep 20 '22 at 18:56
0

Even though access modifiers (internal keyword) and proper packaging already help with dependency governance, specific tools usually give greater flexibility for describing particular rules.

One commonly used tool to analyze .Net code is NDepend.

Another helpful tool to implement dependency governance is NsDepCop.

And of course you can also use Roslyn to develop your own code analyzers and integrate those into your build pipeline. An example analyzer checking for "invalid" assembly references you can find here: http://www.plainionist.net/Dependency-Governance-DotNet/

plainionist
  • 2,950
  • 1
  • 15
  • 27