Microservices on .net core with Nancy - Part 1
In this series of posts, I'm going to talk a little about Microservices, a little about Nancy, and I'm going to put together a very simple architecture that should serve as a good start for your own Microservices projects.
- Part 1 will focus on getting a single Microservice running.
- Part 2 will add in a second Microservice that can be consumed by the first service, and provide a simple framework for inter-service communication.
What is microservices architecture? In a nutshell, it's "SOA", but each service endpoint runs in it's own process.
Why do it? There's plenty of articles kicking around as to why, and what the trade-offs are, etc... However, the reasons that I do it are thus:
- You're baking in a way to efficiently scale, both in terms of team size, and project complexity. By keeping teams small and autonomous, it encourages innovation, and reduces the need for communication that can otherwise kill your productivity as you scale.
- Individuals within your team don't need to understand as much of the problem domain (and indeed, even as much of "other bits of code") as they usually do. In practice, this basically means less head-scratching and more productivity.
- It requires automation. Automation is a good thing. You may have to do a bit more than usual to get a production-grade microservices cluster running and maintainable, but it's worth it.
- Since each microservice runs in it's own process, does a small number of things, and is loosely coupled to other services, technology choice isn't as important. You could use a different language for every service if you wanted and it wouldn't affect the system as a whole. Early decisions on framework or language don't impact you years down the line in the same way as they would in a monolith - you can change it.
What's Nancy? If you have ever done ruby... you've probably heard of a domain-specific language called "Sinatra" - that you might use intead of rails. Nancy is built from the ground up with the same core principles, but in .Net.
Nancy is a low-ceremony web framework that you would use instead of MVC or WebAPI.
Nancy "just works". It gets out of your way and just let's you get on with it. Turns out it's pretty good for building Microservices for that reason. It runs on OWIN, IIS, Self-hosted, Kestrel, whatever. Here's a quick sample taken from the Nancy github readme:
To get a true feel for how to do this, we need to create a sample solution with 2 microservices - so we can explore inter-service communication and how that might work.
We'll build 4 projects:
- Microservice 1: FactorialService - a simple service that calculates the factorial of a number (n!). Eg 4! = 4 * 3 * 2 * 1 = 24
- Microservice 2: LoggingService - keep a log of activity from the FactorialService
- FactorialService.Models and LoggingService.Models - the request and response class definitions. These serve as contracts for service calls and are kept separate so that they may be referenced by consuming services.
- Visual Studio Code
- .Net core 1.1
- Command line
Since we're using 100% cross-platform tools, everything we do should work on Mac, PC or Linux.
- Make a folder... i.e. "MicroservicesDemo" somewhere
- Launch Visual Studio Code and open the folder you just created
- Install the following Visual Studio Code Extensions (click "Extensions" in the sidebar): C#, C# Extensions and hit the Restart button once installed
- Inside the MicroServicesDemo folder, make a folder called "src". All of our projects will live here.
- Inside the MicroservicesDemo folder, make a json file called "global.json" and put the following content into it:
- Open the command palette (CMD-Shift-P on Mac, Ctrl-Shift-P on PC) and use the "OmniSharp: Select Project" command. Set it to the MicroservicesDemo folder.
Microservice 1: FactorialService
Launch the VS Code Integrated Terminal ( Ctrl + ` ) and run:
We need to add a few packages to project.json, modify Program.cs, add a Startup.cs file and a Nancy Bootstrapper.
project.json - modify as shown to add package dependencies
Program.cs - modify to launch Kestrel on a pre-defined port
Startup.cs - in full. Here we add a console logger and tell it to use Nancy to process requests. Note that we are passing a new Bootstrapper that we are about to create - this is so that we may copy dependencies from the built-in container to Nancy's TinyIOC.
Bootstrapper.cs - a place to copy dependencies. At the time of writing this is how you need to copy dependencies into Nancy with .net core. I expect this to improve in future. Note we're also enabling tracing so we can see and diagnose any errors when testing the microservice.
If you run this (change into FactorialService folder in Terminal) and type:
You should get this:
Hosting environment: Production Content root path: D:\Projects\MicroservicesDemo\FactorialService Now listening on: http://*:5001 Application started. Press Ctrl+C to shut down.
Go ahead and hit Ctrl+C. Now we're fully bootstrapped and we can add the Nancy Module.
Before we add the module, we want to create a models project to hold the request and response. Note this is overkill in this simple example, but I'm trying to illustrate a pattern that we can use to build a more complex solution.
From terminal, in the src folder, run:
Add System.Dynamic.Runtime to the newly generated project.json in the FactorialService.Models project (so we can use the dynamic keyword in our source):
Now, create 2 classes in FactorialService.Models:
We want to reference this newly created library from FactorialService. Update the FactorialService project.json file:
Go ahead and create a Module.cs file in FactorialService:
In terminal, within the FactorialService folder, run:
If you load up Postman, and try to hit the service, you should get this:
Cool! One microservice done. Almost... before we go any further we'll add some validation. We want to restrict the input to a positive integer between 1 and 12 - this is because it'll overflow our integer from 13 and up.
Nancy can find validators in the currently executing assembly. Create a new file in FactorialService called Validators.cs
Hooking it up is very simple. Make the following changes to Module.cs
Now if we run our code, it should show errors as appropriate. Note also that we are restricting the request to integers - non integers will throw a 404.
And that is a simple Microservice built using Nancy and .net core. In the next part, I'll introduce a second Microservice for Logging, and implement inter-service communication.