Internship Preparation - CLEAN API Architecture
Building scalable APIs using Clean Architecture principles, layered design, and Entity Framework Core
Understanding layered architecture and separation of concerns
// Clean Architecture has 5 main layers:
// 1. Domain Layer (Core/Entities)
// - Business entities and domain logic
// - No dependencies on other layers
// - Pure C# classes
namespace MyApp.Domain.Entities
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public string Category { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
// Domain logic
public void UpdatePrice(decimal newPrice)
{
if (newPrice <= 0)
throw new ArgumentException("Price must be greater than zero");
Price = newPrice;
UpdatedAt = DateTime.UtcNow;
}
}
}
// 2. Application Layer (Use Cases/Business Logic)
// - Application-specific business rules
// - Defines interfaces for infrastructure and persistence
// - Orchestrates domain objects
namespace MyApp.Application.Interfaces
{
public interface IProductRepository
{
Task<Product?> GetByIdAsync(int id);
Task<IEnumerable<Product>> GetAllAsync();
Task<Product> AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(int id);
}
}
// 3. Infrastructure Layer
// - External concerns (email, file storage, third-party APIs)
// - Implements non-database interfaces from Application layer
// - Does NOT contain the database — that belongs in Persistence
namespace MyApp.Infrastructure.Services
{
public class EmailService : IEmailService
{
public async Task SendOrderConfirmationAsync(string to, int orderId)
{
// SendGrid, SMTP, or any email provider
}
}
}
// 4. Persistence Layer
// - Database only: EF Core DbContext, repositories, migrations
// - Implements repository interfaces defined in Application layer
// - Keeps EF Core completely out of Domain and Application
namespace MyApp.Persistence.Context
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(ApplicationDbContext).Assembly);
}
}
}
// 5. Presentation Layer (API/Controllers)
// - User interface / API endpoints
// - Depends on Application layer only
// - Handles HTTP requests/responses
namespace MyApp.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _repository;
public ProductsController(IProductRepository repository)
{
_repository = repository;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
{
var products = await _repository.GetAllAsync();
return Ok(products);
}
}
}💡 Explanation: Clean Architecture separates concerns into 5 layers. Domain has no dependencies. Application defines interfaces and use cases. Infrastructure handles external services (email, storage). Persistence handles database access (EF Core, repositories, migrations). Presentation (API) handles HTTP. Dependencies flow inward toward Domain.
Layered with dependency inversion
Large applications, long-term maintenance
Enterprise APIs, complex business logic
Traditional layered approach
Medium-sized applications
Standard CRUD applications
All code in one project
Small applications, prototypes
Simple APIs, learning projects