RESTful WebAPI With Onion Architecture

In this article, you will learn about RESTful WebAPI with Onion Architecture.

Hello friends, here I will show you how to create a WebApi with the following characteristics:

  • ASP.Core 2.1
  • EntityFramework
  • FluentValidation
  • Nlogger
  • Swagger
  • Jwt

Let's start. First create an empty project, then add the following folders:

  1. Application
  2. Domain
  3. Service
  4. Infrastructure

Then in the Domain folder, we create a library project Net.Core 2.1 with the name WebApi.Domain add the following dependencies

  • FluentValidation.AspNetCore

In this project, we add the following folders:

  • Dtos
  • Entities
  • Interfaces

In the Entities folder, we create the BaseEntity class:

  1. namespace WebApi.Domain.Entities  
  2. {  
  3.     public abstract class BaseEntity  
  4.     {  
  5.         public virtual int Id { getset; }  
  6.     }  
  7. }  

Our project classes will inherit the field Id from this abstract class (if you want you can add other fields like CreatedAt or CreatedBy).

Then we create the Country class with the properties that defines a Country.

  1. namespace WebApi.Domain.Entities  
  2. {  
  3.     public class Country : BaseEntity  
  4.     {  
  5.         public string Name { getset; }  
  6.         public int Population { getset; }  
  7.         public decimal Area { getset; }  
  8.         public string ISO3166 { getset; }  
  9.         public string DrivingSide { getset; }  
  10.         public string Capital { getset; }  
  11.   
  12.     }  
  13. }  

Now to make the exercise more interesting, we are going to assume that we do not want to expose all the Country class. In the Dtos folder, we create the following CountryDensityDTO class.

  1. using System;  
  2. using WebApi.Domain.Entities;  
  3.   
  4. namespace WebApi.Domain.Dtos  
  5. {  
  6.     public class CountryDensityDTO : BaseEntity  
  7.     {  
  8.         public string Name { getset; }  
  9.         public string Capital { getset; }  
  10.         public decimal Area { getset; }  
  11.         public int Population { getset; }  
  12.   
  13.   
  14.         public int Populationdensity  
  15.         {  
  16.             get  
  17.             {  
  18.                 return Decimal.ToInt32(Population / Area);  
  19.             }  
  20.         }  
  21.     }  
  22. }  

This class exposes Name, Capital Area, Population and a calculated field Populationdensity.

Now we will continue with the Infrastructure layer and then we will finish the missing parts.

We go to the Infrastructure folder and create a library Net.Core 2.1. We name it WebApi.Infrastructure.Data.

We add the following Packages:

  • Microsoft.EntityFrameworkCore.SqlServer 2.1.4
  • Microsoft.EntityFrameworkCore.Tools 2.1.4
  • Microsoft.Extensions.Identity.Stores 2.1.1
  • Microsoft.VisualStudio.Web.CodeGeneration.Design 2.1.5
  • Add Project reference WebApi.Domain 

We create the following folders:

  • Context
  • EntityDbMapping
  • Repository

In the Context Folder, we add the SqlServerContext class.

We refer to our Country entity with DbSet to work with the database. 

As we work with CodeFirst approach, we will create a mapping for our entity Country in the database.

"modelBuilder.Entity<Country>(new CountryMap().Configure);"

Optionally, in this part we can also add seed data when creating a table.

  1. using Microsoft.AspNetCore.Identity.EntityFrameworkCore;  
  2. using Microsoft.EntityFrameworkCore;  
  3. using WebApi.Domain.Entities;  
  4. using WebApi.Infrastructure.Data.EntityDbMapping;  
  5.   
  6. namespace WebApi.Infrastructure.Data.Context  
  7. {  
  8.     public class SqlServerContext :   IdentityDbContext<ApplicationUser>  
  9.     {  
  10.        
  11.         public DbSet<Country> Country { getset; }  
  12.   
  13.         public SqlServerContext(DbContextOptions<SqlServerContext> options) : base(options)  
  14.         {  
  15.            
  16.         }      
  17.         protected override void OnModelCreating(ModelBuilder modelBuilder)  
  18.         {  
  19.             base.OnModelCreating(modelBuilder);  
  20.             modelBuilder.Entity<Country>(new CountryMap().Configure);  
  21.             // ModelBuilderExtensions.Seed(modelBuilder);  
  22.   
  23.         }  
  24.     }  
  25.     //Data for first time on table  
  26.     public static class ModelBuilderExtensions  
  27.     {  
  28.         public static void Seed(this ModelBuilder modelBuilder)  
  29.         {  
  30.             modelBuilder.Entity<Country>().HasData(  
  31.                 new Country  
  32.                 {  
  33.                     Id = 1,  
  34.                     Name = "Venezuela",  
  35.                     Population = 300000000,  
  36.                     Area = 230103  
  37.                    
  38.                 },  
  39.                 new Country  
  40.                 {  
  41.                     Id = 2,  
  42.                     Name = "Peru",  
  43.                     Population = 260000000,  
  44.                     Area =33249                
  45.                 }  
  46.             );  
  47.         }  
  48.     }  
  49. }  

In the folder, EntityDbMapping, we create the CountryMap class. In this class, we define the physical representation of the properties of the Country class as fields in the table of the database.

  1. using Microsoft.EntityFrameworkCore;    
  2. using Microsoft.EntityFrameworkCore.Metadata.Builders;    
  3. using WebApi.Domain.Entities;    
  4.     
  5. namespace WebApi.Infrastructure.Data.EntityDbMapping    
  6. {    
  7.     public class CountryMap : IEntityTypeConfiguration<Country>    
  8.     {    
  9.         public void Configure(EntityTypeBuilder<Country> builder)    
  10.         {    
  11.             builder.ToTable("Country");    
  12.     
  13.             builder.HasKey(c => c.Id);    
  14.     
  15.             builder.Property(c => c.Name)    
  16.                 .IsRequired()    
  17.                 .HasColumnName("Name")    
  18.                 .HasColumnType("varchar(150)");    
  19.     
  20.             builder.Property(c => c.Population)    
  21.                 .IsRequired()    
  22.                 .HasColumnType("int")    
  23.                 .HasColumnName("Population");    
  24.     
  25.             builder.Property(c => c.Area)    
  26.                 .IsRequired()    
  27.                 .HasColumnType("decimal(14,2)")    
  28.                 .HasColumnName("Area");    
  29.     
  30.             builder.Property(c => c.ISO3166)    
  31.             .IsRequired()    
  32.             .HasColumnType("varchar(3)")    
  33.             .HasColumnName("ISO3166");    
  34.     
  35.             builder.Property(c => c.DrivingSide)    
  36.             .IsRequired()    
  37.             .HasColumnType("varchar(50)")    
  38.             .HasColumnName("DrivingSide");    
  39.     
  40.             builder.Property(c => c.Capital)    
  41.             .IsRequired()    
  42.             .HasColumnType("varchar(50)")    
  43.             .HasColumnName("Capital");    
  44.         }    
  45.     
  46.     }    
  47. }    

This is it for now.

In the next chapter, we will implement validations with FluentValidation. We will also configure Mapper to use it with our DTOs and will implement Identity using Jwt.

You can download code or clone in here.