The .NET CLI and Project Structure Guide I Wish I Had When Transitioning from Spring Boot

1. Introduction: The Learning Curve Nobody Talks About
When I first transitioned from Spring Boot to ASP.NET Core, one of the biggest challenges was not actually C#.
It was understanding the ecosystem.
With Spring Boot, many workflows felt familiar because of Maven, Gradle, application.properties, and the convention-heavy project structure.
In .NET, I suddenly had to understand:
- the
dotnetCLI
- multiple project templates
- solution files
- startup projects
- launch profiles
- appsettings.json
- NuGet package management
- EF Core tooling
- multi-project architectures
- user secrets
- watch mode
- different hosting models
At first, the ecosystem felt fragmented.
I remember keeping random .NET CLI commands in Notepad because I constantly forgot them while switching between projects.
Over time, however, I realized something important:
The .NET CLI is one of the most powerful and consistent developer tooling ecosystems available today.
Once the concepts click, productivity improves dramatically.
This article covers the commands, project structures, and workflows I struggled with earlier.
2. Understanding the .NET Ecosystem Structure
One of the first confusing things for developers coming from Spring Boot is that .NET projects are often organized differently.
A typical enterprise .NET solution using the clean architecture structure may look like this:
InventoryManagement/
│
├── InventoryManagement.sln
│
├── global.json
├── Directory.Build.props
├── Directory.Packages.props
├── .editorconfig
├── README.md
├── .gitignore
│
├── docs/
│ ├── architecture/
│ ├── api-specs/
│ ├── diagrams/
│ └── decisions/
│
├── deploy/
│ ├── docker/
│ ├── kubernetes/
│ ├── nginx/
│ └── terraform/
│
├── scripts/
│ ├── database/
│ ├── setup/
│ ├── migrations/
│ └── ci/
│
├── src/
│ │
│ ├── BuildingBlocks/
│ │ ├── BuildingBlocks.Logging/
│ │ ├── BuildingBlocks.Messaging/
│ │ ├── BuildingBlocks.Security/
│ │ ├── BuildingBlocks.Observability/
│ │ └── BuildingBlocks.Shared/
│ │
│ ├── Services/
│ │ │
│ │ ├── Identity/
│ │ │ ├── Identity.Api/
│ │ │ ├── Identity.Application/
│ │ │ ├── Identity.Domain/
│ │ │ ├── Identity.Infrastructure/
│ │ │ └── Identity.Contracts/
│ │ │
│ │ ├── Inventory/
│ │ │ ├── Inventory.Api/
│ │ │ ├── Inventory.Application/
│ │ │ ├── Inventory.Domain/
│ │ │ ├── Inventory.Infrastructure/
│ │ │ └── Inventory.Contracts/
│ │ │
│ │ ├── Orders/
│ │ │ ├── Orders.Api/
│ │ │ ├── Orders.Application/
│ │ │ ├── Orders.Domain/
│ │ │ ├── Orders.Infrastructure/
│ │ │ └── Orders.Contracts/
│ │ │
│ │ └── Notifications/
│ │ ├── Notifications.Api/
│ │ ├── Notifications.Application/
│ │ ├── Notifications.Domain/
│ │ ├── Notifications.Infrastructure/
│ │ └── Notifications.Contracts/
│ │
│ ├── ApiGateway/
│ │ └── Gateway.Api/
│ │
│ └── SharedKernel/
│ ├── SharedKernel.Domain/
│ ├── SharedKernel.Application/
│ └── SharedKernel.Contracts/
│
├── tests/
│ │
│ ├── UnitTests/
│ │ ├── Inventory.UnitTests/
│ │ ├── Orders.UnitTests/
│ │ └── Identity.UnitTests/
│ │
│ ├── IntegrationTests/
│ │ ├── Inventory.IntegrationTests/
│ │ ├── Orders.IntegrationTests/
│ │ └── Identity.IntegrationTests/
│ │
│ ├── ArchitectureTests/
│ └── PerformanceTests/
│
├── observability/
│ ├── prometheus/
│ ├── grafana/
│ ├── loki/
│ └── otel/
│
└── .github/
└── workflows/i) Important Concepts
a) Solution File (.sln)
The solution file acts like a workspace containing multiple projects.
Think of it as an organizational layer.
b) Project File (.csproj)
Each project has its own .csproj file.
This is roughly comparable to Maven's pom.xml.
The project file defines:
- dependencies
- SDK type
- build settings
- target frameworks
- references
ii) Why Multiple Projects Are Common
In enterprise .NET applications, separation of concerns is strongly emphasized.
Typical layers include:
- API layer
- Application/business logic
- Domain models
- Infrastructure/data access
This structure maps very naturally to Clean Architecture and Domain-Driven Design patterns.
3. Creating Projects with the .NET CLI
The .NET CLI revolves around project templates.
This was initially confusing because there are many different templates depending on the application type.
i) Listing Available Templates
dotnet new listThis shows all installed templates.
ii) Creating Common Project Types
a) ASP.NET Core Web API
dotnet new webapi -n InventoryManagement.Apib) Console Application
dotnet new console -n DemoAppc) Class Library
dotnet new classlib -n InventoryManagement.Domaind) Blazor Application
dotnet new blazore) MAUI Application
dotnet new mauif) gRPC Service
dotnet new grpciii) Creating a Solution
dotnet new sln -n InventoryManagementiv) Adding Projects to the Solution
dotnet sln add src/InventoryManagement.Api/InventoryManagement.Api.csprojThis workflow becomes extremely powerful once you understand how projects connect together.
4. Managing Dependencies with NuGet
NuGet is the package manager for .NET.
Coming from Maven or Gradle, the experience feels surprisingly clean.
i) Installing Packages
dotnet add package Microsoft.EntityFrameworkCore.DesignYou can also target a specific project:
dotnet add src/InventoryManagement.Api/InventoryManagement.Api.csproj package Microsoft.EntityFrameworkCore.Designii) Removing Packages
dotnet remove package Microsoft.EntityFrameworkCore.Designiii) Restoring Dependencies
dotnet restoreiv) Updating Packages
Most IDEs handle this visually, but package updates can also be managed via CLI tools.
v) Why This Matters
One thing I eventually appreciated about the .NET ecosystem is the consistency.
Most developer workflows follow the same command structure:
dotnet [verb] [target] [options]This makes the tooling relatively predictable once the patterns become familiar.
5. Running Applications and Watch Mode
The dotnet run command is straightforward:
However, things become more interesting with watch mode.
i) Watch Mode
dotnet watch runThis automatically rebuilds and restarts the application whenever files change.
For backend API development, this becomes a major productivity improvement.
ii) Launch Profiles
ASP.NET Core projects usually contain:
Properties/launchSettings.jsonThis file defines:
- ports
- environment variables
- launch profiles
- HTTPS settings
Example:
{
"profiles": {
"http": {
"commandName": "Project",
"applicationUrl": "http://localhost:5000"
}
}
}iii) Running Specific Profiles
dotnet run --launch-profile httpThis concept initially confused me because it differs from how Spring Boot profiles work.
Eventually, I realized that .NET separates:
- environment configuration
- launch configuration
- application settings
more explicitly.
6. Understanding appsettings.json and Environment Configuration
Configuration management in .NET is one of the ecosystem's strongest areas.
A typical project may contain:
appsettings.json
appsettings.Development.json
appsettings.Production.jsoni) Base Configuration
{
"ConnectionStrings": {
"DefaultConnection": "..."
}
}ii) Environment-Specific Overrides
{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
}
}iii) Environment Selection
The environment is usually controlled using:
ASPNETCORE_ENVIRONMENT=Developmentiv) User Secrets
One feature I quickly appreciated was user secrets.
Instead of hardcoding sensitive configuration locally:
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Host=localhost;Port=5432;Database=inventory_dotnet;Username=karianmash;Password=password"This stores secrets outside the repository.
This is much cleaner than accidentally committing credentials into source control.
v) Secret Keys
Generating secure values is also easy:
[guid]::NewGuid().ToString("N")I used this frequently for JWT secrets and application keys.
7. Entity Framework Core CLI Workflows
Entity Framework Core tooling was another major learning curve.
Initially, I struggled to understand:
- startup projects
- migrations
- database updates
- multi-project setups
i) Adding Migrations
dotnet ef migrations add InitialCreateii) Updating the Database
dotnet ef database updateiii) Multi-Project Architectures
In Clean Architecture setups, migrations often live in the Infrastructure project while the API acts as the startup project.
Example:
dotnet ef migrations add InitialCreate \
--project src/InventoryManagement.Infrastructure \
--startup-project src/InventoryManagement.Apiiv) Listing Migrations
dotnet ef migrations listv) Removing Migrations
dotnet ef migrations removevi) Generating SQL Scripts
dotnet ef migrations scriptThis becomes very useful for:
- production deployments
- DBA reviews
- migration auditing
vii) Important Realization
Eventually I understood: Read more about
EF Core tooling makes much more sense once you understand project boundaries.
Read more about EF Core on my previous article: Link
The confusion often comes from architecture, not the CLI itself.
8. Build, Clean, and Debugging Workflows
Some of the most common commands eventually become muscle memory.
i) Building Projects
dotnet buildii) Cleaning Build Artifacts
dotnet cleaniii) Running Tests
dotnet testiv) Restoring Packages
dotnet restorev) Why These Commands Matter
One thing I appreciated compared to some ecosystems is how unified the tooling feels.
The same CLI handles:
- project creation
- dependency management
- testing
- builds
- publishing
- migrations
- configuration
This consistency improves developer ergonomics significantly over time.
vi) IDE Integration
Another important realization is that tools like:
- Visual Studio
- Rider
- VS Code
are heavily powered by the same CLI underneath.
Learning the CLI directly makes developers much more independent from the IDE.
9. Practical Commands I Kept in Notepad
During my transition into .NET, I constantly saved some of useful commands mentioned above because I kept forgetting them. At the time, these felt like random scattered commands.
Looking back, they were actually part of learning the operational side of backend engineering.
Eventually, backend development becomes more than writing application code.
You also start learning:
- infrastructure
- databases
- migrations
- deployment workflows
- developer tooling
- environment management
- automation
10. Conclusion: The CLI Eventually Becomes a Superpower
Transitioning from my previous Spring Boot background to ASP.NET Core initially felt overwhelming because the ecosystem exposed many concepts explicitly.
At first, this felt like complexity.
Over time, however, I realized it was actually flexibility.
The .NET CLI eventually becomes one of the most productive parts of the ecosystem because it provides a consistent interface for:
- creating applications
- managing dependencies
- configuring environments
- running migrations
- building projects
- debugging applications
- automating workflows
More importantly, learning the CLI teaches developers how the ecosystem actually works underneath the IDE. That understanding becomes incredibly valuable when:
- debugging build issues
- working in CI/CD pipelines
- scaling multi-project systems
- onboarding into enterprise codebases
- troubleshooting deployments
The learning curve is real.
But once the mental model clicks, the .NET developer experience becomes extremely powerful.
Share this article
If you found this post helpful, feel free to share it with your network!
