ASP.NET Core  

Microservices with ASP.NET Core: Step-by-Step Implementation

Introduction

Modern applications are becoming increasingly large, complex, and interconnected. In such scenarios, microservices architecture offers a scalable, maintainable, and flexible approach compared to traditional monolithic systems.

In this article, we’ll explore how to design and implement microservices using ASP.NET Core, covering step-by-step setup, communication, data management, and deployment strategies.

Why Microservices?

Before diving into the implementation, let’s understand why microservices are important:

  • Scalability: Each microservice can be scaled independently based on load.

  • Isolation: Fault in one service doesn’t affect others.

  • Technology Freedom: Different teams can use different technologies.

  • Faster Deployment: Smaller codebases mean faster CI/CD cycles.

  • Maintainability: Each service is easier to understand and modify.

Microservices vs Monolithic Architecture

FeatureMonolithicMicroservices
DeploymentSingle deployment for the whole appIndependent deployment for each service
ScalabilityScale entire appScale only needed services
CodebaseSingle large projectMultiple smaller services
Fault IsolationHarderEasier
DatabaseSharedSeparate per service

Step 1: Designing the Microservice Architecture

A typical ASP.NET Core microservices setup may look like this:

  1. API Gateway – Entry point that routes requests to the correct microservice.

  2. Microservices – Each handling a specific domain (e.g., Orders, Inventory, Users).

  3. Database per service – Each microservice manages its own schema.

  4. Communication – Services communicate via REST APIs or message queues.

  5. Configuration & Discovery – Managed through tools like Consul or Ocelot.

Technical Workflow (Flowchart)

         +---------------------+
         |     Client App      |
         +---------+-----------+
                   |
                   v
         +---------------------+
         |     API Gateway     |
         +---------+-----------+
                   |
   +---------------+---------------+
   |                               |
   v                               v
+--------+                   +------------+
| Orders |                   | Inventory  |
|Service |                   | Service    |
+--------+                   +------------+
   |                               |
   v                               v
+---------+                   +-----------+
| SQL DB  |                   | SQL DB    |
+---------+                   +-----------+

Step 2: Creating a Solution Structure

Create a Visual Studio solution (or use CLI):

dotnet new sln -n MicroservicesDemo
mkdir Services
cd Services
dotnet new webapi -n OrderService
dotnet new webapi -n InventoryService
dotnet new webapi -n UserService
dotnet new webapi -n APIGateway

Then, add each project to the solution:

dotnet sln add ./Services/OrderService/OrderService.csproj
dotnet sln add ./Services/InventoryService/InventoryService.csproj
dotnet sln add ./Services/UserService/UserService.csproj
dotnet sln add ./Services/APIGateway/APIGateway.csproj

Step 3: Implementing a Microservice (Example: OrderService)

Startup.cs or Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<OrderDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("OrderDB")));
var app = builder.Build();
app.MapControllers();
app.Run();

OrderController.cs

[ApiController]
[Route("api/[controller]")]
public class OrderController : ControllerBase
{
    private readonly OrderDbContext _context;
    public OrderController(OrderDbContext context)
    {
        _context = context;
    }

    [HttpGet]
    public IActionResult GetOrders() => Ok(_context.Orders.ToList());

    [HttpPost]
    public IActionResult CreateOrder([FromBody] Order order)
    {
        _context.Orders.Add(order);
        _context.SaveChanges();
        return Ok(order);
    }
}

Order Model

public class Order
{
    public int Id { get; set; }
    public string CustomerName { get; set; } = string.Empty;
    public decimal TotalAmount { get; set; }
    public DateTime OrderDate { get; set; } = DateTime.UtcNow;
}

Step 4: Adding the API Gateway

Use Ocelot as a simple API Gateway.

Install Ocelot:

dotnet add package Ocelot

Add a configuration file ocelot.json:

{"Routes": [
    {
      "DownstreamPathTemplate": "/api/order",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        { "Host": "localhost", "Port": 5001 }
      ],
      "UpstreamPathTemplate": "/order",
      "UpstreamHttpMethod": [ "GET", "POST" ]
    }],"GlobalConfiguration": { }}

Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();
var app = builder.Build();
await app.UseOcelot();
app.Run();

Now, hitting https://localhost:7000/order routes to the OrderService.

Step 5: Service-to-Service Communication

To communicate between services, you can use HttpClient or message queues like RabbitMQ.

Example using HttpClient (InventoryService → OrderService):

public class OrderApiClient
{
    private readonly HttpClient _client;
    public OrderApiClient(HttpClient client)
    {
        _client = client;
        _client.BaseAddress = new Uri("https://localhost:5001/api/order/");
    }

    public async Task<List<Order>> GetOrdersAsync()
    {
        var response = await _client.GetAsync("");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<List<Order>>();
    }
}

Step 6: Database Per Service

Each service has its own SQL Server database.

Example

  • OrderDB for OrderService

  • InventoryDB for InventoryService

  • UserDB for UserService

This separation avoids schema conflicts and allows independent scaling.

Step 7: Logging and Monitoring

For centralized monitoring:

  • Use Serilog + Seq or Elastic Stack (ELK)

  • Log all requests/responses per service

  • Include correlation IDs for tracking cross-service requests

Example: Serilog Configuration

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

Step 8: CI/CD Pipeline (Jenkins Example)

Each microservice is deployed independently.

Jenkinsfile Example:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                bat 'dotnet build ./Services/OrderService/OrderService.csproj -c Release'
            }
        }
        stage('Publish') {
            steps {
                bat 'dotnet publish ./Services/OrderService -o ./publish/OrderService'
            }
        }
        stage('Deploy') {
            steps {
                bat 'xcopy /Y /E ./publish/OrderService C:\\inetpub\\OrderService'
                bat 'iisreset'
            }
        }
    }
}

Step 9: Scaling and Load Balancing

To achieve zero downtime and scalability:

  • Host each service under IIS or Docker.

  • Use Application Request Routing (ARR) for load balancing.

  • Use horizontal scaling (multiple instances per service).

Step 10: Versioning and Backward Compatibility

Always maintain backward compatibility:

  • Use API versioning attributes like [ApiVersion("1.0")].

  • Maintain multiple versions in parallel during rollout.

Best Practices

  1. Keep each service small and focused.

  2. Never share databases between services.

  3. Implement retry logic for inter-service calls.

  4. Secure communication with JWT tokens or OAuth2.

  5. Automate builds and deployments.

  6. Use centralized logging and distributed tracing (e.g., OpenTelemetry).

Real-World Example Scenario

Imagine an E-Commerce platform:

  • OrderService handles orders.

  • InventoryService manages stock.

  • UserService manages users and authentication.

  • PaymentService processes transactions.

  • API Gateway routes requests to respective services.

Each service can be deployed, scaled, or updated independently.

Summary

In this article, we explored how to implement microservices using ASP.NET Core step-by-step.
We learned about architecture design, API Gateway setup, service communication, database per service, CI/CD pipeline, and best practices.

Microservices give you freedom, scalability, and resilience, but they also bring complexity — so use them wisely where they truly fit your system’s needs.