Microservices on .net core with Nancy - Part 2

💡
This article is from 2017. Code samples may no longer work.
💡
This is a follow-on from Part 1.

Even though we already have console logging, we'll add a LoggingService so that we can figure out how Microservices might talk to each other. We'll be invoking the service directly over HTTP via a pseudo-RPC mechanism.

Note that we aren't considering a number of things that you'll ordinarily want to consider for a larger microservices project such as the use of a correlation ID, retries, fire and forget, or an event driven architecture.

You can get the starter code for this from Bitbucket - go ahead and grab it, or use your code from Part 1.

Dev Environment

If you haven't followed part 1, here's a rundown:

  • The dev environment, all tools & code will run on Mac, PC or Linux
  • We are using Visual Studio Code
  • Open the Solution root folder in vscode
  • Install the following Visual Studio Code Extensions (click "Extensions" in the sidebar): C#, C# Extensions and hit the Restart button once installed
  • 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 Solution root folder.

Migrate old projects to csproj

Since Part 1 was written, Microsoft have updated their dotnet tools to remove support for project.json (and xproj). Instead, they've reverted back to the MSBuild-based .csproj, but it's much less bloaty. Make sure your .net core SDK is up to date

Navigate into the src/FactorialService and src/FactorialService.Models folders and type "dotnet migrate" in a terminal. This will replace the project.json files with csproj files.

Create new projects

In the "src" folder, either from VSCode built in terminal, or a separate prompt, type:

💡
We use netstandard for the models because it's for libraries that can be consumed by different frameworks, .net core apps or .net 4.6 for example.

Delete the "Class1.cs" file from the models project.

Add packages

Another welcome new feature in the dotnet CLI is the ability to add packages.

In LoggingService, run:

In LoggingService.Models, run:

Create the Models

In LoggingService.Models, add a new C# file, called LogRequest.cs, and add a "Message" property.

Then create one more C# file, called LogResponse.cs, add a "Success" property and a property to hold any errors.

💡
Note that the use of a models library is overkill for this use-case. It's implemented this way to illustrate a design pattern that you can use to build larger microservices.

Reference LoggingService.Models from LoggingService

Run this command-line from the LoggingService folder:

Reference LoggingService.Models from FactorialService

We'll be referencing the logging service models (but not the service itself) from FactorialService. This is the "contract".

Run the same command as above, from the FactorialService folder:

💡
For larger projects, you should consider consumer-driven contracts, and testing them with something like Pact

Create the LoggingService

Similar to FactorialService, we need 4 new c-sharp source files in our LoggingService: a startup file, the nancy bootstrapper, the nancy module, and the validations. We already have a Program.cs file that we will change. This simple structure is copied from our first service, and easily repeatable.

Startup.cs

Typical asp.net core boilerplate. Here we add a console logger and tell our microservice to serve requests with Nancy, and with our Nancy Bootstrapper that we are about to create.

Program.cs

Again, typical boilerplate. We want to serve requests on port 5002 so as to not conflict with our first service, that serves on 5001. Note the asterisk means "any hostname" and is useful for ensuring the same code works in any environment.

Bootstrapper.cs

The nancy bootstrapper is used to copy dependencies from .net core's DI system into Nancy's TinyIOC.

Module.cs

This is the guts of the service. For this implementation, we're simply logging the received message to console. However, you may want to write to file system or a database in a proper implementation.

Validators.cs

We'll just make sure the message we want to log isn't empty.

Testing LoggingService

To test, we simply need to restore packages, and run. In a terminal, from LoggingService:

You should see:

Hosting environment: Production
Content root path: src\LoggingService
Now listening on: http://*:5002
Application started. Press Ctrl+C to shut down.

Now we can test in Postman. Launch it, and test our endpoint.

Failure case
Success case
Console output

Invoking LoggingService from FactorialService

Hopefully that all works as-above. Now we simply need to build some plumbing to allow the Factorial service to invoke the Logging service. Normally you might put this plumbing into a small helper assembly, where you could add policies using something like Polly. For now, we'll just add it straight into our FactorialService.

Add Newtonsoft.Json package

Since we're dealing with JSON, we need this. In a terminal, in the FactorialService directory:

ServiceClient.cs

Since we're only calling post, we'll just implement the post method here... but you should implement the other methods as needed, plus any policies you want to implement. This implementation will work for any service call that has a request and response object and takes a POST.

💡
You'll note below that we are instantiating a new HttpClient for every post. This is bad. HttpClient is designed specifically to be persisted and reused for multiple requests. Don't use BaseAddress if you are calling different hosts with the same client.

Add a new class to FactorialService:

💡
We've not included auth or policies - if the calling service isn't available it'll cascade. Bearer token headers could also be wrapped into the client, and policies can be implemented with something like Polly

Inject our client

We need to inject our client - firstly into .net core's application services, then into Nancy's TinyIOC. 2 small changes required.

In FactorialService's Startup class:

💡
We could wrap our messy looking AddSingleton into a neat extension method. Also note the hardcoded string for the URL. This should be changed to use service discovery.

In FactorialService's Bootstrapper:

Change Log statements

We replace the log statements in our factorial service with code to call the Logging microservice. Note I've left the old ILogger in there to make it easier to see changes, but it can be safely removed.

In FactorialService's Module.cs

Done! Testing time

All that's left is to test that it all works. Open 2 terminal windows and start both of our services with:

Run this in FactorialService and LoggingService folders, in each terminal:

Now, test our FactorialService in Postman:

Success case
Failure case
LoggingService console output

There you go! 2 microservices in .net core with nancy; talking to each other over REST.

💡
The source code above is available in bitbucket