Rapid Microservices Development in .NET. An introduction.

Oleksii Nikiforov - Nov 6 '21 - - Dev Community

TL;DR

Learn how to speed up microservices development process by using set of predefined templates and practices. See https://www.nuget.org/packages/NikiforovAll.CleanArchitecture.Templates/


Problem Statement

The way we develop software is built around the idea of high velocity and readiness to change and adapt to the market's requirements. This approach brings up the next questions:

  1. How to successfully contribute to the existing code base?
  2. How to not break something in meantime?
  3. How do we reduce the learning curve of an existing solution?

(1) Actually, all you need to do is to write clean, understandable, and open for modification code. The actual craft of writing code is honed through years of learning and making mistakes. Component-level design is important and you can benefit from good predefined solution templates, something like Clean Architecture gives you a good starting point. Also, if you feel like your project has some complex domain you might want to incorporate DDD in the mix. Personally, I find it really useful because it facilitates communication between developers and domain experts. It is easier to map code snippets to domain and project requirements.

(2) Frequent changes increase the possibility of bugs, as software engineers, we deal with it by extensively testing a codebase. At first glance, you might think writing tests is an additional effort that prevents you from shipping some valuable and urgent features. But, in practice, it is actually another way around, you want to invest in a testing toolkit to save future self from some nasty bugs. Also, tests serve as live documentation and enable refactoring. My suggestion is to be practical about it, don't try to get 100% coverage by writing tons of useless unit tests, determine what is the best in a given context. Simply, write tests, my dude.

(3) Clean code reduces the cognitive load and overall complexity of a codebase. In my opinion, for rapid development, you need somewhat consistency for technical decisions. It is a good idea to use common frameworks, libraries, tools, and even cross-cutting concerns code, just make sure it doesn't introduce additional coupling and unnecessary complexity.

Proposal

Luckily, there is the remedy! Rapid Microservices Development RMD === "remedy", got it? 😏

As an organization responsible for developing microservices solutions you want to build reusable components so it will be easy to create a new microservice from the scratch. Creating project templates is a well-known approach and it is quite simple. Rapid microservice development is a goal and templates might be a viable solution, just be mindful about the goal.

Templates Family

I've created a set of project templates that provides you information of how you might organize microservices solutions yourself. Note, each template might be used individually outside of microservices development context.

Feedback is highly appreciated. 🙏

https://www.nuget.org/packages/NikiforovAll.CleanArchitecture.Templates/

All you need to do is to install it via running the command:

dotnet new --install NikiforovAll.CleanArchitecture.Templates::1.1.1

Once installed, you can see a list of templates by running:

$ dotnet new -l na-
# These templates matched your input: 'na-'

# Template Name                Short Name  Language  Tags
# ---------------------------  ----------  --------  --------------------------------------------
# Build Project Template       na-bu       bash      build-project/Template
# Clean Architecture Template  na-ca       [C#]      CleanArchitecture/DDD/Template
# Event Sourcing Template      na-es       [C#]      EventSourcing/CleanArchitecture/DDD/Template
# Gateway Template             na-ga       [C#]      gateway/Template
Enter fullscreen mode Exit fullscreen mode
Name Alias Repository Status
Build Project na-bu https://github.com/NikiforovAll/na-bu N/A
Clean Architecture Template na-ca https://github.com/NikiforovAll/na-ca
Event Sourcing Template na-bu https://github.com/NikiforovAll/na-es
Gateway na-ga https://github.com/NikiforovAll/na-ga

Build project

In this blog post I will show you how to create a one of the components yourself.

The responsibility of build project is a starting project of any developer. The main goal is to have zero-configuration required to get the project up and running, this is really important and people will say thank you for that.

Build project consists of something like:

  1. Scripts to pull code base and latest changes from the remotes.
  2. Scripts to manage infrastructure used during development. All you need to do is to run docker compose service1, service2, ... to run the system locally. This is really useful.
  3. Projects assets, something like architecture documentation, postman collection to speed up manual developer testing, guidelines, etc.

I will explain the anatomy of the build project from the template in a moment. But first, we need to generate one:

$ dotnet new na-bu -n MyFirstBuildProject --dry-run
File actions would have been taken:
  Create: ./.env
  Create: ./.gitignore
  Create: ./.vscode/settings.json
  Create: ./assets/http/gateway/projects.http
  Create: ./assets/http/naca/projects.http
  Create: ./assets/http/naes/projects.http
  Create: ./build/docker_postgres_init.sql
  Create: ./build/execute-tests.sh
  Create: ./build/generate-report.sh
  Create: ./build/run-services.sh
  Create: ./build/setup-infrastructure.sh
  Create: ./docker-compose-local-infrastructure.yml
  Create: ./docker-compose-tests.override.yml
  Create: ./docker-compose-tests.yml
  Create: ./docker-compose.override.yml
  Create: ./docker-compose.yml
  Create: ./docker-images.txt
  Create: ./README.md
  Create: ./routes.conf.json
  Create: ./scripts/generate-report.sh
  Create: ./scripts/git-clone-all.sh
  Create: ./scripts/git-pull-all.sh
  Create: ./scripts/git-summary/.gitignore
  Create: ./scripts/git-summary/git-summary.sh
  Create: ./scripts/git-summary/README.md
  Create: ./scripts/git-summary/screenshot.png
  Create: ./scripts/open-in-browser.sh

$ dotnet new na-bu -n MyFirstBuildProject
The template "Build Project Template" was created successfully.
Enter fullscreen mode Exit fullscreen mode

As you can see we have:

  • 📂 ./scripts - some tools and scripts to manage build project.
  • 📂 ./build - commands to build, start and test the solution.
  • 📂 ./assets - project artifacts
  • 📂 docker-compose.yml, docker-compose-local-infrastructure.yml, docker-compose.override.yml - run system locally
  • 📂 docker-compose-tests.yml, docker-compose-tests.override.yml - run tests locally

The template pulls three other predefined templates na-ca, na-es, na-ga. You can change that.

Let's clone microservices from the na-bu template:

$ ./scripts/git-clone-all.sh
========================================================
Cloning repository: na-ca
========================================================
Cloning into 'na-ca'...
remote: Enumerating objects: 1146, done.
remote: Counting objects: 100% (1146/1146), done.
remote: Compressing objects: 100% (568/568), done.
remote: Total 1146 (delta 673), reused 968 (delta 506), pack-reused 0Receiving objects:  96% (1101/1146)
Receiving objects: 100% (1146/1146), 172.02 KiB | 978.00 KiB/s, done.
Resolving deltas: 100% (673/673), done.
========================================================
Cloning repository: na-es
========================================================
Cloning into 'na-es'...
remote: Enumerating objects: 1019, done.
remote: Counting objects: 100% (1019/1019), done.
remote: Compressing objects: 100% (523/523), done.
remote: Total 1019 (delta 571), reused 870 (delta 430), pack-reused 0R
Receiving objects: 100% (1019/1019), 173.87 KiB | 1.26 MiB/s, done.
Resolving deltas: 100% (571/571), done.
Updating files: 100% (183/183), done.
========================================================
Cloning repository: na-ga
========================================================
Cloning into 'na-ga'...
remote: Enumerating objects: 53, done.
remote: Counting objects: 100% (53/53), done.
remote: Compressing objects: 100% (35/35), done.
remote: Total 53 (delta 15), reused 46 (delta 8), pack-reused 0
Receiving objects: 100% (53/53), 18.59 KiB | 2.66 MiB/s, done.
Resolving deltas: 100% (15/15), done.
Enter fullscreen mode Exit fullscreen mode

Check the current state of the solution:

$ ./scripts/git-summary/git-summary.sh
Repository                       Branch Remote State
================================ ==== ====== =====
/d/dev/MyFirstBuildProject       main ?  --
/d/dev/MyFirstBuildProject/na-ca main origin
/d/dev/MyFirstBuildProject/na-es main origin
/d/dev/MyFirstBuildProject/na-ga main origin
Enter fullscreen mode Exit fullscreen mode

Now we can build and run the solution:

$ ./build/run-services.sh start
Creating network "myfirstbuildproject_default" with the default driver
Creating volume "myfirstbuildproject_rabbitmqdata-nikiforovall" with local driver
Creating volume "myfirstbuildproject_postgresdata-nikiforovall" with local driver
Creating volume "myfirstbuildproject_seq-nikiforovall" with local driver
# ...
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Creating seq                                ... done
Creating myfirstbuildproject_postgres_1     ... done
Creating myfirstbuildproject_rabbitmq_1     ... done
Creating myfirstbuildproject_naga.gateway_1 ... done
Creating myfirstbuildproject_naes.api_1     ... done
Creating myfirstbuildproject_naes.worker_1  ... done
Creating myfirstbuildproject_naca.worker_1  ... done
Creating myfirstbuildproject_naca.api_1     ... done

Containers starting in background
For log info: run-services.sh info
Enter fullscreen mode Exit fullscreen mode

The system is up and running (screenshot from awesome https://github.com/jesseduffield/lazydocker)

Image description

Now we can use code from assets folder to test things out:

# Create a project in "Clean Architecture" service created from na-ca template

POST http://localhost:3000/ca/projects
Content-Type: application/json

{
    "name": "Clean Architecture Tasks",
    "colourCode": "#FFFFFF"
}

# Get projects

GET http://localhost:3000/ca/projects/

# Create a project in "Event Sourcing" service created from na-es template

POST http://localhost:3000/es/projects
Content-Type: application/json

{
    "name": "Event Sourcing Tasks",
    "colourCode": "#FF5733"
}

# Get projects

GET http://localhost:3000/es/projects/
Enter fullscreen mode Exit fullscreen mode

Image description

Summary

I encourage you to continue the investigation of the codebase on your own. You may find some inspiration or good practices, there are hidden gems 🙂. Let me know if you want to see a review of one of the components in the comments.

Reference

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .