IConfiguration vs IOptions NET
Synchronous and Asynchronous in .NET Core
Model Binding and Validation in ASP.NET Core
ControllerBase vs Controller in ASP.NET Core
ConfigureServices and Configure methods
IHostedService interface in .NET Core
ASP.NET Core request processing
| Clean Architecture | Hexagonal-Architecture | |
Onion Architecture |
Onion Architecture is a software design pattern that emphasizes the principle of Separation of Concerns by organizing an application into a series of concentric layers. Introduced by Jeffrey Palermo, its core principle, similar to Clean Architecture, is the Dependency Inversion Principle: dependencies flow inwards towards the core business logic.
π‘ This design ensures that the most critical part of your applicationβthe domain model and business rulesβis independent of external concerns like databases, frameworks, or user interfaces.
The typical Onion Architecture, when applied in .NET Core, is divided into the following layers:
The solution is typically broken down into multiple projects to enforce the dependency rules.
IProductRepository interface and the UseCases folder with CreateProductUseCase.ApplicationDbContext and ProductRepository implementation.ProductsController.This class holds the core business logic and entities, independent of any framework or database.
// ProductManagement.Domain/Entities/Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; private set; }
public decimal Price { get; private set; }
// Domain validation and business rules are encapsulated here
public Product(string name, decimal price)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Product name cannot be empty.", nameof(name));
if (price <= 0)
throw new ArgumentException("Price must be greater than zero.", nameof(price));
Name = name;
Price = price;
}
}
This defines the contract for data access, which is part of the application's core logic but doesn't care about the implementation.
// ProductManagement.Application/Interfaces/IProductRepository.cs
public interface IProductRepository
{
Task AddProductAsync(Product product);
Task<Product> GetProductByIdAsync(int id);
}
This project, which has references to both ProductManagement.Application and ProductManagement.Domain, provides the database-specific implementation of the repository.
// ProductManagement.Infrastructure/Persistence/ProductRepository.cs
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _dbContext;
public ProductRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task AddProductAsync(Product product)
{
_dbContext.Products.Add(product);
await _dbContext.SaveChangesAsync();
}
public async Task<Product> GetProductByIdAsync(int id)
{
return await _dbContext.Products.FindAsync(id);
}
}
The Web API controller depends on the Application layer to access the use cases, and dependency injection resolves the Infrastructure layer's implementation at runtime.
// ProductManagement.Api/Controllers/ProductsController.cs
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _productRepository;
public ProductsController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
[HttpPost]
public async Task<IActionResult> CreateProduct([FromBody] CreateProductDto dto)
{
var product = new Product(dto.Name, dto.Price);
await _productRepository.AddProductAsync(product);
return Ok(product);
}
}
| Clean Architecture | Hexagonal-Architecture | |