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.

Strict Clean Architecture API Infrastructure implementations Application defines interfaces no EF Core types EF Core Domain depends on nothing DI My design API Infrastructure FinanceDbContext Application IFinanceDbContext exposes DbSet<T> EF Core Domain depends on nothing DI each arrow points at what a project depends on
Fig. 01  ·  Same layers, one extra dependency

What each project is responsible for

ProjectResponsibilities
Finance.ApiHTTP requests, controllers, authentication, middleware
Finance.ApplicationUse cases, services, DTOs, interfaces, queries
Finance.DomainCore entities and data relationships
Finance.InfrastructureEF Core, PostgreSQL, entity configurations, migrations

Side by side

Strict Clean ArchitectureMy design
Dependency directionInwardInward
Domain depends onNothingNothing
Application depends onDomainDomain and EF Core
Interface in ApplicationHides the persistence frameworkIFinanceDbContext exposes DbSet<T>
Queries live inInfrastructureApplication services
Replacing EF Core changesInfrastructureInfrastructure 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.cspublic 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.csvar 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

Next Dependency Injection