When you build a simple self-contained service and run it locally it feels so easy ๐Ÿคฉ. But when you have to develop, debug, and test on your local machine many services that would be challenging ๐Ÿ’ญ. First of all, you have to deal with unique ports mystery for each service you run. And if you would like to run that services in Kubernetes locally, promise me, it is no so straightforward ๐Ÿคž.

In the first quarter of 2020 Microsoft have started building the experimental ๐Ÿงช tool Tye with the idea to decrease microservices and distributed application developing, testing, and deployment complexity ๐Ÿš€. This project is part of .NET Foundation. The top three contributors for Tye project are rynowak, jkotalik, and davidfowl. ๐Ÿคฉ

dotnet/tye
Tye is a tool that makes developing, testing, and deploying microservices and distributed applications easier. Project Tye includes a local orchestrator to make developing microservices easier and ...
Tye is a developer tool that makes developing, testing, and deploying microservices and distributed applications easier. Project Tye includes a local orchestrator to make developing microservices easier and the ability to deploy microservices to Kubernetes with minimal configuration.

Let`s try out tye tool!

Build steps:

  • Install prerequisite tools
  • Create Solution and multiple projects
  • Run using tye tool
  • Add Service discovery

Prerequisites ๐Ÿ”จ

Tools you have to install before create this example project:

Installing tye

The installation consists of two steps. The first step is NuGet source registration and the second is tye tool installation.

Register NuGet source

Before installation, you have to add NuGet package source, because that package is placed in a different source, not https://nuget.org.

dotnet nuget add source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --name dotnet5

Using command dotnet nuget list source you can check if dotnet5 source was added.

Install tye tool

By executing the following command you will install tye tool in the global scope. The current version you can get in GitHub repository.

dotnet tool install -g Microsoft.Tye --version "0.4.0-alpha.20371.1"

After installation completed check if command tye works correctly ๐ŸŽ‰.

After executing tye command get summary help.

Create a solution with multiple projects

We have to build one Solution file which contains multiple projects. For better illustration, I chose to build two backend projects and one frontend. Frontend makes a call to both backend APIs.

By executing the following sequence of commands you will create all projects you need for the next step โš™๏ธ.

 dotnet new sln -n SampleApplication
 dotnet new webapi -n FirstBackend
 dotnet new webapi -n SecondBackend
 dotnet new web -n Frontend
Create Solution, two WebAPI and one Web projects
dotnet sln add .\Frontend\Frontend.csproj
dotnet sln add .\FirstBackend\FirstBackend.csproj
dotnet sln add .\SecondBackend\SecondBackend.csproj
Add projects to solution

At the end run dotnet build to check if all projects build successfully.

Run using tye tool

Without any changes let`s try to run all three projects using tye. ย By executing the following tye run command all three projects should be running โค๏ธ. In Tye terminology project names as services.

tye output after running command

In output, we can see that tye reads Solution file and launches all projects they discovered. As we can identify that ports for each project are unique. If we check all addresses, we will see that all projects are working correctly.

Tye has multiple choice of how you can run your projects. By default, it runs each project as a single process. But also you can run all projects like containers by executing tye run --docker.

Projects running as docker containers

Manage running processes in Dashboard ๐Ÿ“ˆ

When you run tye its starts Dashboard. The dashboard is built using Blazor. ย If you open address http://127.0.0.1:8000 you will see:

  • services list tye running and running type project or container
  • each service logs, by clicking on the View link in column Logs
  • each service metrics, by clicking on the link in column Name
tye Dashboard

What I found out interesting that tye attaches all metrics counters out of the box without any changes in the project, that`s nice. You can call URLs multiple times and check if metrics are changing, for example, Microsoft.AspNetCore.Hosting/total-requests.

Debug project by attaching to tye running process ๐Ÿ”

Running multiple projects using only a single command looks amazing, but this tool is for the development phase, so we need to debug our projects. We can attach to the process from Visual Studio Code. By using .NET Core Attach configuration profile you could choose the process tye are running and debug your project easy way. ๐Ÿš€

Project debugging by attaching to tye running process

Add Service discovery

If you run tye run multiple times you will see that endpoint ports are changing each time dynamically. We have one frontend application that calls two APIs. So we have to figure out the URI of APIs ๐Ÿค”. Tye provides us the possibility to discover running services URIs using environment variables.

Add service discovery extension library

To enable service discovery we need to add NuGet package to our frontend project by executing a command.

dotnet add .\Frontend\Frontend.csproj package Microsoft.Tye.Extensions.Configuration --version 0.4.0-alpha.20371.1
Add NuGet package to provide service discovery

Change Startup.cs to add inject IConfiguration and output discovered URIs for backend.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Frontend
{
    public class Startup
    {
        /// Inject IConfiguration
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        ...

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    var firstBackendUri = Configuration.GetServiceUri("FirstBackend");
                    var secondBackendUri = Configuration.GetServiceUri("SecondBackend");

                    await context.Response.WriteAsync($"FirstBackend uri: {firstBackendUri} \n");
                    await context.Response.WriteAsync($"SecondBackend uri: {secondBackendUri} \n");
                    await context.Response.WriteAsync("\nFrontend!");
                });
            });
        }
    }
}
Discover APIs URIs from frontend using tye configuration

After Startup.cs modification try to run again tye run and take a look at the frontend output. You will see that URIs were discovered successfully. In the next steps we will modify our backend APIs and try to call from frontend.

Service URIs discovery

Add backend controllers

In FirstBackend project create new controller BackendController.cs in the Controllers directory and copy the following code.

using Microsoft.AspNetCore.Mvc;

namespace FirstBackend.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class BackendController : ControllerBase
    {
        [HttpGet]
        public string Get() => "First backend";
    }
}

In SecondBackend project create new controller BackendController.cs in the Controllers directory and copy the following code.

using Microsoft.AspNetCore.Mvc;

namespace SecondBackend.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class BackendController : ControllerBase
    {
        [HttpGet]
        public string Get() => "Second backend";
    }
}

Call APIs from frontend

Copy following code to Frontend project Startup.cs file.

using System.Net.Http;
...

namespace Frontend
{
    public class Startup
    {
        ...

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", async context =>
                {
                    var firstBackendUri = Configuration.GetServiceUri("FirstBackend");
                    var secondBackendUri = Configuration.GetServiceUri("SecondBackend");

                    var client = new HttpClient 
                    {
                        BaseAddress = firstBackendUri
                    };
                    var firstBackendResponse = await client.GetAsync("/backend");
                    var firstBackendResponseContent = await firstBackendResponse.Content.ReadAsStringAsync();

                    client = new HttpClient 
                    {
                        BaseAddress = secondBackendUri
                    };
                    var secondBackendResponse = await client.GetAsync("/backend");
                    var secondBackendResponseContent = await secondBackendResponse.Content.ReadAsStringAsync();

                    await context.Response.WriteAsync($"FirstBackend uri: {firstBackendUri} \n");
                    await context.Response.WriteAsync($"response: {firstBackendResponseContent} \n\n");

                    await context.Response.WriteAsync($"SecondBackend uri: {secondBackendUri} \n");
                    await context.Response.WriteAsync($"response: {secondBackendResponseContent} \n\n");                  
                    
                    await context.Response.WriteAsync("\nFrontend!");
                });
            });
        }
Call backend APIs using discovered URIs

After Startup.cs modification try to run again tye run and take a look at the frontend output. You will see a response from both backend APIs.

Frontend output

In the first moment that looks like magic ๐Ÿง™โ€โ™‚๏ธ. In a nutshell, tye does the following steps:

  • Find .sln (other extensions also are supported: tye.yaml, tye.yml, *.csproj, *.fsproj, *.sln)
  • Extract solution metadata and build, configure collection of services to run (unique ports are assigned using by PortAssigner.cs)
  • All URIs are registered using Environment variables in ProcessRunner.cs class when you run your projects as independent processes (process runner is used by default)
  • At the end frontend project reads that values using extension method GetServiceUri

Summary ๐Ÿฌ

In this post, we have tried out tye tool for developers to increase productivity when developing a microservices architecture application. As we saw tye simplify application running locally task a lot. Using only one command you can run, debug, test locally whole application without any inconvenience. Tye has a lot more features like: add tye configuration, run dependencies like database, deploy to ย Kubernetes cluster on-premise, or direct to cloud. The tool is open source and written on .NET Core, you have to clone that repository because there is a lot of interesting stuff and techniques to learn.

Have fun and perform better with tye! ๐Ÿฆ‰

[eof]