Simplified Clean Architecture - A Practical Approach
Simplified Clean Architecture: A Practical Approach
Introduction
Clean Architecture is a powerful design pattern that ensures maintainability, scalability, and separation of concerns in software projects. However, many developers find traditional Clean Architecture implementations overly complex, with numerous layers and abstractions that may not be necessary for small to medium-sized applications.
This article presents a simplified approach to Clean Architecture that retains the core principles while reducing unnecessary complexity. The goal is to provide a structured, easy-to-follow framework that balances architecture best practices with simplicity.
Understanding the Key Layers
A Clean Architecture-based application is typically divided into four main layers:
1. Core Layer (Domain)
This layer represents the heart of the application, containing business logic, entities, and domain rules. It is completely independent of external dependencies.
Key Components:
- Entities: Core business objects such as
User,Post, orOrder. - Value Objects: Immutable objects that represent domain concepts like
EmailorMoney. - Interfaces: Contracts for repositories and domain services.
Example:
public class User
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public string Email { get; private set; }
public User(string name, string email)
{
Id = Guid.NewGuid();
Name = name;
Email = email;
}
}
2. Application Layer
This layer orchestrates business logic and acts as an intermediary between the Core and Infrastructure layers. It contains use cases, service implementations, and application-specific logic.
Key Components:
- Services: Contain use case logic.
- Interfaces: Define contracts for application services and repositories.
Example:
public interface IUserService
{
Task<Guid> CreateUserAsync(string name, string email);
Task<User> GetUserByIdAsync(Guid id);
}
3. Infrastructure Layer
This layer provides implementations for external dependencies such as databases, external APIs, and file systems. It depends on both the Core and Application layers.
Key Components:
- Repositories: Implement data persistence logic.
- Services: Handle external operations like sending emails or processing payments.
Example:
public class UserRepository : IUserRepository
{
private readonly AppDbContext _context;
public UserRepository(AppDbContext context)
{
_context = context;
}
public async Task AddAsync(User user)
{
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
}
}
4. Web API (Presentation) Layer
This layer exposes the application to external clients via HTTP endpoints. It depends on the Application layer and acts as a communication bridge.
Key Components:
- Controllers: Handle HTTP requests and responses.
- DTOs (Data Transfer Objects): Shape data for API consumption.
Example:
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserDto dto)
{
var userId = await _userService.CreateUserAsync(dto.Name, dto.Email);
return Ok(new { Id = userId });
}
}
Project File Structure
A well-organized project structure is crucial for maintainability. Below is the suggested file structure:
/src
├── Core
│ ├── Domain
│ │ ├── Entities
│ │ ├── Interfaces
├── Application
│ ├── Services
│ ├── Interfaces
├── Infrastructure
│ ├── Data
│ ├── Repositories
├── WebAPI
│ ├── Controllers
│ ├── Models
/tests
├── UnitTests
Running the Project
To set up the project, follow these steps:
-
Install dependencies:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.AspNetCore.Mvc -
Configure Dependency Injection in
Program.cs:builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); -
Run the application:
dotnet run --project WebAPI
Final Thoughts
Clean Architecture does not have to be complex. By following this simplified approach, you can still reap the benefits of a maintainable and scalable application without unnecessary complexity. Start with a minimal structure, and expand only when needed.
Would you like to see a sample project with this structure? Let me know in the comments!