A good folder structure is the cheapest architecture decision you will ever make. In ASP.NET Core MVC, the default template separates code by technical kind — Controllers, Models, Views — which means a single feature is scattered across several folders. The ASP.NET Core MVC VSA folder structure solves this by grouping everything a feature needs into one folder. This article lays out the exact structure with a real Currency feature so you can copy it into your .NET 10 project.
The Complete ASP.NET Core MVC VSA Folder Structure
Here is the full layout for one feature inside an MVC application that uses Areas for admin domains:
App/
├── Areas/
│ └── Admin/
│ └── Currency/ # one feature = one slice
│ ├── Controllers/
│ │ └── CurrencyController.cs
│ ├── Cqrs/
│ │ ├── GetCurrencyListHandler.cs
│ │ ├── GetCurrencyByIdHandler.cs
│ │ ├── CreateCurrencyHandler.cs
│ │ ├── UpdateCurrencyHandler.cs
│ │ └── DeleteCurrencyHandler.cs
│ ├── Endpoints/
│ │ └── CurrencyEndpoint.cs
│ └── Views/
│ ├── Index.cshtml + Index.cshtml.js
│ ├── Create.cshtml + Create.cshtml.js
│ ├── Edit.cshtml + Edit.cshtml.js
│ └── Detail.cshtml + Detail.cshtml.js
├── wwwroot/
└── Program.cs
The same shape repeats for every feature: Areas/Admin/Currency, Areas/Admin/Tax, Areas/Admin/User. Because the structure is identical, onboarding a new developer means teaching one pattern and letting them apply it to every feature.
What Belongs in Each Subfolder
Each subfolder has one job:
- Controllers — the classic MVC entry point. Binds the request, calls a CQRS handler, and returns a
View(),RedirectToAction(), or JSON result. - Cqrs — read and write handlers. Queries return data using EF Core no-tracking reads; commands mutate data with full change tracking. Validators for commands live next to them.
- Endpoints — the REST surface for the same feature. Maps Minimal API endpoints so mobile and third-party clients can use the identical operations.
- Views — Razor pages for the feature plus their small
.cshtml.jsenhancement files, keeping client behavior adjacent to the server code.
A Command Handler and Its Validator
Commands inside the slice follow a uniform shape. The handler owns the business rule, and a validator guards the input:
public sealed class CreateCurrencyCommand
{
public string Code { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public decimal Rate { get; set; }
}
public sealed class CreateCurrencyHandler
{
private readonly AppDbContext _db;
public CreateCurrencyHandler(AppDbContext db) => _db = db;
public async Task<int> HandleAsync(CreateCurrencyCommand command)
{
var currency = new Currency
{
Code = command.Code,
Name = command.Name,
Rate = command.Rate
};
_db.Currencies.Add(currency);
await _db.SaveChangesAsync();
return currency.Id;
}
}
Validation can live in the same slice as a CreateCurrencyValidator using FluentValidation, so adding or changing a rule never requires leaving the feature folder. This mirrors the production layout inside the MVC EDevKit Basic source code.
The MVC View Pair: Index.cshtml and Index.cshtml.js
Views stay close to their behavior. The list page is plain Razor that renders a table, and the JavaScript enhancement lives in the matching .cshtml.js file:
// Areas/Admin/Currency/Views/Index.cshtml.js
document.addEventListener('DOMContentLoaded', function () {
if (window.dataTableInitialized) return;
window.dataTableInitialized = true;
const table = document.getElementById('currencyTable');
if (table) {
new DataTable('#currencyTable', {
serverSide: true,
ajax: { url: '/Admin/Currency/List', type: 'POST' }
});
}
});
Because the enhancement sits next to its view, a developer who owns the Currency feature never searches the entire project for where the table behavior is configured. That is the practical payoff of the ASP.NET Core MVC VSA folder structure.
Key Takeaways
- The ASP.NET Core MVC VSA folder structure keeps Controllers, Cqrs, Endpoints, and Views inside one feature folder
- Areas give you a natural domain boundary; each feature inside an area is a slice
- Handlers and validators live beside the controller that uses them
- Razor views pair with their
.cshtml.jsenhancement files - One uniform, repeatable structure makes onboarding and AI-assisted coding dramatically easier
FAQ
Where do shared services live in the MVC VSA folder structure? Shared infrastructure such as the DbContext, authentication, and logging lives outside the slices, usually in Infrastructure/ or Services/ at the project root. Slices consume them, they do not own them.
Should I use Areas or plain folders for MVC VSA? Both work. Areas add route and view-lookup conventions that suit large admin modules; plain feature folders are enough for smaller apps. The slice layout inside each is identical.
Can DataTables work with the MVC VSA folder structure? Yes. The DataTables server-side endpoint is just another file inside the slice — usually a controller action or the per-feature Endpoints file.
Does this structure scale to 100 features? Yes. Because every feature follows the same template, a 100-feature app is still just 100 copies of one well-understood pattern.