DEV Community

Cover image for Creating a RESTful API with .NET and Clean Architecture: A Complete Guide ๐Ÿš€
0x2e73
0x2e73

Posted on • Edited 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 (6)

Collapse
 
arashzandi profile image
Arash zandi

May you publish your codes to the github ?

Collapse
 
0x2e73 profile image
0x2e73

github.com/mou-inoks/clean-archite...

here, there is a bit more then in this guide.

Collapse
 
arashzandi profile image
Arash zandi

That deserves more than 10K github stars.๐ŸŽ‰โค๏ธ๐Ÿ™๐Ÿ‘

Thread Thread
 
0x2e73 profile image
0x2e73

thank you very much for your comments appriciate it

Collapse
 
arashzandi profile image
Arash zandi

Thank you very much ๐Ÿ™

Collapse
 
0x2e73 profile image
0x2e73

Yes, no problem, ill make a github repo and come back to you this afternoon ;)