"Every enterprise project that starts as one big project ends up being rewritten. Clean Architecture is how you avoid the rewrite."
This is the first post in the ERP RFQ Module series. Before writing a single line of business logic, we set up the solution structure that makes everything else possible. Five projects. Five layers. Each with a clear job and clear boundaries.
This blog is part of the Green Concrete ERP project. To see the full walkthrough, architecture diagram, and download source code, visit the ERP Project Page →
Why 5 Projects — The Real Reason
A single ASP.NET project can build an entire ERP. But when the database changes, the UI breaks. When the AI service changes, the API breaks. Clean Architecture solves this by enforcing a dependency rule: outer layers depend on inner layers, never the reverse.
GREEN CONCRETE ERP — 5-LAYER STRUCTURE
GreenConcrete.Domain ← Entities, enums, state machine. ZERO dependencies.
↑
GreenConcrete.Application ← MediatR handlers, FluentValidation, interfaces.
↑ Depends only on Domain.
GreenConcrete.Infrastructure ← EF Core, Azure OpenAI, QuestPDF, MailKit.
↑ Implements interfaces defined in Application.
GreenConcrete.WebApi ← Thin controllers, JWT auth, Swagger.
Depends on Infrastructure + Application.
GreenConcrete.BlazorClient ← WASM UI, typed HttpClient, sessionStorage auth.
References only Application (DTOs, not Infrastructure).Setting Up the Solution — Exact Commands
mkdir GreenConcreteERP && cd GreenConcreteERP dotnet new sln -n GreenConcrete.ERP dotnet new classlib -n GreenConcrete.Domain -f net8.0 dotnet new classlib -n GreenConcrete.Application -f net8.0 dotnet new classlib -n GreenConcrete.Infrastructure -f net8.0 dotnet new webapi -n GreenConcrete.WebApi -f net8.0 dotnet new blazorwasm -n GreenConcrete.BlazorClient -f net8.0 dotnet sln add **/*.csproj # Wire project references: dotnet add GreenConcrete.Application reference GreenConcrete.Domain dotnet add GreenConcrete.Infrastructure reference GreenConcrete.Application dotnet add GreenConcrete.WebApi reference GreenConcrete.Infrastructure dotnet add GreenConcrete.WebApi reference GreenConcrete.Application dotnet add GreenConcrete.BlazorClient reference GreenConcrete.Application
Notice: BlazorClient references Application — NOT Infrastructure. This boundary keeps the UI clean. The Blazor app sees only DTOs and command/query objects, never EF Core or Azure OpenAI.
What Breaks If You Skip This
- Single project: Your Blazor page imports your EF Core DbContext directly. Change a database column and the UI component breaks.
- Two projects (API + UI): Better, but the API still mixes business logic with database access. Testing requires a real database.
- Five projects (Clean Architecture): Business logic is in Application, testable with mocked interfaces. Infrastructure can be swapped without changing a single handler.
🎯 Quick Check
Q1: Why does BlazorClient reference Application but NOT Infrastructure?
Show Answer
Because the UI should only see DTOs and command/query definitions. If it referenced Infrastructure, it would have access to EF Core DbContext, Azure OpenAI client, and other implementation details — breaking separation of concerns.
Q2: What is the dependency rule in Clean Architecture?
Show Answer
Outer layers depend on inner layers, never the reverse. Domain has zero dependencies. Application depends only on Domain. Infrastructure depends on Application. No inner layer ever references an outer layer.
Q3: If you switch from SQL Server to Cosmos DB, which layers change?
Show Answer
Only Infrastructure. You write a new repository implementation using the Cosmos DB SDK, implementing the same interfaces defined in Application. Domain, Application, WebApi, and BlazorClient do not change.
Key Takeaways
- Clean Architecture enforces a dependency rule: outer layers depend on inner layers, never the reverse
- Five projects: Domain (entities), Application (handlers), Infrastructure (implementations), WebApi (API), BlazorClient (UI)
- BlazorClient references only Application — it sees DTOs, never database models or service implementations
- Swapping any infrastructure component requires changes only in the Infrastructure layer
- The setup takes 5 minutes with dotnet CLI — the discipline pays back for the lifetime of the project