DEV Community

Cristian Sifuentes
Cristian Sifuentes

Posted on

C# Architecture Mastery — Architecture Smells in ASP.NET Core (Part 5)

C# Architecture Mastery — Architecture Smells in ASP.NET Core (Part 5)

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();
}
Enter fullscreen mode Exit fullscreen mode

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() { }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

EF Core belongs only in Infrastructure.

The application layer should depend on:

IOrderRepository
Enter fullscreen mode Exit fullscreen mode

Not:

DbSet<Order>
Enter fullscreen mode Exit fullscreen mode

7. Smell Detection Checklist (Use in Code Reviews)

Ask these questions:

  1. Does this controller contain logic?
  2. Does any service feel "too important"?
  3. Is DbContext visible outside Infrastructure?
  4. Do business rules depend on EF features?
  5. 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)