DEV Community

Cover image for Creating a RESTful API with .NET and Clean Architecture: A Complete Guide 🚀
0x2e73
0x2e73

Posted on

Creating a RESTful API with .NET and Clean Architecture: A Complete Guide 🚀

Hey there! 👋 Are you tired of messy code shit architectures? Well, let’s fix that! In this post, we’ll walk through creating a RESTful API using Clean Architecture with .NET 8. By the end, you’ll have a modular, scalable, and super clean API, which means less stress for you and more time to enjoy your coffee ☕. Let’s dive in!

Wtf is clean architecture ?

You might be wondering, "Why should I bother with Clean Architecture? Isn’t my code already fine?" 🤔 Well, no, your code is bad, but its not important, mine is not better. Clean Architecture helps separate concerns into distinct layers, making your code more maintainable, testable, and future-proof. Plus, it gives you that “I’ve got everything under control” feeling. 😉

Clean Architecture is like a neat apartment: when it’s organized, you can find anything easily. When it’s messy? You end up searching for your keys for 20 minutes (we've all been there 🙈).

The Layers of Clean Architecture

Here’s a quick rundown of the layers in Clean Architecture:

  1. Core: Where the magic happens – your entities and interfaces.
  2. Application: This layer handles business logic (services, use cases), talking to Core through interfaces.
  3. Infrastructure: The concrete stuff – think databases, third-party APIs, and services.
  4. WebApi: The shiny API layer that interacts with the outside world (a.k.a. your clients).

Image description

Steps to Create the API 🚧

lets code !

Creating the Project

First, let’s start by setting up the foundation (i.e. the cool house we’re building 🏠). Open up a terminal and create the projects:

dotnet new sln -n MyCleanApi
dotnet new classlib -n MyCleanApi.Core
dotnet new classlib -n MyCleanApi.Application
dotnet new classlib -n MyCleanApi.Infrastructure
dotnet new webapi -n MyCleanApi.WebApi
dotnet sln MyCleanApi.sln add MyCleanApi.Core/MyCleanApi.Core.csproj
dotnet sln MyCleanApi.sln add MyCleanApi.Application/MyCleanApi.Application.csproj
dotnet sln MyCleanApi.sln add MyCleanApi.Infrastructure/MyCleanApi.Infrastructure.csproj
dotnet sln MyCleanApi.sln add MyCleanApi.WebApi/MyCleanApi.WebApi.csproj
Enter fullscreen mode Exit fullscreen mode

This will create four projects:

  • Core: The brains 💡 of your app (entities and interfaces).
  • Application: Where the business logic happens (the magic ✨).
  • Infrastructure: The hands-on stuff (database, external APIs).
  • WebApi: The gateway to your API (like the doorman at a fancy restaurant 🏙️).

Define the Entities (Core) 🏢

In Core, define a simple Product entity (because who doesn’t love products, right? 😄).

namespace MyCleanApi.Core.Entities
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode

Define Repository Interfaces (Core) 📚

In Core, define the repository interface to manage your Products. This will act like the API to interact with your data.

namespace MyCleanApi.Core.Interfaces
{
    public interface IProductRepository
    {
        Task<IEnumerable<Product>> GetAllAsync();
        Task<Product> GetByIdAsync(int id);
        Task AddAsync(Product product);
        Task UpdateAsync(Product product);
        Task DeleteAsync(int id);
    }
}
Enter fullscreen mode Exit fullscreen mode

Create the Services (Application) 💼

In Application, create the service that will handle your business logic. This is the brains behind the operation. No stress, it’s like a superhero team!

namespace MyCleanApi.Application.Services
{
    public class ProductService
    {
        private readonly IProductRepository _productRepository;

        public ProductService(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        public async Task<IEnumerable<Product>> GetAllProductsAsync()
        {
            return await _productRepository.GetAllAsync();
        }

        public async Task<Product> GetProductByIdAsync(int id)
        {
            return await _productRepository.GetByIdAsync(id);
        }

        public async Task AddProductAsync(Product product)
        {
            await _productRepository.AddAsync(product);
        }

        public async Task UpdateProductAsync(Product product)
        {
            await _productRepository.UpdateAsync(product);
        }

        public async Task DeleteProductAsync(int id)
        {
            await _productRepository.DeleteAsync(id);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Implement the Repository (Infrastructure) 🔧

In Infrastructure, implement the repository using Entity Framework (because EF is like the trusty sidekick of your app 🦸‍♂️).

namespace MyCleanApi.Infrastructure.Repositories
{
    public class ProductRepository : IProductRepository
    {
        private readonly AppDbContext _context;

        public ProductRepository(AppDbContext context)
        {
            _context = context;
        }

        public async Task<IEnumerable<Product>> GetAllAsync()
        {
            return await _context.Products.ToListAsync();
        }

        public async Task<Product> GetByIdAsync(int id)
        {
            return await _context.Products.FindAsync(id);
        }

        public async Task AddAsync(Product product)
        {
            await _context.Products.AddAsync(product);
            await _context.SaveChangesAsync();
        }

        public async Task UpdateAsync(Product product)
        {
            _context.Products.Update(product);
            await _context.SaveChangesAsync();
        }

        public async Task DeleteAsync(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product != null)
            {
                _context.Products.Remove(product);
                await _context.SaveChangesAsync();
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Set Up the Database Context (Infrastructure) 🏗️

Now, let’s make sure the database can handle the Products we’re going to throw at it (it’s tough, don’t worry 😆).

namespace MyCleanApi.Infrastructure
{
    public class AppDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    }
}

Enter fullscreen mode Exit fullscreen mode

Set Up Dependency Injection (WebApi) 🤖

In WebApi, we need to configure dependency injection so everything talks to each other (because even APIs need friends 🫶).

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<ProductService>();
Enter fullscreen mode Exit fullscreen mode

Create the API Endpoints (WebApi) 📡

Now, let’s expose the API! This is where you handle HTTP requests. We’ll set up the basic CRUD operations for Product.

namespace MyCleanApi.WebApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductController : ControllerBase
    {
        private readonly ProductService _productService;

        public ProductController(ProductService productService)
        {
            _productService = productService;
        }

        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            var products = await _productService.GetAllProductsAsync();
            return Ok(products);
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> GetById(int id)
        {
            var product = await _productService.GetProductByIdAsync(id);
            if (product == null) return NotFound();
            return Ok(product);
        }

        [HttpPost]
        public async Task<IActionResult> Create([FromBody] Product product)
        {
            await _productService.AddProductAsync(product);
            return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
        }

        [HttpPut("{id}")]
        public async Task<IActionResult> Update(int id, [FromBody] Product product)
        {
            product.Id = id;
            await _productService.UpdateProductAsync(product);
            return NoContent();
        }

        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(int id)
        {
            await _productService.DeleteProductAsync(id);
            return NoContent();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing and Running the API 🎯

Don’t forget to test everything! You can use Postman or any HTTP client to test your API. And remember: always check your database (it’s like checking if your fridge is full before cooking 😅).

Conclusion 🎉

Boom! You’ve now built a RESTful API with .NET and Clean Architecture. Your code is organized, scalable, and ready for future upgrades (like adding a robot army… just kidding… or not 🤖).

Let me know how it goes, or if you have any questions. Happy coding, and may your API requests always return 200 OK! 💻✨

Top comments (0)