Interview frequency: …
Short answer
Split the solution into four projects: Domain (entities and pure business logic), Application (use cases and interfaces), Infrastructure (databases and APIs), and WebApi (Controllers), with dependencies always pointing inward.
Explanation
The main goal is to isolate core business logic from changeable technical details. The Domain project depends on no other projects and installs no external packages. Application defines use cases through MediatR interfaces. Infrastructure depends on Application to implement its interfaces (such as EF Core DbContext), and WebApi connects everything through Dependency Injection.
Code example
// Shop.Domain (references nothing)
public class Order { public Guid Id { get; init; } public decimal Total { get; private set; } }
// Shop.Application → Domain
public interface IOrderRepository { Task<Order?> GetAsync(Guid id, CancellationToken ct); }
public class GetOrderHandler(IOrderRepository orders) { /* use case */ }
// Shop.Infrastructure → Application
public class EfOrderRepository(AppDbContext db) : IOrderRepository
{
public Task<Order?> GetAsync(Guid id, CancellationToken ct) => db.Orders.FindAsync([id], ct).AsTask();
}
// Shop.WebApi → Application + Infrastructure: the composition root connects the arrows
builder.Services.AddScoped<IOrderRepository, EfOrderRepository>();Common mistakes
- Adding an EF Core or ASP.NET Core reference to the Domain project, contaminating pure business logic.