Setting up microservices is often a pain. It can be a tiny misconfiguration or simply a lack of required knowledge. Fortunately there is one tool that can take all of this pain away - Project Tye.
Getting started 📌
Start with a simple application consisting of two services, one of which triggers the other one. It may sound trivial but it's enough to showcase the basics of the Project Tye usage and its features.
Installing Project Tye 🧰
Project Tye is a .NET CLI tool that require .NET Core 3.1 SDK for version 0.10.x
or .NET 6 SDK for all newer releases. I am using the 0.11.0-alpha.22057
version when writing this article. Install the latest version of Tye globally by running the following command.
dotnet tool install -g Microsoft.Tye
If you install it successfully you'll be able to run the tye
command.
Prepare Solution 🧩
Create FakeWeatherApp
solution with two projects WeatherApi
and NotificationApi
. Use the Visual Studio or the following commands.
mkdir FakeWeatherApp
cd FakeWeatherApp
dotnet new sln -n FakeWeatherApp
dotnet new webapi -n WeatherApi
dotnet new webapi -n NotificationApi
dotnet sln add WeatherApi NotificationApi
Adding Tye support 🔧
Tye uses a tye.yaml
file for configuration purposes. Create one by running the tye init
command.
# tye application configuration file
name: fakeweatherapp
services:
- name: weatherapi
project: WeatherApi/WeatherApi.csproj
- name: notificationapi
project: NotificationApi/NotificationApi.csproj
Each service is an individual part of an entire application. It could be a project, container or executable.
Tye automatically detected our two projects and added them to its configuration file. Unfortunately, if you add more projects, you will have to update this file manually. For now, let's leave the configuration as it is and see the output of the tye run
command.
Tye searches for free ports and creates bindings automatically. It also hosts the Tye Dashboard with some basic but useful information about services.
Tye Dashboard 📺
Dashboard is available at http://localhost:8000/
by default. In case of the port being taken, Tye will use a different, random free port. See the output of tye run
to find out what yours is.
Let's go over all the Services table columns.
- Name: the service name from
tye.yaml
file, which is also a link to the metrics page shown below - Type: the service type, which can be project, container, executable or external
- Source: the path of the source from which the service was built
- Bindings: a links to the URLs of the service
- Replicas: how many replicas are running / how many replicas are expected to be running
- Restarts: how many restarts have happened
- Logs: a link to the service streaming logs
Connecting the Services 🤙🏻
At this point we have two exactly the same applications running. Let's make some changes and then wire them up!
NotificationApi
Firstly, remove all the weather logic that was automatically added by dotnet new webapi
command and create a new controller called NotificationController
. Its route should be /notifications
. Then add a new action that supports HTTP POST method and accepts an object with Type
and Message
fields. Finally, to see any results, just log incoming data to the console. A sample code is shown below.
using Microsoft.AspNetCore.Mvc;
namespace NotificationApi.Controllers;
[ApiController]
[Route("notifications")]
public class NotificationController : ControllerBase
{
private readonly ILogger<NotificationController> _logger;
public NotificationController(ILogger<NotificationController> logger)
{
_logger = logger;
}
[HttpPost]
public ActionResult Post([FromBody] NotificationDto notificationDto)
{
_logger.LogInformation(notificationDto.ToString());
return NoContent();
}
}
public record NotificationDto(string Type, string Message);
WeatherApi
Let's start by modifying the WeatherForecastController
class. Change its route to /weather-forecasts
to follow the naming convention. Remove the field of type ILogger
as it's not necessary. Use HttpClient
to connect to the second service. To do so, add HttpClientFactory
field and expect it in the constructor function. Then create a client and make a HTTP POST request to /notifications
url.
using Microsoft.AspNetCore.Mvc;
namespace WeatherApi.Controllers;
[ApiController]
[Route("weather-forecasts")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries =
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly IHttpClientFactory _httpClientFactory;
public WeatherForecastController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
var httpClient = _httpClientFactory.CreateClient("notificationapi");
await httpClient.PostAsJsonAsync("/notifications", new
{
Type = "TestType",
Message = "Test"
});
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
Of course, it won't work without the configuration. Set up a new HttpClient
in Program.cs
file. Remember to do it before builder.Build()
call.
builder.Services.AddHttpClient("notificationapi", client =>
{
client.BaseAddress = // ???
});
Usually we put all environment variables into the .appsettings
file, but this time we don't know what the actual NotificationApi
address is. We decided not to use a fixed port so we have to get it somehow.
The Tye team provides a NuGet package with the configuration extensions called Microsoft.Tye.Extensions.Configuration
. It makes very easy to get the service URI not only during the development but also when deploying to Kubernetes.
builder.Services.AddHttpClient("notificationapi", client =>
{
client.BaseAddress = builder.Configuration
.GetServiceUri("notificationapi"); // service name from tye.yaml
});
Testing 🕹️
Use the tye run
command to start the application and open the dashboard. Click the WeatherApi
url and append /swagger
at the end. You'll see the Swagger UI. Make a HTTP GET request to /weather-forecasts
.
Open the NotificationApi
logs to check if it was successfully called. See the results below.
Debugging 🪲
Tye can run each service in debug mode. It exposes a hook to attach to from your editor or IDE. The debugging experience is the same as with any other local project. You can add breakpoints, step through code, see locals etc.
Run in debug mode using the command below. Alternatively, pass *
instead of a specific service name for all services.
tye run --debug notificationapi
Open Debug / Attach to Process...
window in the Visual Studio and search for the NotificationApi.exe
to attach the built-in debugger.
External dependencies 🔗
Applications are usually built using many databases, services and other processes. Fortunately, the Tye makes it quite easy to add and setup any Docker image.
A PostgreSQL database setup as an example.
# tye application configuration file
name: app
services:
- name: postgres
image: postgres
env:
- name: POSTGRES_PASSWORD
value: "@password1"
- name: POSTGRES_DB
value: "test_db"
bindings:
- port: 5432
connectionString: Server=${host};Port=${port};User Id=postgres;Password=${env:POSTGRES_PASSWORD};
Learn more 📖
Check the project's GitHub repository for more https://github.com/dotnet/tye. Give it a star so they can continue developing this awesome tool.
Conclusion 🏆
Project Tye is an experimental tool, so I wouldn't recommend using it in production. However, it might be a great idea to use it in your side project. Who knows, maybe thanks to the time saved on setup, you will finish it and become a millionaire 💸.