Developing APIs with a clean and maintainable structure is crucial for the long-term success of any software project. Clean Architecture is a popular approach that helps create scalable and maintainable systems. In this blog, we will explore how to develop APIs in ASP.NET Core using C# with Clean Architecture. We will provide a step-by-step guide and code examples to help you get started.
What is Clean Architecture?
Clean Architecture is a design pattern that emphasizes separation of concerns and independence from frameworks and technologies. It allows you to build systems that are easy to test, maintain, and evolve. The key idea is to organize your code into layers, with each layer having a specific responsibility.
Layers in Clean Architecture
- Core (Entities and Use Cases): Contains the business logic and domain entities.
- Application (Service Interfaces and DTOs): Contains service interfaces and data transfer objects (DTOs).
- Infrastructure (Data Access and External Services): Contains implementations for data access and external services.
- Presentation (API Controllers): Contains the API controllers that handle HTTP requests.
Step-by-Step Solution
Step 1: Set Up Your ASP.NET Core Project
- Create a new ASP.NET Core Web API project.bashCopy code
dotnet new webapi -n CleanArchitectureExample cd CleanArchitectureExample
- Create the necessary folders for each layer:
- Core
- Application
- Infrastructure
- Presentation
Step 2: Define Entities in the Core Layer
Create a folder Core/Entities
and add a simple entity class, e.g., Product.cs
.
csharpCopy codenamespace CleanArchitectureExample.Core.Entities
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
Step 3: Define Use Cases in the Core Layer
Create a folder Core/Interfaces
and add an interface for your repository, e.g., IProductRepository.cs
.
csharpCopy codenamespace CleanArchitectureExample.Core.Interfaces
{
public interface IProductRepository
{
Task<IEnumerable<Product>> GetAllProductsAsync();
Task<Product> GetProductByIdAsync(int id);
Task AddProductAsync(Product product);
}
}
Step 4: Implement the Repository in the Infrastructure Layer
Create a folder Infrastructure/Data
and add a class that implements IProductRepository
, e.g., ProductRepository.cs
.
csharpCopy codeusing CleanArchitectureExample.Core.Entities;
using CleanArchitectureExample.Core.Interfaces;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace CleanArchitectureExample.Infrastructure.Data
{
public class ProductRepository : IProductRepository
{
private readonly List<Product> _products = new();
public Task<IEnumerable<Product>> GetAllProductsAsync()
{
return Task.FromResult<IEnumerable<Product>>(_products);
}
public Task<Product> GetProductByIdAsync(int id)
{
var product = _products.Find(p => p.Id == id);
return Task.FromResult(product);
}
public Task AddProductAsync(Product product)
{
_products.Add(product);
return Task.CompletedTask;
}
}
}
Step 5: Configure Dependency Injection in the Presentation Layer
In the Startup.cs
or Program.cs
, register the dependencies.
csharpCopy codeusing CleanArchitectureExample.Core.Interfaces;
using CleanArchitectureExample.Infrastructure.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddSingleton<IProductRepository, ProductRepository>();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 6: Create API Controllers in the Presentation Layer
Create a folder Presentation/Controllers
and add a controller, e.g., ProductsController.cs
.
csharpCopy codeusing CleanArchitectureExample.Core.Entities;
using CleanArchitectureExample.Core.Interfaces;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace CleanArchitectureExample.Presentation.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpGet]
public async Task<IEnumerable<Product>> Get()
{
return await _productRepository.GetAllProductsAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<Product>> Get(int id)
{
var product = await _productRepository.GetProductByIdAsync(id);
if (product == null)
{
return NotFound();
}
return product;
}
[HttpPost]
public async Task<ActionResult> Post([FromBody] Product product)
{
await _productRepository.AddProductAsync(product);
return CreatedAtAction(nameof(Get), new { id = product.Id }, product);
}
}
}
Conclusion
Clean Architecture helps you create maintainable and scalable APIs by separating concerns and organizing code into layers. By following this step-by-step guide, you can develop APIs in ASP.NET Core with C# that are easy to test, maintain, and evolve. Remember to keep learning and adapting to new practices to improve your code quality and architecture.