Clean Architecture Overview
In Clean Architecture, the system is divided into layers, each with distinct responsibilities:
- Core Domain (Entities and Interfaces)
- Contains the core business logic and entities.
- Independent of other layers, making it highly reusable and testable.
- Application Layer (Services, DTOs, Use Cases)
- Contains business logic and use cases that coordinate actions on entities.
- Infrastructure Layer (Database and External Services)
- Handles data access (e.g., EF Core) and any external services.
- Presentation Layer (UI)
- In this case, the Blazor components that interact with the user.
Let's break this can be done in Visual Studio 2022.
Step 1: Set Up the Solution Structure
- Open Visual Studio 2022 and Create a New Project.
- Choose Blank Solution and name it
InventoryManagementSolution. - Add the Projects:
- Right-click the solution in Solution Explorer and select Add > New Project.
- Core Domain: Add a Class Library named
InventoryManagement.Domain. - Application Layer: Add a Class Library named
InventoryManagement.Application. - Infrastructure Layer: Add a Class Library named
InventoryManagement.Infrastructure. - Presentation Layer: Add a Blazor Server App named
InventoryManagement.Web.
Note: Project Dependencies need to be configured.
Project Reference Dependencies
- Domain Layer (
InventoryManagement.Domain):
- This project is independent and should not reference any other projects. It contains the core entities and interfaces.
- Application Layer (
InventoryManagement.Application):
- This layer depends on the
Domainlayer for accessing the core entities and interfaces. - Add Reference to Domain:
- Right-click on the
InventoryManagement.Applicationproject. - Select Add > Project Reference.
- Check the box for
InventoryManagement.Domain. - Click OK.
- Infrastructure Layer (
InventoryManagement.Infrastructure):
- This layer needs to interact with the
Domainlayer to implement repositories, and it might need theApplicationlayer if certain application services are involved. - Add Reference to Domain:
- Right-click on the
InventoryManagement.Infrastructureproject. - Select Add > Project Reference.
- Check the box for
InventoryManagement.Domain. - Click OK.
- Presentation Layer (
InventoryManagement.Web):
- This layer, which is the Blazor project, needs to interact with
Applicationfor business logic and theInfrastructurefor data access (dependency injection and services). - Add References to Application and Infrastructure:
- Right-click on the
InventoryManagement.Webproject. - Select Add > Project Reference.
- Check the boxes for both
InventoryManagement.ApplicationandInventoryManagement.Infrastructure. - Click OK.
Final Dependency Flow
The dependencies between the projects should be as follows:
InventoryManagement.Domain: No references to other projects.InventoryManagement.Application: ReferencesInventoryManagement.Domain.InventoryManagement.Infrastructure: ReferencesInventoryManagement.Domain.InventoryManagement.Web: ReferencesInventoryManagement.ApplicationandInventoryManagement.Infrastructure.
Visual Representation
InventoryManagement.Domain <-- (No dependencies)
↑ ↑
│ │
InventoryManagement.Application
↑
InventoryManagement.Infrastructure
↑
InventoryManagement.Web
This structure maintains the principles of Clean Architecture:
- Domain is at the core, and every other layer depends on it.
- Application uses Domain entities and coordinates the use cases.
- Infrastructure implements data access for the Domain entities.
- Presentation interacts with the Application layer, which handles the business logic.
By keeping the references in this structure, the dependencies flow inward (towards the core) and follow the Clean Architecture approach, ensuring a clear separation of concerns and making the solution easier to maintain and extend in the future.
Step 2: Define Each Layer
2.1 Core Domain (Entities and Interfaces)
Project: InventoryManagement.Domain
- Add Entity Classes:
- Create a class named
Cleat.cs:
namespace InventoryManagement.Domain.Entities
{
public class Cleat
{
public int Id { get; set; }
public string Name { get; set; }
public string Brand { get; set; } = "Adidas";
public double MenSize { get; set; }
public double WomenSize { get; set; }
public int StockQuantity { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
}
}
- Define Repository Interface:
- Create an interface named
ICleatRepository.cs:
namespace InventoryManagement.Domain.Interfaces
{
public interface ICleatRepository
{
Task<IEnumerable<Cleat>> GetAllCleatsAsync();
Task<Cleat> GetCleatByIdAsync(int id);
Task AddCleatAsync(Cleat cleat);
Task UpdateCleatAsync(Cleat cleat);
Task DeleteCleatAsync(int id);
}
}
2.2 Application Layer (Services, DTOs, Use Cases)
Project: InventoryManagement.Application
- Add Services:
- Create a class named
CleatService.csto encapsulate the use cases.
using InventoryManagement.Domain.Entities;
using InventoryManagement.Domain.Interfaces;
namespace InventoryManagement.Application.Services
{
public class CleatService
{
private readonly ICleatRepository _cleatRepository;
public CleatService(ICleatRepository cleatRepository)
{
_cleatRepository = cleatRepository;
}
public async Task<IEnumerable<Cleat>> GetAllCleatsAsync()
{
return await _cleatRepository.GetAllCleatsAsync();
}
public async Task AddCleatAsync(Cleat cleat)
{
await _cleatRepository.AddCleatAsync(cleat);
}
// Additional methods for Update, Delete, etc.
}
}
2.3 Infrastructure Layer (EF Core and Database)
Project: InventoryManagement.Infrastructure
- Add Dependencies:
- Install
Microsoft.EntityFrameworkCore.SqlServerandMicrosoft.EntityFrameworkCore.Tools. - Don't forget to install
Microsoft.EntityFrameworkCore.Design . - Implement Repository:
- Create a class named
CleatRepository.csthat implementsICleatRepository:
using InventoryManagement.Domain.Entities;
using InventoryManagement.Domain.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace InventoryManagement.Infrastructure.Repositories
{
public class CleatRepository : ICleatRepository
{
private readonly InventoryContext _context;
public CleatRepository(InventoryContext context)
{
_context = context;
}
public async Task<IEnumerable<Cleat>> GetAllCleatsAsync()
{
return await _context.Cleats.ToListAsync();
}
public async Task<Cleat> GetCleatByIdAsync(int id)
{
return await _context.Cleats.FindAsync(id);
}
public async Task AddCleatAsync(Cleat cleat)
{
_context.Cleats.Add(cleat);
await _context.SaveChangesAsync();
}
public async Task UpdateCleatAsync(Cleat cleat)
{
_context.Entry(cleat).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
public async Task DeleteCleatAsync(int id)
{
var cleat = await _context.Cleats.FindAsync(id);
if (cleat != null)
{
_context.Cleats.Remove(cleat);
await _context.SaveChangesAsync();
}
}
}
}
- Define the
DbContext: - Add a class named
InventoryContext.cs:
using InventoryManagement.Domain.Entities;
using Microsoft.EntityFrameworkCore;
namespace InventoryManagement.Infrastructure
{
public class InventoryContext : DbContext
{
public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { }
public DbSet<Cleat> Cleats { get; set; }
}
}
2.4 Presentation Layer (Blazor UI)
Project: InventoryManagement.Web
- Add Service Registration:
- In
Program.cs, register the services:
using InventoryManagement.Application.Services; using InventoryManagement.Domain.Interfaces; using InventoryManagement.Infrastructure; using InventoryManagement.Infrastructure.Repositories; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args);
// Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor();
// Register DbContext and Repository
builder.Services.AddDbContext<InventoryContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<ICleatRepository, CleatRepository>();
builder.Services.AddScoped<CleatService>();
var app = builder.Build();
```
- Add Razor Components:
- Create a Razor component named
CleatsList.razorto list cleats:
@page "/cleats"
@inject CleatService CleatService
<h3>Cleats Inventory</h3>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Gender</th>
<th>Size</th>
<th>Stock Quantity</th>
<th>Price</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@foreach (var cleat in Cleats)
{
<tr>
<td>@cleat.Name</td>
<td>@cleat.Gender</td>
<td>@cleat.Size</td>
<td>@cleat.StockQuantity</td>
<td>@cleat.Price.ToString("C")</td>
<td>@cleat.Description</td>
</tr>
}
</tbody>
</table>
@code {
private List<Cleat> Cleats = new List<Cleat>();
protected override async Task OnInitializedAsync()
{
Cleats = (await CleatService.GetAllCleatsAsync()).ToList();
}
}
Step 3: Test and Deploy
- Run the Application:
- Press F5 to run the solution. (Before we run, we need to setup database. Let's do that in Part 2)
- Navigate to
/cleatsto see the inventory.
Summary
- Domain Layer (
InventoryManagement.Domain): - Holds entities (
Cleat) and interfaces (ICleatRepository). - Application Layer (
InventoryManagement.Application): - Contains use cases and services (
CleatService). - Infrastructure Layer (
InventoryManagement.Infrastructure): - Contains the EF Core DbContext and implements data access (
CleatRepository). - Presentation Layer (
InventoryManagement.Web): - Blazor project for user interaction.
This architecture ensures separation of concerns, improves testability, and makes it easier to scale and maintain your application.
