The missing Project that fixes everything in .NET
It has many names. Robert C. Martin (AKA Uncle Bob) calls it the Main Component, and Mark Seemann calls it the Composition Root. You may also know it as the Application Host or Startup. Not the same as "Startup.cs". I'm still trying to find my preferred name, so you may see me using those terms interchangeably.
Robert C. Martin says it is the Ultimate Detail. Even then, most seem to not care about it.
Those brave souls who care about it will look into the face of .NET and say: "Not Today!". Arya Stark style. They will often do it by:
- Moving entry points to a Presentation Project. For example, extracting Controllers out of an MVC API.
- Load dependencies through dynamic assembly loading using something like MEF.
I have a slightly different approach, and it's what I want to show you today.
But first, let's see what the Main Component/Composition Root/Application Host/Startup is.
❓ What is it
When you apply Clean Architecture, Onion Architecture, Hexagonal Architecture, or any other shape of device-independent architecture, keeping the dependencies independent and respecting the Dependency Rule may be challenging. .NET, you know I'm talking about you. The Template for an API or Web application is already the host itself.
You need to define the dependencies and configurations of the involved modules/components/projects, which can be challenging when the DI configuration happens inside the adapter. You may do it through a Dependency Injection Container or by Pure DI. But, the DI responsible project will need to know all the other projects. Unless you want to do it dynamically. What I don't recommend in most cases.
The dependency management becomes a problem when that Project should be a simple adapter to the Core Application logic. Once you introduce all the references there, it becomes hard to enforce the Dependency Rule and to spot shortcuts. Once you notice it, you may have an MVC directly inserted into your SQL DB even when it was not supposed to. That's why adapters should be independent of each other.
Robert C. Martin is absolutely right when he says it is the "dirtiest of all the dirty components". The project is coupled to everyone. But he does it in good faith. Someone has to sacrifice to keep the rest of the family safe.
🏗 How it sits in the architecture
The application host/startup is the initial entry point of the application.
You can picture it as the outermost circle Onion Architecture or Clean Architecture diagram.
This component will be the one you will start when you run your application. It loads everything, configures it, and hands control to another component.
👍 The benefits
Besides the obvious benefit of removing unwanted dependencies from other projects, it will allow you to use multiple configurations, depending on the scenario. So, if you want to use the same application logic as a CLI or an API, you simply have a different host/startup where you do a different configuration. Just that. Do you need a sandbox version with another DB Adapter? You implement another host/startup. Those are just examples.
📝 How to do it
Note: If you want to follow along, you can find the example here. Then, just run the following script.
dotnet add src/Adapter.Api/Adapter.Api.csproj reference ./src/Adapter.Kafka/Adapter.Kafka.csproj
dotnet add src/Adapter.Api/Adapter.Api.csproj reference ./src/Adapter.PostgreSQL/Adapter.PostgreSQL.csproj
Before we start, your dependency diagram may look something like this.
1. Create a new console project
dotnet new console -n Application.Api -o src/Application.Api
dotnet sln add src/Application.Api
2. Change old startup project (Adapter.Api) to a library. Edit the .csproj and add the Output Type as a library
3. Remove references to other adapters from old startup project (Adapter.Api).
4. Add references to the new project
dotnet add src/Application.Api/Application.Api.csproj reference ./src/Adapter.Kafka/Adapter.Kafka.csproj
dotnet add src/Application.Api/Application.Api.csproj reference ./src/Adapter.PostgreSQL/Adapter.PostgreSQL.csproj
dotnet add src/Application.Api/Application.Api.csproj reference ./src/Adapter.Api/Adapter.Api.csproj
5. Convert the Program.cs for the old startup project (Adapter.Api) into a class
I like to split the building from the running.
public class AdapterApi
private WebApplicationBuilder _builder;
public AdapterApi(string args)
_builder = WebApplication.CreateBuilder(args);
public Task RunAsync()
var app = _builder.Build();
6. Receive the Dependency Injection configuration
Add an argument to the constructor to receive an Action to configure DI.
Now the constructor will look like this:
public AdapterApi(string args, Action<IServiceCollection> options)
_builder = WebApplication.CreateBuilder(args);
7. Setup the adapter and run the application
Now, in your new startup project (Application.Api) you simply setup your Adapters and run it.
var api = new AdapterApi(args, options =>
Now, your diagram will look like this.
Take a look at this article if you have any doubts about how to assemble a device-independent solution.
I hope you have found this useful.
Follow me on Twitter (@gsferreira), and let's keep in touch.