Clean Architecture vs my design
In strict Clean Architecture, dependencies point inward. Domain has no dependencies, Application depends on Domain, and Infrastructure depends on Application to implement its interfaces. API depends on Application and may reference Infrastructure for dependency injection configuration.
My project follows this dependency direction, but I simplified the architecture by allowing Application to use EF Core directly.
What each project is responsible for
| Project | Responsibilities |
|---|---|
Finance.Api | HTTP requests, controllers, authentication, middleware |
Finance.Application | Use cases, services, DTOs, interfaces, queries |
Finance.Domain | Core entities and data relationships |
Finance.Infrastructure | EF Core, PostgreSQL, entity configurations, migrations |
Side by side
| Strict Clean Architecture | My design | |
|---|---|---|
| Dependency direction | Inward | Inward |
| Domain depends on | Nothing | Nothing |
| Application depends on | Domain | Domain and EF Core |
| Interface in Application | Hides the persistence framework | IFinanceDbContext exposes DbSet<T> |
| Queries live in | Infrastructure | Application services |
| Replacing EF Core changes | Infrastructure | Infrastructure and Application |
Where EF Core reaches Application
IFinanceDbContext is defined in Application and implemented by FinanceDbContext in Infrastructure, following the Dependency Inversion Principle. However, the interface exposes DbSet<T> for the entities described in Entity Relationships, so Application still depends on EF Core.
backend/src/ Finance.Application/ Common/ IFinanceDbContext.cs public interface IFinanceDbContext { DbSet<User> Users { get; } DbSet<Category> Categories { get; } DbSet<Expense> Expenses { get; } Task<int> SaveChangesAsync(CancellationToken cancellationToken = default); }
My services use EF Core methods such as AsNoTracking() and CountAsync().
backend/src/ Finance.Application/ Expenses/ ExpenseService.cs var expenses = db.Expenses.AsNoTracking().Where(e => e.UserId == userId); // ... var totalCount = await expenses.CountAsync(ct);
This means my Application layer doesn't depend on Infrastructure, but it does depend on EF Core.the any better