A Single-Project Monolith with Vertical Slice Architecture combines two ideas that reinforce each other. The monolith keeps the whole application in one .NET 10 project, so there is a single assembly, a single dependency injection container, and a single deployable unit. Vertical Slice Architecture organizes that one project by business feature instead of by technical layer, so each feature — create an invoice, approve a leave request, register a customer — owns everything it needs in one folder.
Together they solve the two complaints developers have about both camps: monoliths feel tangled because they are organized by layer, and vertical slice examples feel unrealistic because they still assume a multi-project solution. This article shows the combined structure in concrete .NET 10 terms.
Why a Single-Project Monolith Pairs Naturally with VSA
VSA removes the reason most people create extra projects in the first place. The usual argument for a Domain project and an Application project is to keep layers from leaking into each other. But a layer is a compile-time concept, and the real unit of change is the feature. Once you organize by feature, the technical layers still exist as folders and namespaces — models, commands, handlers — but they sit inside the feature that owns them. You get the same separation of concerns with a fraction of the ceremony, and you never pay the multi-project tax of fifteen csproj files.
Single-Project Monolith Folder Layout: Features, Not Layers
The folder layout is the architecture. Under the root of the project, a Features folder holds one folder per business capability, and shared technical concerns live in separate clearly named areas:
src/MyApp/
Features/
Invoices/
ApproveInvoiceCommand.cs
ApproveInvoiceHandler.cs
InvoiceDto.cs
Leave/
ApproveLeaveCommand.cs
ApproveLeaveHandler.cs
Customers/
RegisterCustomerCommand.cs
RegisterCustomerHandler.cs
CustomerDto.cs
Infrastructure/
Data/AppDbContext.cs
Auth/IdentityConfig.cs
Audit/AuditInterceptor.cs
Shared/
Result.cs
BaseEntity.cs
Program.cs
Three rules keep this honest. First, a feature never reaches into another feature's folder for business logic; cross-feature needs go through a public interface or a shared command. Second, Infrastructure is for genuinely reusable technical services, not a dumping ground. Third, the folder names speak the business language, so the codebase reads like a product manual rather than a compiler diagram.
One Slice, One File, One Responsibility
In .NET 10, a slice can be a single file using MediatR. The command, its validation, and its handler belong together because they change together:
public record ApproveInvoiceCommand(Guid InvoiceId) : IRequest<Result>;
public sealed class ApproveInvoiceValidator
: AbstractValidator<ApproveInvoiceCommand>
{
public ApproveInvoiceValidator()
{
RuleFor(x => x.InvoiceId).NotEmpty();
}
}
public sealed class ApproveInvoiceHandler
: IRequestHandler<ApproveInvoiceCommand, Result>
{
readonly AppDbContext _db;
readonly ICurrentUser _user;
public ApproveInvoiceHandler(AppDbContext db, ICurrentUser user)
=> (_db, _user) = (db, user);
public async Task<Result> Handle(
ApproveInvoiceCommand cmd, CancellationToken ct)
{
var invoice = await _db.Invoices
.FirstAsync(i => i.Id == cmd.InvoiceId, ct);
invoice.Approve(_user.Name);
await _db.SaveChangesAsync(ct);
return Result.Success();
}
}
Everything the feature does is in front of you. When a new developer asks what approving an invoice involves, the answer is one file. When the business changes the approval rule, you edit one file, and the test for that file is the only test that needs to change. That is the cohesion a single-project monolith plus VSA delivers.
Shared Infrastructure Still Has a Home
Putting features in folders does not mean abandoning shared code. EF Core's DbContext, the audit interceptor, the JWT issuer, and the file storage abstraction are genuinely cross-cutting, and they belong in Infrastructure and Shared folders with namespaces that make their role obvious. The rule is that a slice consumes shared services but never owns them, and shared code must not contain business rules for any single feature. When a feature needs to read audit history or write a file, it references a Shared interface; the implementation stays in one place and changes once for every feature at the same time.
This is where the single-project monolith quietly outperforms both extremes. A multi-project solution needs a separate assembly for every shared concern, which multiplies the reference graph. A poorly organized monolith hides shared code inside features, which creates hidden coupling. The VSA monolith avoids both by giving shared code an explicit, non-feature home — the folder layout makes the boundary visible, and the compiler enforces what the layout declares.
Enforcing Boundaries Inside a Single-Project Monolith
With one project, discipline has to come from somewhere. Namespaces mirror the folder layout — MyApp.Features.Invoices, MyApp.Shared — so a code review can spot a cross-feature dependency at a glance. Analyzers can flag feature-to-feature references, and integration tests can assert the dependency graph stays acyclic. EF Core configuration is centralized in Infrastructure while each slice owns only its own query and mutation logic, so the monolith stays modular enough to extract a slice later if it ever genuinely needs to.
Key Takeaways
- A Single-Project Monolith gives you one assembly, one DI container, and one deployable unit.
- Vertical Slice Architecture organizes that project by feature folders instead of technical layers.
- Each slice — command, validation, handler — lives together and changes together in one file.
- Namespaces, analyzers, and tests replace project references as the boundary enforcement.
- Every Indotalent product is a Single-Project Monolith built with VSA — complete .NET 10 source code for $21 each.
FAQ
Is VSA only for single-project monoliths? No, but the single-project monolith is where VSA is strongest. Without project boundaries, VSA's folders and namespaces carry the entire responsibility for cohesion, which is exactly where they excel.
How is this different from clean architecture? Clean architecture draws boundaries between technical layers across projects; VSA draws boundaries between business features inside one project. The two can coexist, but for most applications feature-first plus one project is simpler and faster to deliver.
Do I still need MediatR? Not strictly. MediatR is convenient because it lets each slice self-register via assembly scanning and keeps handlers testable in isolation. If you prefer plain classes with explicit wiring, the folder structure works the same way.
How do I stop features from depending on each other? Expose shared behavior through interfaces or a small Shared folder, and let analyzers flag feature-to-feature references. This keeps the graph acyclic and preserves the option to extract a slice later.