Con la llegada de .NET 8, Microsoft nos trajo una librería o bueno framework más bien el cual es muy potente y utilizarlo puede salvarnos muchos problemas en ciertos entornos, donde va a mejorar drásticamente la experiencia de desarrollo, Antes de continuar, ten en cuenta que estamos en la preview 1, y espero muchas mejoras a lo largo no solo de 2024 pero durante las proximas versiones de .NET.
Índice
Hoy vamos a ver qué es .NET Aspire y por supuesto como utilizarlo en nuestro favor con el proyecto de Distribt. Como siempre el código estará disponible en github, pero, esta vez solo en un branch, ya que no es puramente compatible "Out of the box" con el resto del proyecto.
1 - Que es .NET Aspire?
.NET aspire es es un framework/servicio/llámalo como quieras que nos ha montado Microsoft con la idea de que sea más fácil hacer desarrollo de aplicaciones distribuidas.
Aspire nos trae una serie de componentes que son indispensables a la hora de hacer sistemas distribuidos, los cuales incluyen service discovery, telemetry, resiliencia y health checks, todo esto por defecto, pero por supuesto se puede ampliar.
Nota: Los proyectos deben estar en NET8 para que todo funcione bien .
1.1 - Cuándo utilizar net aspire
Hay que tener en cuenta que es únicamente para desarrollo local, técnicamente se puede desplegar en producción pero no se debería, por lo menos por ahora, desconozco si hay intenciones futuras sobre desplegarlo en producción o si va a ser parte de Azure.
Por lo que dijeron durante la NetConf de 2023 el nacimiento de este proyecto se debe a que las empresas se quejan mucho sobre la complejidad de crear aplicaciones distribuidas. Lo cual siendo sincero no lo veo normal, osea sí, si los desarrolladores no se preocupan lo más mínimo en tener un ecosistema saludable. En resumen, si hubieran seguido el curso de Distribt ;) sabrían lo que hacer y cómo para no gastar dias y dias, o incluso semanas, en hacer el onboarding de los usuarios.
En nuestro caso particular, sabréis que para montar toda la infra de Distribt y poder hacer test en local hay un script que lo monta todo, apoyado por un docker compose.
Esto lo que hace es simular la infraestructura que tendrías en producción.
- Enlace al script.
- Enlace al docker compose.
2 - Empezar con .NET Aspire
Ahora lo que vamos a hacer es implementar .net aspire en nuestro código, vamos a ver no solo el “tutorial”, sino que lo vamos a implementar a un proyecto existente.
Lo primero que tenemos que hacer es actualizar los workloads de .net, para ello, en la línea de comandos corremos el siguiente comando
dotnet workload update
Y posteriormente instalamos el workload de aspire:
dotnet workload install aspire
Con estos comandos lo que hemos hecho es simplemente instalar las plantillas de los proyectos que se nos van a crear cuando creamos un nuevo proyecto en .NET, aquí podemos ver un ejemplo cuando creamos uno de estos proyectos:
Como vemos tenemos dos opciones, ¿qué es lo que hace cada una?
.NET Aspire Application
: nos instala el paquete deAppHost
y deServiceDefaults
, estos paquetes son los que incluyen la configuración de los heathchecks, service discovery, telemetry, etc, cualquier cosa común que quieras poner, aquí es donde va..NET Aspire Starter Application
: Además de los dos anteriores, nos incluye un backend (minimal API) y un front end (Blazor), para que podamos testear las funcionalidades
2.1 - Explicación de los proyectos de .NET Aspire
Primero vamos a explicar el backend y en front end, ya que son los más sencillos, En ambos casos todo es como cualquier otro proyecto normal, salvo dos excepciones.
La primera, común para ambos, es que una vez creamos el builder, de nuestra web application, vamos a llamar al método AddServiceDefaults()
, el cual explicaremos en un minuto:
var builder = WebApplication.CreateBuilder(args);
// Add service defaults & Aspire components.
builder.AddServiceDefaults();
Y luego ,en el caso del front end (en verdad esto es igual para el backend, pero en este caso no tiene ninguna dependencia) cuando especificamos la URL del backend, no especificamos una URL o una IP que sería lo normal, sino que vamos a indicar un nombre de aplicación:
builder.Services.AddHttpClient<WeatherApiClient>(client => client.BaseAddress = new("http://apiservice"));
Ahora vamos a pasar el primero de los dos proyectos puramente de .NET Aspire, qué es ServiceDefaults
, como he dicho es donde especificamos telemetry, health Checks, etc y esto se hace a través del extensión 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;
}
Tanto este método, como los métodos que utiliza, vienen por defecto con la plantilla.
Sí seguisteis el curso de Distribt esto es una copiadita de mi clase de setup, no es exactamente igual, obviamente, pero vaya, que la idea es la misma.
Si seguimos invesitgando, puedes observar como el .csproj
tiene una propiedad nueva, la cual especifica que es un proyecto compartido de aspire “IsAspireSharedProject
”.
Finalmente el proyecto que es el que hace que todo esto tenga sentido, el proyecto AppHost
. Aquí es donde vamos a referenciar los proyectos que vamos a ejecutar y no solo eso, es donde definimos el nombre que hemos especificado antes de “apiservice
”, aquí es donde se especifica.
Para el proyecto de ejemplo este es el código:
var builder = DistributedApplication.CreateBuilder(args);
var apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
.WithReference(apiService);
builder.Build().Run();
Observamos que el apphost está referenciando los proyectos que va a utilizar, y luego en el de front end, además le está especificando que utilizamos la api como referencia.
Y cuando ejecutamos este proyecto, vamos a ver que suceden dos cosas:
- Primero, Todas las aplicaciones referenciadas se van a iniciar
- segundo, el propio apphost contiene un dashboard que está super bien.
En esta imagen puedes ver los proyectos que están siendo utilizados y sus respectivas URLs para acceder a ellos, pero además, si te fijas en el menú de la izquierda podemos ver un apartado de monitorización, donde tenemos logs, traces y métricas de cada uno.
Como nota importante, decir que se pueden utilizar projectos en otros lenguajes dentro de Aspire, o bueno eso he leído, no lo he testeado.
Finalmente, estoy seguro de que te has dado cuenta del gran “problema” que nos vamos a encontrar con .NET Aspire y es que para que todo funcione de una forma óptima todos los proyectos que van a ser utilizados tienen que estar en el mismo repositorio, bueno, técnicamente en la misma solución, pero, quién tiene soluciones que afectan a más de un repo?
3 - Implementar Aspire en un proyecto existente
Primero de todo, vuelvo a mencionar lo que he dicho, utilizar aspire te fuerza, o al menos por ahora a tener un mono-repo, lo cual no se yo si va muy enlazado a los sistemas distribuidos. Pero bueno, ignoremos ese punto pues da la casualidad de que en este proyecto de Distribt tengo todo en un único repositorio.
Como el proyecto es bastante grande no voy a ponerme a cambiar toda la infraestructura, porque posiblemente llevará horas, lo que voy a hacer es intentar acoplar Aspire a lo que ya tengo. Recuerda que en este caso en concreto, tengo un montón de servicios (vault, cónsul, rabbitmq, myslq, mongodb, telemetry, etc) así que por ahora la prueba es hacer que ese dashboard nos muestre los proyectos en sí ,los cuales representan microservicios.
Para este ejemplo voy a configurar únicamente Producs.Api.Read
, Products.Api.Write
, Products.Consumer
; Para los que no hayáis seguido el proyectos de distribt, es muy sencillo, los proyectos de escritura y lectura están separados y tienen la base de datos separada y a través de consistencia eventual con el consumer se sincroniza la información.
Ahora lo que deberíamos hacer es crear un nuevo proyecto .NET Aspire, voy a reutilizar el Apphost que hemos visto en el punto 2, obviamente, cambiando los proyectos:
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();
Pero claro, no hemos incluido el nuevo paquete llamado ServiceDefaults, el cual lo voy a introducir en el setup que he mencionado antes.
public static WebApplication Create(string[] args, Action<WebApplicationBuilder>? webappBuilder = null)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
...resto del código...
return builder.Build();
}
Como addservicedefaults ya contiene telemetry, he aprovechado y quitado todo el código previamente existente del paquete de setup.
- Nota: AddServiceDefaults en el caso de este proyecto en particular se puede incluir en cada proyecto de forma individual o en el shared setup que afecta a todos, el resultado es el mismo.
Si ahora ejecutamos nuestro AppHost y creamos un producto podemos ver tanto el dashboard:
Y el waterfall de las llamadas:
Como ejemplo sencillo, esto ya nos funciona, y por ahora es suficiente, ya es bastante largo el post, además, lo único extra que podemos incluir en el service discovery de otros servicios como MySQL, RabbitMQ, etc, que implicaría tener que cambiar conexiones, etc.
Si tu proyecto no tiene nada “compartido” a estas alturas quizá rente, pero teniendolo como lo tengo yo, no merece la pena.
Conclusion
Llevo un tiempo sin poner conclusiones, pero creo que para este post debo incluir una.
Personalmente aspire me gusta, tengo que investigar más, ya que para mi ha sido una primera toma de contacto, el hecho de que tengas que tenerlo todo en el mismo repo va a hacer que no lo utilice en el trabajo, y hasta que no cambien eso, dudo que la situación cambie. Espero que algo inventen.
El dashboard me parece la mejor de las mejoras, incluido logs, métricas, etc, ya que pueden ayudar mucho al desarrollo local. Por otro lado el AddServiceDefaults es algo que yo espero que todas las empresas ya tuviesen implementado (en verdad se que no, he trabajado en muchas y hay muuuucho copy&paste), además entiendo que en el caso particular de mi proyecto, el setup se podría hacer un AspireSharedProject.
El tema del service discovery está bien, pero no tengo claro si es compatible con otros service discoveries una vez desplegado en producción. Por lo que he visto, cuando configuras otros servicios tienes que poner la url en el appsettings
, lo cual, destruye el service discovery en sí y te obliga a desplegar esas variables (env files, octopus, etc) y no a consultarlas (service discovery) por entorno.
Tener que desplegar las variables, no es ni mejor ni peor que consultarlas, simplemente es otra forma, ambas tienen pros y contras.
Es la primera versión así que tampoco espero que este perfecto, de hecho poco a poco van sacando más paquetes de compatibilidad con librerías, muchos de ellos aportados por la comunidad, no por Microsoft directamente.
Pero para la versión siguiente, o quizá NET 10 (que es LTS) espero mucho mas, y veo primordial que se puedan invocar proyectos fuera de la solución; de primeras me esperaba algo más mágico, al final es un wrapper con un proyecto que corre todo lo que hay en una solución a la vez, cosa que se puede hacer muy sencillo tanto en visual studio como en Rider. Lo de centralizar los logs o telemetry esta bien, pero es algo que cualquier empresa seria necesita tener, además, telemetry en local tampoco tiene mucho sentido.
Idealmente tendría que ser posible poner la URL/GIT de un proyecto y que aspire lo descargue, ejecute, etc, similar a lo que se hace con docker, yo espero algo así.
Además para el desarrollo local necesitas tener en tu máquina/docker/servidor con acceso el elemento de infraestructura que necesitas, así que personalmente no le veo tanto uso como le ví el día de la NetConf.