For almost all of my projects, I follow this simple project structure and it works great, so let’s go over it from the bottom.
The entry project for your solution, in most cases it will be an API Host (for HTTP communication), but with this approach adding a new way of hosting your app is as easy as adding a new project and hooking up into your application. It should reference the
Infrastructure project and register all it’s entry points as needed (
app.UseEndpoints(endpoints => endpoints.MapControllers()); in the case of HTTP API). An example name for the project would be:
Example API Host containing Controller and Startup, Program
Infrastucture & Database Access
Infrastructure is implementing all of the non-domain logic of your application, that your domain abstracts from via interfaces. This layer should be replaceable if needed - it should be possible to change database ORM, dependency injection container, etc. If your infrastructure layer is mostly focused on data persistence it can be merged into one project. Possible names for this project would be
The domain layer should realize all your business ideas and nothing more, it should function as a separate entity. The domain should be written in pure C# (POCOs) without any unnecessary dependencies on other projects. Additional part of the Domain are
Outbound Ports which define the
Interfaces which the
Domain requires to function (without any concrete implementations eg.
Repositories abstracted by interfaces). It should be possible to move over the project to another application that would like to realize the domain, with clear information on what the
Domain requires to function (
Outbound ports to be fulfilled in the
Domain layer could also be split into multiple projects depending on the number of subdomains your application may realize.
Example Domain layer folder with User Entity, Service and Outbound Ports to be realised in the Infrastructure
This layer is responsible for handling all the requests that are specified in the
Contracts project. Handling the requests means not only passing them to the appropriate services located in the
Infrastructure layer, but it should also validate them against non-business requirements, map the request/response objects from the one exposed to the user to the internal ones used by the domain (eg.
Entity), check for permissions. This layer is the contact to the outside world, so it may be responsible for throttling requests and/or caching them.
Example Application layer folder following CQRS and including mapping and valiators for User
This layer specifies all DTOs used in the
Application layer as well as the
User Interface. It should contain all definitions of requests and responses for them. The Contract name stands for the contract that it establishes between the Client and Application, saying given those inputs it will produce the given output. This layer may be named
Example Contracts layer folder with Requests for User
This layer is the entry point to the application for the user, it may take a form of a regular web page, SPA, native application, and many more. If the interface shares the same language as the backend (eg.
Blazor) it should reference the same contracts project, but in the other case, it may be a good idea to autogenerate it from the original