In this article, we will create two microservices using .NET Core 6 Web API and Containerization that services using Docker and connect them using Ocelot API Gateway.

Agenda

Prerequisites:

Introduction of Microservice

There are two types of architecture used for design applications: the first is monolithic, and the second one is microservices.

Monolithic Architecture

Microservice Architecture

Introduction of Ocelot API Gateway

Step-by-step implementation of microservices

Let’s start.

Step 1

Create a Blank Empty solution using a visual studio.

Step 2

Configure your project.

Step 3

Project Structure:

Step 4

Create a new folder inside the solution and name it Microservices. Next, create ProductMicroservice and UserMicroservice projects inside that.

Step 5

Configure the ProductMicroservice project and path of service.

Step 6

Provide additional information related to the project.

Step 7

Next, create the UserMicroservice project inside the Microservices folder and follow the same steps as we used to create ProductMicroservice.

Step 8

Create an Ocelot API Gateway web API project outside the Microservices folder.

Let’s start the implementation of ProductMicroservice

Project Structure

Step 1

Install the following NuGet packages.

Step 2

Create Product Class inside the Model folder.

namespace ProductMicroservice.Model
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public string ProductDescription { get; set; }
        public int ProductPrice { get; set; }
        public int ProductStock { get; set; }
    }
}

Step 3

Next, Create DbContextClass inside the Data folder.

using Microsoft.EntityFrameworkCore;
using ProductMicroservice.Model;

namespace ProductMicroservice.Data
{
    public class DbContextClass : DbContext
    {
        protected readonly IConfiguration Configuration;

        public DbContextClass(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
        }

        public DbSet<Product> Products { get; set; }
    }
}

Step 4

After that, create IProductService and ProductService inside the services folder.

IProductService

using ProductMicroservice.Model;

namespace ProductMicroservice.Services.Interface
{
    public interface IProductService
    {
        public IEnumerable<Product> GetProductList();
        public Product GetProductById(int id);
        public Product AddProduct(Product product);
        public Product UpdateProduct(Product product);
        public bool DeleteProduct(int Id);
    }
}

ProductService

using ProductMicroservice.Data;
using ProductMicroservice.Model;
using ProductMicroservice.Services.Interface;

namespace ProductMicroservice.Services
{
    public class ProductService : IProductService
    {
        private readonly DbContextClass _dbContext;

        public ProductService(DbContextClass dbContext)
        {
            _dbContext = dbContext;
        }
        public IEnumerable<Product> GetProductList()
        {
            return _dbContext.Products.ToList();
        }
        public Product GetProductById(int id)
        {
            return _dbContext.Products.Where(x => x.ProductId == id).FirstOrDefault();
        }

        public Product AddProduct(Product product)
        {
            var result = _dbContext.Products.Add(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }

        public Product UpdateProduct(Product product)
        {
            var result = _dbContext.Products.Update(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }
        public bool DeleteProduct(int Id)
        {
            var filteredData = _dbContext.Products.Where(x => x.ProductId == Id).FirstOrDefault();
            var result = _dbContext.Remove(filteredData);
            _dbContext.SaveChanges();
            return result != null ? true : false;
        }
    }
}

Step 5

Configure the connection string inside the appsetting.json file.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP;Initial Catalog=ProductServiceDB;User Id=sa;Password=****@1;"
  }
}

Step 6

Later on, configure a few services inside the Program.cs class.

using ProductMicroservice.Data;
using ProductMicroservice.Services;
using ProductMicroservice.Services.Interface;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped < IProductService, ProductService > ();
builder.Services.AddDbContext < DbContextClass > ();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 7

Next, Create a ProductsController.cs.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ProductMicroservice.Model;
using ProductMicroservice.Services.Interface;

namespace ProductMicroservice.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly IProductService productService;
        public ProductsController(IProductService _productService)
        {
            productService = _productService;
        }
        [HttpGet]
        public IEnumerable<Product> ProductList()
        {
            var productList = productService.GetProductList();
            return productList;

        }
        [HttpGet("{id}")]
        public Product GetProductById(int id)
        {
            return productService.GetProductById(id);
        }
        [HttpPost]
        public Product AddProduct(Product product)
        {
            return productService.AddProduct(product);
        }
        [HttpPut]
        public Product UpdateProduct(Product product)
        {
            return productService.UpdateProduct(product);
        }
        [HttpDelete("{id}")]
        public bool DeleteProduct(int id)
        {
            return productService.DeleteProduct(id);
        }
    }
}

Step 8

Finally, execute the following command in the package manager console to create migration and create the database.

add-migration "Initial"
update-database

Implementation of UserMicroservice service

Project Structure

Step 1

Install the following NuGet packages:

Step 2

Create a User Class inside the Model folder.

namespace UserMicroservice.Model
{
    public class User
    {
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Address { get; set; }
    }
}

Step 3

Next, Create DbContextClass inside the Data folder.

using Microsoft.EntityFrameworkCore;
using UserMicroservice.Model;

namespace UserMicroservice.Data
{
    public class DbContextClass : DbContext
    {
        protected readonly IConfiguration Configuration;

        public DbContextClass(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder options)
        {
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
        }

        public DbSet<User> Users { get; set; }
    }
}

Step 4

After that, create IUserService and UserService inside the services folder.

IUserService

using UserMicroservice.Model;

namespace UserMicroservice.Services.Interface
{
    public interface IUserService
    {
        public IEnumerable<User> GetUserList();
        public User GetUserById(int id);
        public User AddUser(User product);
        public User UpdateUser(User product);
        public bool DeleteUser(int Id);
    }
}

UserService

using UserMicroservice.Data;
using UserMicroservice.Model;
using UserMicroservice.Services.Interface;

namespace UserMicroservice.Services
{
    public class UserService : IUserService
    {
        private readonly DbContextClass _dbContext;

        public UserService(DbContextClass dbContext)
        {
            _dbContext = dbContext;
        }

        public User AddUser(User product)
        {
            var result = _dbContext.Users.Add(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }

        public bool DeleteUser(int Id)
        {
            var filteredData = _dbContext.Users.Where(x => x.UserId == Id).FirstOrDefault();
            var result = _dbContext.Remove(filteredData);
            _dbContext.SaveChanges();
            return result != null ? true : false;
        }

        public User GetUserById(int id)
        {
            return _dbContext.Users.Where(x => x.UserId == id).FirstOrDefault();
        }

        public IEnumerable<User> GetUserList()
        {
            return _dbContext.Users.ToList();
        }

        public User UpdateUser(User product)
        {
            var result = _dbContext.Users.Update(product);
            _dbContext.SaveChanges();
            return result.Entity;
        }
    }
}

Step 5

Configure the connection string inside the appsetting.json file.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=DESKTOP;Initial Catalog=UserServiceDB;User Id=sa;Password=****@1;"
  }
}

Step 6

Later on, configure a few services inside Program.cs class.

using UserMicroservice.Data;
using UserMicroservice.Services;
using UserMicroservice.Services.Interface;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped < IUserService, UserService > ();
builder.Services.AddDbContext < DbContextClass > ();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 7

Next, create a UsersController.cs.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using UserMicroservice.Model;
using UserMicroservice.Services.Interface;

namespace UserMicroservice.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UsersController : ControllerBase
    {
        private readonly IUserService userService;
        public UsersController(IUserService _userService)
        {
            userService = _userService;
        }

        [HttpGet]
        public IEnumerable<User> UserList()
        {
            var userList = userService.GetUserList();
            return userList;
        }

        [HttpGet("{id}")]
        public User GetUserById(int id)
        {
            return userService.GetUserById(id);
        }

        [HttpPost]
        public User AddUser(User user)
        {
            return userService.AddUser(user);
        }

        [HttpPut]
        public User UpdateUser(User user)
        {
            return userService.UpdateUser(user);
        }

        [HttpDelete("{id}")]
        public bool DeleteUser(int id)
        {
            return userService.DeleteUser(id);
        }
    }
}

Step 8

Finally, execute the following command in the package manager console to create a migration, and create the database.

add-migration "Initial"
update-database

Containerization of Product and User Service using Docker

Step 1

Create a Docker file for ProductMicroservice inside the root folder.

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app

EXPOSE 443
EXPOSE 80 

# copy project csproj file and restore it in docker directory
COPY ./*.csproj ./
RUN dotnet restore

# Copy everything into the docker directory and build
COPY . .
RUN dotnet publish -c Release -o out

# Build runtime final image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "ProductMicroservice.dll"]

Step 2

Next, create a Docker file for UserMicroservice inside the root folder.

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /app

EXPOSE 443
EXPOSE 80 

# copy project csproj file and restore it in docker directory
COPY ./*.csproj ./
RUN dotnet restore

# Copy everything into the docker directory and build
COPY . .
RUN dotnet publish -c Release -o out

# Build runtime final image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
WORKDIR /app
COPY --from=build /app/out .
ENTRYPOINT ["dotnet", "UserMicroservice.dll"]

Step 3

Create a docker-compose.yml file inside the main solution folder.

version: '3.5'
services:
  ProductService:
   image: ${DOCKER_REGISTRY-}producstmicroservice:v1
   build:
    context: ./ProductService
    dockerfile: Dockerfile
   environment: 
    - CONNECTIONSTRINGS__DEFAULTCONNECTION=Data Source=192.168.3.6,1433;Initial Catalog=ProductServiceDB;User Id=*****;Password=******
   ports:
    - "4201:80"
  UserService:
   image: ${DOCKER_REGISTRY-}usersmicroservice:v1
   build:
    context: ./UserService
    dockerfile: Dockerfile
   environment: 
    - CONNECTIONSTRINGS__DEFAULTCONNECTION=Data Source=192.168.3.6,1433;Initial Catalog=UserServiceDB;User Id=******;Password=******
   ports:
    - "4202:80"

Step 4

Open CMD inside the main solution folder where the docker-compose.yml file is present and execute the following command.

docker-compose build
docker-compose up

Step 5

Open Docker Desktop and you can see inside that there are two images created and is in running mode.

Step 6

Open the Container section and there you will see your two images running inside a container with their individual port number.

Step 7

Use the following URLs to check your microservices functionality which is running inside docker container.

Product Service

http://localhost:4201/swagger/index.html

User Service

http://localhost:4202/swagger/index.html

Implementation of Ocelot API Gateway

Step 1

Install the Ocelot NuGet Package.

Step 2

Project Structure:

Step 3

Create an ocelot.json file for routing.

{
  "GlobalConfiguration": {
    "BaseUrl": "https://localhost:7062"
  },
  "Routes": [
    {
      "UpstreamPathTemplate": "/gateway/product",
      "UpstreamHttpMethod": [ "Get" ],
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4201
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/gateway/product/{id}",
      "UpstreamHttpMethod": [ "Get", "Delete" ],
      "DownstreamPathTemplate": "/api/products/{id}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4201
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/gateway/product",
      "UpstreamHttpMethod": [ "Post", "Put" ],
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4201
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/gateway/user",
      "UpstreamHttpMethod": [ "Get" ],
      "DownstreamPathTemplate": "/api/users",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4202
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/gateway/user/{id}",
      "UpstreamHttpMethod": [ "Get", "Delete" ],
      "DownstreamPathTemplate": "/api/users/{id}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4202
        }
      ]
    },
    {
      "UpstreamPathTemplate": "/gateway/user",
      "UpstreamHttpMethod": [ "Post", "Put" ],
      "DownstreamPathTemplate": "/api/users",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 4202
        }
      ]
    }
  ]
}

Step 4

Configure the ocelot.json file inside Program.cs class and register services related to that.

using Ocelot.DependencyInjection;
using Ocelot.Middleware;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddCors(options => {
    options.AddPolicy("CORSPolicy", builder => builder.AllowAnyMethod().AllowAnyHeader().AllowCredentials().SetIsOriginAllowed((hosts) => true));
});
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot(builder.Configuration);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseCors("CORSPolicy");
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
await app.UseOcelot();
app.Run();

Step 5

Right-click on the solution and open the property. Then, apply to start on API Gateway projects that we have created.

Step 6

After running your Ocelot API Gateway project, you will see this swagger UI:

Step 7

Open the Postman and check the functionality.

Conclusion

In this article, we discussed the working of microservice architecture and Ocelot API gateway using .NET Core 6 API and containerization of applications using Docker.

Happy coding!