Skip to content

Programming a Microservice with .Net Core 3.1

In my last post, I talked about the theory of a microservice. Today it is going to be more practical. I will create two microservices using ASP .net core 3.1. Over the next posts., I will extend the microservices using CQRS, docker and docker-compose, RabbitMQ and automatic builds and deployments.

Create a Microservice using ASP .Net Core 3.1

You can find the code of  the finished demo on GitHub. I will talk about the key aspects of the microservice but not about every detail. You will need at least a basic understanding of C# and ASP.NET Core.

To-do list for the Microservice

Our two microservice should satisfy the following requirements:

  • Implement a Customer API with the following methods: create customer, update customer
  • Implement an Order API with the following methods: create order, pay order, get all orders which had already been paid
  • When a customer name is updated, it should also be updated in the Order API
  • The APIs should not directly call each other to update data
  • ASP .Net Core 3.1 Web API with DI/IoC
  • Communication between microservices should be implemented through some kind of queue
  • Use DDD and CQRS approaches with the Mediator and Repository Pattern

To keep it simple, I will use an in-memory database. During the implementation, I will point out what you have to change if you want to use a normal database. I will split up the full implementation. In this post, I will create the microservices with the needed features. In the following posts, I will implement Swagger, create a Docker container, set up RabbitMQ and explain CQRS and Mediator.

Structure of the Microservices

I created a solution for each microservice. You can see the structure of the microservices on the following screenshot.

Structure of the Order Microservice
Structure of the Order Microservice

Both microservice have exactly the same structure, except that the order solution has a Messaging.Receive project and the customer solution has a Messaging.Send project. I will use these projects later to send and receive data using RabbitMQ.

An important aspect of an API is that you don’t know who your consumers are and you should never break existing features. To implement versioning, I place everything like controllers or models in a v1 folder. If I want to extend my feature and it is not breaking the current behavior, I will extend it in the already existing classes. If my changes were to break the functionality, I will create a v2 folder and place the changes there. With this approach, no consumer is affected and they can implement the new features whenever they want or need them.

The API Project

The API project is the heart of the application and contains the controllers, validators, and models as well as the startup class in which all dependencies are registered.

Controllers in the API Project

I try to keep the controller methods as simple as possible. They only call different services and return a model or status to the client. They don’t do any business logic.

The action to create a new customer
The action to create a new customer

The _mediator.Send is used to call a service using CQRS and the Mediator pattern. I will explain that in a later post. For now, it is important to understand that a service is called and that a Customer is returned. In case of an exception, a bad request and an error message are returned.

My naming convention is that I use the name of the object, in that case, Customer. The HTTP verb will tell you what this action does. In this case, the post will create an object, whereas put would update an existing customer.


To validate the user input, I use the NuGet FluentValidations and a validator per model. Your validator inherits from AbstractValidator<T> where T is the class of the model you want to validate. Then you can add rules in the constructor of your validator. The validator is not really important for me right now and so I try to keep it simple and only validate that the first and last name has at least two characters and that the age and birthday are between zero and 150 years. I don’t validate if the birthday and the age match. This should be changed in the future.

Validate your models using FluentValidations
Validate your models using FluentValidations


In the Startup.cs, I register my services, validators and configure other parts of the application like AutoMapper, the database context or Swagger. This part should be self-explanatory and I will talk about Swagger or RabbitMQ later.

Register your classes and configure the services
Register your classes and configure the services


The Data project contains everything needed to access the database. I use Entity Framework Core, an in-memory database and the repository pattern.

Database Context

In the database context, I add a list of customers that I will use to update an existing customer. The database context is created for every request, therefore updated or created customers will be lost after the request. This behavior is fine for the sake of this demo.

Adding customers to the Customer DbSet
Adding customers to the Customer DbSet

If you want to use a normal database, all you have to do is delete the adding of customers in the constructor and change the following line in the Startup class to take your connection string instead of using an in-memory database.

Database configuration to use an in-memory database in the Microservice
Database configuration to use an in-memory database

You can either hard-code your connection string in the Startup class or better, read it from the appsettings.json file.

Database configuration to use a SQL database in the Microservice
Database configuration to use a SQL database


I have a generic repository for CRUD operations which can be used for every entity. If I had a special use case for a specific entity, I would create a specific repository that would inherit from this generic one.

The generic repository of the Microservice
The generic repository

When I save or update an entity, I return the entity. When I get data, I always return an IQueryable. The IQueryable contains the query but hasn’t accessed the database yet. This can increase the performance since you can make an even more specific query and load only certain data instead of all.


The Domain project contains all entities and no business logic. In my microservice, this is only the Customer or Order entity.


The Messaging.Send project contains everything I need to send Customer objects to a RabbitMQ queue. I will talk about the specifics of the implementation in a later post.

Publish a Customer to a RabbitMQ queue
Publish a Customer to a RabbitMQ queue


The Service project is split into Command and Query. This is how CQRS separates the concerns of reading and writing data. I will go into the details in a later post. For now, all you have to know is that commands write data and queries read data. A query consists of a query and a handler. The query indicates what action should be executed and the handler implements this action. The same for the command

The CreateCustomerCommandHandler contains the logic to create a customer
The CreateCustomerCommandHandler contains the logic to create a customer

The handler often calls the repository to retrieve or change data.


For my tests, I like to create a test project for each normal project wheres the name is the same except that I add .Test at the end. I use xUnit, FakeItEasy, and FluentAssertions. Currently, there are no tests for the RabbitMQ logic.

Run the Microservices

In the previous section I only talked about the Customer service but the Order service has the same structure and should be easy to understand.

Now that the base functionality is set up, it is time to test both microservice. Before you can start them, you have to make two small changes to be actually able to start them. Currently, we have no queue and therefore the microservices will generate an exception. In the future, it would be nice if the microservices could work even without a queue.

Edit the Customer Service

Open the CustomerCommandHandler in the Service project and comment out the following line _customerUpdateSender.SendCustomer(customer);

Comment out sending data to the queue
Comment out sending data to the queue

This line is responsible for publishing the Customer to the queue

Edit the Order Service

In the Order API, you have to comment out services.AddHostedService<CustomerFullNameUpdateReceiver>(); in the Startup class of the API project.

Comment out the background service which listens for changes in the queue
Comment out the background service that listens for changes in the queue

This line would register a background service that listens to change in the queue and would pull these changes.

Test the Microservices

After you made the changes to both APIs, you can start them. This should display the Swagger GUI which gives you information about all actions and models and also lets you send requests. The GUI should be self-explanatory but I will talk more about it in my next post.

The Swagger GUI with the available actions and models
The Swagger GUI with the available actions and models


Today, I talked about the structure and the features of my microservices. This is just the beginning but both applications are working and could be already deployed. It is important to keep in mind that each microservice has its own data storage and is kept as simple as possible.

You can find the code of  the finished demo on GitHub. In my next post, I will talk about Swagger and how you can use it to easily and quickly document your microservice while providing the opportunity to test requests.

Published inASP.NET MVC / CoreProgramming


  1. Phu Phan Phu Phan

    I think your solution have a problem. You leaked Entities in the Presentation layer.

    • Hi Phu Phan,

      I guess you mean that I am returning entities in the Controller? For example in the CustomerController, I return a Customer object after creating or updating the customer. Yes I use the Customer entity in this case but I wouldn’t gain anything if I used a Dto. If the Customer entity had more properties like password and salt, which I don’t want to return to the user, I would have created a dto.

      Using Dtos is recommended but not required. I prefer simplicity over blindly following recommendations. You could argue that once the Customer entity changes, I will have to create the dto but at the moment I don’t know if the entity will ever change.

  2. Pierre Bouillon Pierre Bouillon

    Hi, thank you for this article, this is very insightful !
    There is a lot to unwrap here for me as I’m pretty new to micro services, but I sure will learn a lot from your project and blog !

  3. coherentlab coherentlab

    This is really a great post about micro services using ASP .net core 3.1. I was bit confused and facing lots of issues while working but now i have clarity for my work,this blog is very useful for beginners also i feel to share with the new infant users because its a time saving blog.” keep posting ahead”.

  4. Lijo Lijo

    This article is very helpful.
    I have a doubt if we need to change or add new fields in the Domain class.How the versioning will work.You have not added the versioning in Domain Class.


    • Hi Lijo,

      If you add new fields to the domain class, you have to make it nullable or set a default value on the database. Having multiple versions doesn’t make any sense because then you would need multiple tables for (more or less) the same data. Database changes are probably one of the most complex problems you can run into when having APIs because you have to make sure that the existing versions still work.

      The same for deleting columns. First you have to make the column nullable and not use in the code anymore. Once you can guarantee that no one uses this field anymore, only then you can delete it.

  5. Shoeb Shoeb

    Hi Wolfgang, after getting a project from git location, I am unable to build at my local, as you have checked in the project without .sln file, could you please guide me what process I should follow at my system to exactly create the project and build it?

    • Hi Shoeb,
      There are 2 sln files. One in the CustomerApi folder and one in the OrderApi folder

Leave a Reply

Your email address will not be published. Required fields are marked *

Follow by Email