C# Architecture Mastery — Architecture Smells in ASP.NET Core (Part 5)
Most ASP.NET Core projects compile.
Many even work.
But under the surface, they slowly rot due to architecture smells — patterns that feel productive early and become catastrophic later.
In this Part 5, we’ll dissect the three most common architecture smells seen in real-world ASP.NET Core systems:
Fat Controllers · God Services · DbContext Leaks
These are not style issues.
They are scalability and correctness problems.
1. What an Architecture Smell Really Is
An architecture smell is:
A structural warning sign that the system will resist change, testing, or scaling.
Smells are dangerous because:
- They feel “reasonable” at first
- They often pass code review
- They grow silently
Clean Architecture is largely about eliminating smells before they spread.
2. Fat Controllers — The Most Common Smell
The Smell
Controllers that:
- Contain business rules
- Orchestrate workflows
- Talk directly to the database
- Perform validation, mapping, and logic
// ❌ Fat controller
[HttpPost]
public async Task<IActionResult> Create(OrderDto dto)
{
if (dto.Total <= 0)
return BadRequest();
var entity = new Order { Total = dto.Total };
_db.Orders.Add(entity);
await _db.SaveChangesAsync();
_email.SendConfirmation(entity);
return Ok();
}
Why It’s Dangerous
- Impossible to unit test
- Logic duplicated across endpoints
- Controllers become unmaintainable
The Fix
Controllers should:
- Accept input
- Call a use case
- Return output
Nothing else.
3. God Services — The Silent Monolith
The Smell
A single service that:
- Knows everything
- Does everything
- Depends on everything
// ❌ God service
class OrderService
{
void CreateOrder() { }
void CancelOrder() { }
void RefundOrder() { }
void GenerateInvoice() { }
void SendEmail() { }
}
Why It’s Dangerous
- Violates SRP
- Becomes impossible to refactor
- Any change risks breaking everything
God services are where Clean Architecture goes to die.
The Fix
Split by business capability, not technical layers.
4. DbContext Leaks — The Architecture Killer
The Smell
DbContext injected directly into:
- Controllers
- Domain services
- Application services
// ❌ DbContext leak
class OrdersController
{
private readonly AppDbContext _db;
}
Why It’s Dangerous
- Infrastructure leaks into core logic
- EF Core becomes a hard dependency
- Business rules become persistence-aware
At this point, you don’t have Clean Architecture.
You have EF-driven design.
5. Why DbContext Leaks Are So Harmful
They:
- Destroy testability
- Prevent database replacement
- Tie logic to EF behavior
- Encourage query-driven business rules
Once DbContext leaks inward, architecture collapse accelerates.
6. The Correct Boundary
Application → Interfaces → Infrastructure
EF Core belongs only in Infrastructure.
The application layer should depend on:
IOrderRepository
Not:
DbSet<Order>
7. Smell Detection Checklist (Use in Code Reviews)
Ask these questions:
- Does this controller contain logic?
- Does any service feel "too important"?
- Is DbContext visible outside Infrastructure?
- Do business rules depend on EF features?
- Would this be testable without ASP.NET Core?
If yes — you’ve found a smell.
8. Smells Are Architectural Debt
Unlike technical debt:
- Smells compound
- Smells spread
- Smells infect new code
Ignoring them early guarantees pain later.
9. Clean Architecture Is Mostly Subtraction
Great architecture is not about adding layers.
It’s about removing responsibilities from the wrong places.
Thin controllers.
Focused services.
Isolated infrastructure.
Final Thoughts
If your ASP.NET Core app has:
- Fat controllers
- God services
- DbContext leaks
It may work today.
But it will fight you tomorrow.
Recognizing these smells early is one of the strongest signals of senior engineering maturity.
✍️ Written by Cristian Sifuentes — helping teams identify architectural decay before it becomes irreversible.

Top comments (0)