DEV Community

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

Posted on • Edited on

24 2 2 2 3

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! 💻✨

Image of Datadog

The Future of AI, LLMs, and Observability on Google Cloud

Datadog sat down with Google’s Director of AI to discuss the current and future states of AI, ML, and LLMs on Google Cloud. Discover 7 key insights for technical leaders, covering everything from upskilling teams to observability best practices

Learn More

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 ;)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up