Using dotnet new templates to streamline microservices development
You may not know this, but there is a part of Allegro codebase which we started developing in C# due to some special
requirements. This implies new programming opportunities and challenges — one of these is creating a completely new
.NET Core starter project. Let’s explore one potential solution: dotnet new
templates.
What is it and why would I want to use it? #
When working on a project consisting of numerous microservices, you often find that each service shares a part of
“common” code. This becomes a burdensome copy-paste activity when you have to add one more microservice.
There are a number of straightforward and not-so-clean solutions to this problem (i.e. copying from a microservice of
a colleague who read “Clean Code” and/or maintains an exemplary service). In this post, however, I’d like
to explore another option suitable for dotnet core developers: dotnet new
templates.
When you type dotnet new
in your terminal, you get a rather large list of predefined solutions, webapi, mvc or console
being the most noted. But did you know you can use it to create a sample project for your teammates — in the company
or for your open source project? Lo and behold, there’s a simple solution that’s shipped with the SDK: custom dotnet new
templates to the rescue!
Factor out common code #
You can easily standardize a lot of boilerplate code with a custom dotnet new
template. Let’s take a look at standard
WebAPI project files: Startup.cs
and Program.cs
. The second one usally stays untouched unless someone
needs to configure HostBuilder. So why don’t we factor out the common code from the former:
- loading configuration,
- setting up service to service authorization,
- serialization settings,
- logging configuration,
- health checks,
- most widely used libraries (e.g. Swagger),
- HttpClientFactory,
- any other boilerplate you may want.
All configuration comes out of the box, so having a template makes it easy and convenient to create a service that complies to all standards and policies the project may have. The template can also be used to suggest good practices to developers. Comments in this code can help the new dev onboard easier and explain something he might otherwise ask or not, as well as reduce cognitive overhead for all developers by helping them with the template.
Enforce consistent code style and solution structure #
Moreover, a custom dotnet new
template might be also a good way to nudge the team to keep your services’ solutions
structured in the same way across all repos. This may come in handy by reducing the cognitive overhead when you find
yourself looking for a bug in your mate’s service’s code. Other than that, onboarding a new teammate may be a bit easier
if he has a simple microservice that he can dive into to see an example of your development standards.
Adding unit and integration tests to the template is really worth considering. Such template, after some discussions
and development, would become a source of truth for the project’s code style.
Set the rules for dependency and tooling management #
Instituting common tooling is way easier now with the dotnet tool
command. You can store tools’ desired versions
in the dotnet-tools.json
and include them in the template. Same stands for referenced packages — you can settle
for a specific version of given nuget and enforce it by shipping it locked in your dependency manager
(hello, paket!) with your template.
You can also deliver your template with build or deployment scripts for your microservice as well as a minimal dockerfile
— just enough to build it, test it and deploy it somewhere.
Nuts-and-bolts #
When it comes to creating a dotnet new
template, there aren’t many traps or catches — the process is rather
straightforward and well documented. Just create a new dotnet project, add some config files and voila! The most
important files are template.json
and dotnetcli-host.json
. Using the first, one can describe his template via
some metadata and specify parameters. For example, you can create a C# project called ServiceTemplate and use this
config to replace this string everywhere (including filenames) with a flag passed from the CLI:
{
"$schema": "http://json.schemastore.org/template",
"author": "Szymek Adach",
"classifications": [ "WebApi", "Microservice", "C#8" ],
"identity": "Allegro.ServiceTemplate",
"name": "Allegro C# microservice",
"shortName": "csharp-service",
"tags": {
"language": "C#",
"type": "project"
},
"symbols":{
"serviceName": {
"type": "parameter",
"datatype": "text",
"replaces": "ServiceTemplate",
"isRequired": true,
"description": "Name of the service",
"FileRename": "ServiceTemplate"
},
"deploymentScriptServiceName": {
"type": "parameter",
"datatype": "text",
"replaces": "AllegroTemplateServiceName",
"isRequired": true,
"description": "Name of the service used in the deploy.ps1 as well as name of the docker image"
}
}
}
template.json
schema has really in-depth docs that can be found on
GitHub.
dotnetcli-host.json
allows you to specify a shortcut for your command options (the ones generated by the template
engine can sometimes be confusing).
{
"$schema": "http://json.schemastore.org/dotnetcli.host",
"symbolInfo": {
"serviceName": {
"longName": "service-name",
"shortName": "sn"
},
"deploymentScriptServiceName": {
"longName": "deployment-script-service-name",
"shortName": "dssn"
}
}
}
Another useful feature of dotnet templates is the ability of testing them. You can examine the execution of arbitrary
code ran by the template, check for existence of generated files and directories or even make a request to a running
service and assert the response code.
After you test your project, it can be installed from a directory or a nuget via dotnet new -i
command.
Outro #
So, how do we use them at Allegro? .NET Core is a whole new ball game here, so we had to work things out from the ground up,
rather than build up on ready in-house code. Apart from the template configuration files listed above, we created a solution
(well, actually, two solutions for two service flavours: C# and F#). We used the text replacement feature in dockerfiles,
both build and deployment scripts (and yaml files), as well as in the namespaces, projects and solution names.
Now we’re also adding a .editorconfig
file in order to have versioned IDE settings alongside the tempalate.
Although they aren’t a silver bullet, I find dotnet new
custom templates very useful in day-to-day work.
Read the docs and try them yourself. It doesn’t
cost much and benefits are huge.