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
| OCR In ASP.NET | Onion Architecture in .NET | |
Clean Architecture |
Clean Architecture is a software design philosophy that organizes code into distinct layers, ensuring that business rules and domain logic are kept independent of external concerns like databases, user interfaces (UI), frameworks, or external services. It was popularized by Robert C. Martin ("Uncle Bob") and emphasizes the separation of concerns to produce applications that are more maintainable, scalable, and testable.
The central rule of Clean Architecture is the Dependency Rule, which dictates that source code dependencies must always point inward. Code in an inner layer (the core business logic) cannot know anything about the outer layers (implementation details). This is typically achieved through dependency inversion, where inner layers define interfaces that outer layers implement.
The canonical structure of Clean Architecture consists of concentric circles or layers, with each having a specific responsibility.
This example outlines the project structure and components for a Clean Architecture Web API using C#.
Contains the core Product entity with business rules.
// Entities/Product.cs
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public Product(string name, decimal price, int stock)
{
// Add domain validation here
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be null or empty.", nameof(name));
if (price <= 0)
throw new ArgumentException("Price must be positive.", nameof(price));
if (stock < 0)
throw new ArgumentException("Stock cannot be negative.", nameof(stock));
Name = name;
Price = price;
Stock = stock;
}
}
Contains the use cases, interfaces, and data transfer objects (DTOs). It references only the Domain layer.
// Interfaces/IProductRepository.cs
public interface IProductRepository
{
Task<Product> GetByIdAsync(int id);
Task AddAsync(Product product);
Task UpdateAsync(Product product);
}
// UseCases/CreateProductUseCase.cs (or Command/Query pattern with MediatR)
public class CreateProductUseCase
{
private readonly IProductRepository _repository;
public CreateProductUseCase(IProductRepository repository)
{
_repository = repository;
}
public async Task ExecuteAsync(string name, decimal price, int stock)
{
var product = new Product(name, price, stock);
await _repository.AddAsync(product);
}
}
Implements the repository interface using EF Core. It references the Application and Domain layers.
// Persistence/ApplicationDbContext.cs
public class ApplicationDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
// ...
}
// Persistence/ProductRepository.cs
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
public ProductRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(int id)
{
return await _context.Products.FindAsync(id);
}
// ... Implement other methods
}
The entry point of the application, typically an ASP.NET Core Web API controller. It references all other layers to resolve dependencies.
// Controllers/ProductController.cs
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
private readonly CreateProductUseCase _createProductUseCase;
public ProductsController(CreateProductUseCase createProductUseCase)
{
_createProductUseCase = createProductUseCase;
}
[HttpPost]
public async Task<IActionResult&ht; CreateProduct([FromBody] CreateProductDto dto)
{
await _createProductUseCase.ExecuteAsync(dto.Name, dto.Price, dto.Stock);
return CreatedAtAction(nameof(GetProductById), new { id = ... }, null);
}
}
| OCR In ASP.NET | Onion Architecture in .NET | |