Environment:
I'm working on a system that is architected as a modular monolith with ASP.NET Core 7. As a first step communication between bounded contexts is handled by exposed commands/queries.
Problem:
I'm wondering in which project to put the contract between API and application layer so that the API layer can't access anything except the contract.
Thoughts:
In a direct application of dependency inversion, the abstracts are owned by the upper/policy layers. This architecture groups the higher/policy components and the abstractions that define lower services together in the same package. The lower-level layers are created by inheritance/implementation of these abstract classes or interfaces wikipedia referencing Robert C. Martin: Agile Software Development
This would lead to this diagram (infrastructure layer is left out for simplicity):
I don't understand why the contract should be associated with the API layer. Different API technologies (REST, GraphQL, gRPC) can be offered parallel and need to access the same interface/contract.
Steven van Deursen writes in his book "Dependency Injection" that
this dependency would make it impossible to replace the UI.
It would also make the application layer depend on the UI layer. If the service of another bounded context is called the caller needs a dependency to another API layer. This all doesn't make sense to me.
Most examples I have seen put the contract in the application layer:
This allows different API layers to access the provided commands/queries or services simultaniously. But this also allows the API layers to access the application layer and due to the default transitivity of project dependenies in C# even the domain layer. It can bypass the application layer and access the domain layer directly.
I see two options to prevent access to the application internals: When using MediatR the handler class can be made internal while the handler is public. When using interfaces the implementation in the application layer can be made internal and be exposed just to the DI container with [InternalsVisibleToAttribute]. Domain classes and services need to be internal, too and the domain layer must grant access to the application layer with [InternalsVisibleToAttribute]. The infrastructure layer, whih is not covered here, needs to be concidered, too.
Since all of this is quite cumbersome I think the best solution would be to place the contract in a different layer.
The application layer can only access the interfaces and nothing more. Calling services in other bounded contexts requires only a dependency on the contract.
Classes in application and domain layer can even be public, because they can't be accessed directly from the API layer anymore.
None of the examples I have seen used a separate contract layer or prevented the API layer from accessing the application layer – probably for simplicity. The concepts of preventing access to lower levels is often discussed, but not explained how it is achive in .NET. In Java package protected can be used, but how is this handled in .NET? What is the common practice and why?


