Distributed Systems Development with .NET Aspire Locally

With the release of .NET 8, Microsoft brought us a library—or rather, a framework—that is very powerful, and using it can help us avoid a lot of issues in certain environments where it will drastically improve the development experience. Before continuing, keep in mind that we are still at preview 1, and I expect a lot of improvements, not only throughout 2024, but also in upcoming versions of .NET.

 

 

Today we’re going to see what .NET Aspire is and, of course, how to use it to our advantage with the Distribt project. As usual, the code will be available on GitHub, but this time only in a branch, since it’s not strictly compatible "out of the box" with the rest of the project.

 

 

1 - What is .NET Aspire?

.NET Aspire is a framework/service—call it what you will—released by Microsoft with the goal of making it easier to develop distributed applications.

net aspire

Aspire brings us a series of components that are essential for building distributed systems, including service discovery, telemetry, resilience, and health checks, all of this by default, but of course, it can be expanded.

 

Note: The projects must be using NET8 for everything to work properly.

 

 

1.1 - When to use net aspire

 

You should keep in mind that it is only for local development. Technically, it could be deployed in production, but you shouldn’t, at least for now. I’m not aware if there are plans to use it in production or if it will become part of Azure.

 

According to what was mentioned during the NetConf 2023, the birth of this project is because companies often complain about the complexity of building distributed applications. Honestly, I don’t think that’s normal—well, yes, if developers don’t bother at all with having a healthy ecosystem. In short, if you had followed the Distribt course ;) you’d know what to do and how, and you wouldn’t waste days or even weeks onboarding users. 

 

In our particular case, you’ll know that to set up the whole Distribt infrastructure and test locally, there’s a script that brings everything up, using docker compose as support. 

This simulates the infrastructure you’d have in production.

 

 

2 - Getting started with .NET Aspire 

 

Now what we’re going to do is implement .NET Aspire in our code—not just a “tutorial”, but we’ll be adding it to an existing project.

 

The first thing we need to do is update the .NET workloads. For that, run the following command in the command line:

dotnet workload update

And then we install the Aspire workload:

dotnet workload install aspire

With these commands, what we have done is simply install the project templates that will be made available when we create a new .NET project. Here’s an example when we create one of these projects:

new aspire project riderr

 

As you can see, we have two options. What does each one do?

 

  1. .NET Aspire Application: installs the AppHost and ServiceDefaults packages. These packages include the configuration for health checks, service discovery, telemetry, etc. Anything that’s common and you want to set, this is the place.
  2. .NET Aspire Starter Application: In addition to the previous two, it includes a backend (minimal API) and a frontend (Blazor), so you can test functionalities.

 

 

2.1 - Explanation of .NET Aspire projects

 

First, let’s explain the backend and frontend, as these are the simplest. In both cases, everything works like any other standard project, with two exceptions

The first, common to both, is that after creating the builder for our web application, we call the AddServiceDefaults() method, which we’ll explain in a minute:

var builder = WebApplication.CreateBuilder(args);// Add service defaults & Aspire components.builder.AddServiceDefaults();

Then, for the frontend (actually, this also applies to the backend, but in this case there are no dependencies), when specifying the backend URL, we don’t specify a URL or IP, as you might expect; instead, we indicate an application name:

builder.Services.AddHttpClient<WeatherApiClient>(client => client.BaseAddress = new("http://apiservice"));

 

Now let’s move on to the first of the two purely .NET Aspire projects, ServiceDefaults. As I mentioned, this is where we specify telemetry, health checks, etc., and this is done via the extension method AddServiceDefaults.

public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)    {        builder.ConfigureOpenTelemetry();        builder.AddDefaultHealthChecks();        builder.Services.AddServiceDiscovery();        builder.Services.ConfigureHttpClientDefaults(http =>        {            // Turn on resilience by default            http.AddStandardResilienceHandler();            // Turn on service discovery by default            http.UseServiceDiscovery();        });        return builder;    }

Both this method and the methods it calls come by default with the template.

If you followed the Distribt course, this is very similar to my setup class—not exactly the same, obviously, but, well, the idea is there.

 

If we dig a little deeper, you can see that the .csproj file has a new property that specifies that it’s a shared aspire project: "IsAspireSharedProject".

 

Finally, there’s the project that makes all of this work: the AppHost project. This is where we reference the projects we want to run, and not only that, but it’s also where we define the name we specified earlier, “apiservice”.

 

For the sample project, here’s the code:

var builder = DistributedApplication.CreateBuilder(args);var apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");builder.AddProject<Projects.AspireApp1_Web>("webfrontend")    .WithReference(apiService);builder.Build().Run();

We see that AppHost is referencing the projects it will use, and then for the frontend, it’s also specifying that it uses the API as a reference. 

And when you run this project, you’ll see that two things happen:

  • First, all referenced applications will start up
  • Second, AppHost itself includes a really cool dashboard.

dashboard .net aspire

In this image, you can see the projects being used and their respective URLs to access them. Additionally, if you look at the menu on the left, there’s a monitoring section with logs, traces, and metrics for each one.

metricas aspire

As an important note, you can use projects in other languages within Aspire—or at least, that’s what I’ve read, though I haven’t tested it yet.

 

Finally, I’m sure you’ve realized the big “problem” with .NET Aspire: for everything to work optimally, all the projects you want to use must be in the same repository. Well, technically, in the same solution, but who has solutions that span several repos? 

 

 

3 - Implement Aspire in an existing project

 

First of all, I want to reiterate what I said earlier: using Aspire forces you—at least for now—to have a mono-repo, which I’m not sure is that closely tied to distributed systems. But let’s ignore that point, since coincidentally, in the Distribt project, I have everything in a single repository. 

 

Since the project is quite large, I’m not going to refactor all the infrastructure, as that would probably take hours. What I’m going to do is try to fit Aspire into what I already have. Remember, in this specific case, I have a bunch of services (vault, consul, rabbitmq, mysql, mongodb, telemetry, etc.), so for now, the goal is to get the dashboard to show the projects themselves—each representing a microservice. 

 

For this example, I’m going to configure only Producs.Api.Read, Products.Api.Write, and Products.Consumer. For those unfamiliar with the distribt project, it’s really simple—the read and write projects are separated, each with its own database, and through eventual consistency they sync information via the consumer. 

 

Now, what we should do is create a new .NET Aspire project. I’m going to reuse the AppHost we saw in section 2, obviously changing the projects:

using Projects;var builder = DistributedApplication.CreateBuilder(args);builder.AddProject<Distribt_Services_Products_Api_Read>("productsread");builder.AddProject<Distribt_Services_Products_Api_Write>("productswrite");builder.AddProject<Distribt_Services_Products_Consumer>("productsconsumer");builder.Build().Run();

 

But of course, we haven’t yet included the new package called ServiceDefaults, which I’m going to add in the setup I mentioned before.

public static WebApplication Create(string[] args, Action<WebApplicationBuilder>? webappBuilder = null){    WebApplicationBuilder builder = WebApplication.CreateBuilder(args);    builder.AddServiceDefaults();    ...rest of the code...    return builder.Build();}

Because AddServiceDefaults already includes telemetry, I’ve taken advantage of this and removed all the previously existing code from the setup package.

  • Note: In this particular project, AddServiceDefaults can be included individually in each project or in the shared setup that applies to all—the result is the same.

 

If we now run our AppHost and create a product, we can see both the dashboard:

apphost dashboard aspire en distribt

And the waterfall of the calls:

aspire llamadas

As a simple example, this already works for us, and for now that’s enough; this post is already quite long. Also, the only further step would be to add service discovery for other services like MySQL, RabbitMQ, etc., which would mean changing connections, etc.

 

If your project doesn’t have anything “shared” at this point, it might be worth it, but if you have it structured like I do, it’s probably not.

 

 

Conclusion

It’s been a while since I’ve written conclusions, but I think it’s appropriate for this post.

Personally, I like Aspire; I have to investigate more, since this is my first contact. The fact that you have to have everything in the same repo means I won’t use it at work, and unless that changes, I doubt the situation will change. I hope they come up with something.

 

The dashboard is, for me, the best improvement, with logs, metrics, etc., since they can really help local development. On the other hand, AddServiceDefaults is something I’d expect every company to have implemented already (though I know that’s not the case, I’ve worked at many places and there’s a lot of copy-paste), and I also understand that in my specific project, the setup could be made as an AspireSharedProject.

 

The whole service discovery thing is nice, but I’m not clear if it’s compatible with other service discoveries once deployed to production. From what I’ve seen, when you configure other services you have to put the URL in the appsettings, which destroys the service discovery itself and forces you to deploy those variables (env files, Octopus, etc.), rather than consulting them (service discovery) by environment. 
Having to deploy the variables isn’t necessarily better or worse, it’s just another method—both have their pros and cons.

 

It’s the first version, so I don’t expect it to be perfect—little by little they’re releasing more compatibility packages, many of them contributed by the community, not Microsoft directly.

 

But for the next version, or maybe .NET 10 (which is LTS), I expect a lot more, and I think it’s crucial that you can invoke projects outside the solution; honestly, I was expecting something a bit more magical. At the end of the day, it’s just a wrapper with a project that runs everything in a solution at once, which is something you can already do pretty easily in Visual Studio or Rider. Centralizing logs or telemetry is nice, but it’s something any serious company should already have—besides, telemetry in local doesn’t make much sense.
Ideally, you’d be able to set the URL/GIT of a project and Aspire would download and run it, similar to what you can do with Docker. That’s what I’m hoping for.

Additionally, for local development, you need to have on your machine/docker/server, with access, the infrastructure component you need, so personally, I don’t see as much use as I did when I first saw it at NetConf.

 

This post was translated from Spanish. You can see the original one here.
If there is any problem you can add a comment bellow or contact me in the website's contact form

Uso del bloqueador de anuncios adblock

Hola!

Primero de todo bienvenido a la web de NetMentor donde podrás aprender programación en C# y .NET desde un nivel de principiante hasta más avanzado.


Yo entiendo que utilices un bloqueador de anuncios como AdBlock, Ublock o el propio navegador Brave. Pero te tengo que pedir por favor que desactives el bloqueador para esta web.


Intento personalmente no poner mucha publicidad, la justa para pagar el servidor y por supuesto que no sea intrusiva; Si pese a ello piensas que es intrusiva siempre me puedes escribir por privado o por Twitter a @NetMentorTW.


Si ya lo has desactivado, por favor recarga la página.


Un saludo y muchas gracias por tu colaboración

© copyright 2025 NetMentor | Todos los derechos reservados | RSS Feed

Buy me a coffee Invitame a un café