Clean Architecture for .NET and Angular Applications

Introduction

Clean Architecture is useful when an application needs to remain maintainable while its business rules, integrations, UI, and infrastructure continue to evolve.

For an ASP.NET Core and Angular application, the goal is not to create four projects simply because a diagram says so. The goal is to make dependencies point in predictable directions and keep business rules independent from frameworks and infrastructure.

A practical architecture can look like:

Angular
   ↓
ASP.NET Core API
   ↓
Application
   ↓
Domain
   ↑
Infrastructure

The exact project boundaries can vary, but the dependency direction is the important part.

The Dependency Rule

Core business rules should not depend directly on:

  • ASP.NET Core
  • EF Core
  • SQL Server
  • Angular
  • external APIs

Infrastructure depends on the core, not the other way around.

A typical .NET solution might contain:

src/
  Domain/
  Application/
  Infrastructure/
  WebApi/

Angular remains a separate frontend application.

Domain Layer

The Domain layer represents business concepts and rules.

For example:

public sealed class Order
{
    private readonly List<OrderItem> _items = [];

    public Guid Id { get; private set; }
    public OrderStatus Status { get; private set; }

    public IReadOnlyCollection<OrderItem> Items => _items;

    public void AddItem(Product product, int quantity)
    {
        if (quantity <= 0)
            throw new DomainException("Quantity must be greater than zero.");

        _items.Add(new OrderItem(product.Id, quantity));
    }
}

The domain should not need an EF Core DbContext to decide whether an order can accept an item.

Application Layer

The Application layer coordinates use cases.

A CQRS-style command might look like:

CreateOrderCommand
       ↓
Handler
       ↓
Domain Rules
       ↓
Repository / Unit of Work
       ↓
Persistence

MediatR can be used where request/handler separation adds value, but it is not a requirement for Clean Architecture.

The key is that application workflows depend on abstractions rather than concrete infrastructure.

Infrastructure Layer

Infrastructure contains implementations for concerns such as:

  • EF Core
  • repositories
  • external API clients
  • email
  • file storage
  • message brokers

For example:

public interface IOrderRepository
{
    Task<Order?> GetAsync(
        Guid id,
        CancellationToken cancellationToken);
}

Infrastructure can implement this interface using EF Core.

ASP.NET Core API Layer

The API layer translates HTTP requests into application operations and maps application results back to HTTP responses.

Avoid placing substantial business logic in controllers.

For larger applications, Minimal APIs, controllers, or endpoint frameworks can all work. Clean Architecture is about dependency boundaries, not a specific HTTP style.

Angular Architecture

Angular should have similar responsibility boundaries.

A practical frontend structure might be:

src/app/
  core/
  shared/
  features/
    orders/
      pages/
      components/
      services/
      state/
      models/

Feature code should own feature-specific behavior.

Keep reusable UI elements in shared, and application-wide infrastructure such as authentication or HTTP interceptors in core.

DTOs Across the Boundary

The backend should expose API contracts rather than domain entities.

For example:

public sealed record OrderResponse(
    Guid Id,
    decimal Total,
    string Status);

Angular models can represent the API contract without coupling the frontend to EF Core or domain implementation details.

Where Domain-Driven Design Fits

Domain-Driven Design can complement Clean Architecture when the business domain is sufficiently complex.

Useful DDD concepts include:

  • entities
  • value objects
  • aggregates
  • domain services
  • domain events
  • bounded contexts

Do not introduce every DDD pattern into a simple CRUD application.

Use the complexity of the domain to determine how much modeling is justified.

Dependency Injection

ASP.NET Core’s dependency injection system makes it straightforward to connect application abstractions with infrastructure implementations.

For example:

services.AddScoped<IOrderRepository, EfOrderRepository>();

The application depends on IOrderRepository, while infrastructure supplies the implementation.

Avoid the Generic Repository Trap

EF Core already provides a unit-of-work and repository-like abstraction through DbContext and DbSet.

Adding a generic repository around every table can obscure useful EF Core capabilities.

Repositories are most valuable when they represent meaningful domain/application access patterns rather than simply wrapping every CRUD method.

Testing

Clean Architecture can make testing easier because business rules can be tested without starting ASP.NET Core or connecting to SQL Server.

A useful test strategy includes:

  • domain unit tests
  • application/use-case tests
  • API integration tests
  • Angular component tests
  • end-to-end tests for critical workflows

The highest-value tests are those that protect meaningful behavior rather than implementation details.

Common Mistakes

Avoid:

  • putting business logic in controllers
  • letting domain code depend on EF Core
  • exposing EF entities as API contracts
  • creating repositories for every table without purpose
  • making every class an abstraction
  • moving simple logic into excessive layers
  • creating a shared “Common” project that becomes a dumping ground

Conclusion

Clean Architecture is best understood as a dependency-management strategy.

For ASP.NET Core and Angular applications, keep business rules independent, isolate infrastructure, expose deliberate API contracts, and organize Angular code around features.

The architecture should be proportional to the application’s complexity. The goal is not the maximum number of projects or abstractions. The goal is making important business behavior easier to change without creating unnecessary coupling.

Let's connect

Interested in discussing software architecture, engineering challenges or potential collaboration? Feel free to get in touch.

© 2026 Rajdip Khavad. All rights reserved.