Blazor

Let's Create an Shoe Inventory Management System using Blazor. - Part 1

A customer wants help managing inventory of shoes. Blazor is a good alternative for this. We can also use Clean Architecture to make sure we can extend this project as needed.

Hugh Flanagan

Clean Architecture Overview

In Clean Architecture, the system is divided into layers, each with distinct responsibilities:

  1. Core Domain (Entities and Interfaces)
  • Contains the core business logic and entities.
  • Independent of other layers, making it highly reusable and testable.
  1. Application Layer (Services, DTOs, Use Cases)
  • Contains business logic and use cases that coordinate actions on entities.
  1. Infrastructure Layer (Database and External Services)
  • Handles data access (e.g., EF Core) and any external services.
  1. 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

  1. Open Visual Studio 2022 and Create a New Project.
  2. Choose Blank Solution and name it InventoryManagementSolution.
  3. 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

  1. Domain Layer (InventoryManagement.Domain):
  • This project is independent and should not reference any other projects. It contains the core entities and interfaces.
  1. Application Layer (InventoryManagement.Application):
  • This layer depends on the Domain layer for accessing the core entities and interfaces.
  • Add Reference to Domain:
  • Right-click on the InventoryManagement.Application project.
  • Select Add > Project Reference.
  • Check the box for InventoryManagement.Domain.
  • Click OK.
  1. Infrastructure Layer (InventoryManagement.Infrastructure):
  • This layer needs to interact with the Domain layer to implement repositories, and it might need the Application layer if certain application services are involved.
  • Add Reference to Domain:
  • Right-click on the InventoryManagement.Infrastructure project.
  • Select Add > Project Reference.
  • Check the box for InventoryManagement.Domain.
  • Click OK.
  1. Presentation Layer (InventoryManagement.Web):
  • This layer, which is the Blazor project, needs to interact with Application for business logic and the Infrastructure for data access (dependency injection and services).
  • Add References to Application and Infrastructure:
  • Right-click on the InventoryManagement.Web project.
  • Select Add > Project Reference.
  • Check the boxes for both InventoryManagement.Application and InventoryManagement.Infrastructure.
  • Click OK.

Final Dependency Flow

The dependencies between the projects should be as follows:

  • InventoryManagement.Domain: No references to other projects.
  • InventoryManagement.Application: References InventoryManagement.Domain.
  • InventoryManagement.Infrastructure: References InventoryManagement.Domain.
  • InventoryManagement.Web: References InventoryManagement.Application and InventoryManagement.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.cs to 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.SqlServer and Microsoft.EntityFrameworkCore.Tools.
  • Don't forget to install Microsoft.EntityFrameworkCore.Design .
  • Implement Repository:
  • Create a class named CleatRepository.cs that implements ICleatRepository:
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.razor to 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 /cleats to 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.


Become a member
Get the latest news right in your inbox. It's free and you can unsubscribe at any time. We hate spam as much as we do, so we never spam!
Read next

Let's Build a Blog Site with Blazor.

Building a personal blog is a great way to document projects, share insights, and showcase technical skills. Using Blazor Server offers a seamless development experience, allowing you to use C# for both front-end and back-end, while benefiting from server-side rendering and integrated security. In this post, we'll explore why Blazor Server is an ideal choice for building a blog site and how it can help you grow as a developer.

Hugh Flanagan Oct 11
An unhandled error has occurred. Reload 🗙