¿Qué es .NET Aspire?

.NET Aspire presenta un marco de trabajo preparado para la nube y diseñado para el desarrollo de aplicaciones distribuidas. Las aplicaciones nativas de la nube suelen incluir pequeños componentes interconectados o microservicios. Estos a menudo dependen de una multitud de servicios, incluyendo bases de datos, sistemas de caché, mensajería, entre otros.

Al construir dichas aplicaciones, la comunicación entre estos microservicios, la escalabilidad, la resiliencia y la monitorización son nuestras pricipales preocupaciones. El conjunto de herramientas .NET Aspire ha sido creado con el propósito exclusivo de resolver todas estas cuestiones.

Las comunicaciones entre aplicaciones se resuelven a través del Descubrimiento de Servicios. También nos proporciona interfaces estandarizadas para servicios comúnmente utilizados, tales como caché y diferentes proveedores de bases de datos en la forma de distintos componentes que son accesibles a través de paquetes NuGet. Además, obtenemos plantillas de proyectos y un dashboard de control donde podemos monitorear nuestra aplicación distribuida con gran nivel de detalle.

Configuración del Proyecto y Nuevas Herramientas

Para utilizar las características de Aspire, necesitamos instalar las herramientas de .NET Aspire que cosnta de dependencias internas y plantillas de proyectos.

Vamos a instalarlo desde línea de comandos (CLI):

dotnet workload update
dotnet workload install aspire
Nota: seguramente no podrás ver las opciones de la plantilla de Apsire y estoe es por que necesitas Visual Studio 2022 Preview IDE to 17.9.0 Preview 

Una vez instalado, podremos ver las plantillas: Aplicación .NET Aspire y Aplicación Inicial .NET Aspire. Para este artículo, utilizaremos la segunda opción que es la conocida aplicación Weather y nos proporciona por defecto cuatro proyectos:

El proyecto consta de:
  • ApiService, nuestra minimal API.
  • Un front-end Blazor en el proyecto Web.
  • ServiceDefaults que es responsable de configurar la resiliencia, el descubrimiento de servicios y la monitorización para todos los proyectos en nuestra aplicación distribuida.
  • AppHost une todo y actúa como un orquestador. Es nuestro proyecto de inicio y es responsable de ejecutar todo dentro de nuestra aplicación.

Lanza el proyecto:

Al hacerlo, la página del Dashboard de control se carga por defecto. En esta página, podemos ver qué proyectos hay en nuestra solución junto con su estado, ubicación, variables de entorno y puntos finales. Además, obtenemos la flexibilidad de explorar los registros y rastreos para cada proyecto. El tablero de control también ofrece un conjunto completo de métricas para nuestra aplicación distribuida.
Proyecto Tye ha muerto, larga vida a .NET Aspire

Los lectores de este blog podrían haber visto una entrada sobre el Proyecto Tye que publiqué hace algún tiempo: https://jmfloreszazo.com/introduccion-al-proyecto-tye/

Y tal vez se estén preguntando, ¿por qué me tomo un momento para hablar sobre este proyecto?

El motivo por el que Aspire adopta un enfoque distinto al del Proyecto Tye.

En el Proyecto Tye, la ejecución de servicios, contenedores y otros ejecutables, que se especifican en la configuración YAML del archivo tye.yaml, se orquestaba mediante código en C#.

El modelo de aplicación de Tye, al estar diseñado para ser consciente de la totalidad de los recursos que necesita orquestar, tiene conocimiento de todas las URLs, los puertos y las cadenas de conexión correspondientes. Esto le permite, por ejemplo, inyectar dicha información a través de variables de entorno.

Por otro lado, el modelo de aplicación de .NET Aspire guarda similitudes. Sin embargo, a diferencia de Tye, en Aspire el desarrollador declara sus recursos utilizando código en C# en lugar de código YAML.

Y mi aprecación personal, es que el Proyecto Tye esta ya descartado por parte de Microsoft y casi seguro es la fuente de inspiración para Aspire. He revisado el código fuente de ambos proyectos y es lo que me lleva a esa conclusión.

Creo que las lecciones aprendidas por Microsoft desde el año 2021 con el Proyecto Tye hasta hoy, posicionarán a .NET Aspire como una herramienta esencial, y no será un experimento como lo fue Tye.

Orquestación con .NET Aspire

Como ya sabemos, el proyecto AppHost es responsable de la orquestación dentro de nuestra aplicación. Veamos cómo funciona:

var builder = DistributedApplication.CreateBuilder(args);

var apiService = builder.AddProject<Projects.AspireSampleApp_ApiService>("apiservice");

builder.AddProject<Projects.AspireSampleApp_Web>("webfrontend")
    .WithReference(apiService);

builder.Build().Run();

Todo sucede en el archivo Program.cs.

Primero, creamos un constructor de aplicaciones distribuidas utilizando el método CreateBuilder(). Despues, agregamos nuestros dos proyectos usando el método AddProject(), pasando el nombre del proyecto deseado como una cadena.

Actualmente, nuestra API no necesita saber acerca de ningún otro proyecto, por lo que su configuración solo depende del método AddProject(). Sin embargo, nuestro front-end depende estrictamente de la API para obtener sus datos, así que después de agregarlo a nuestra instancia del constructor también usamos el método WithReference() para registrar la API.

Finalmente, utilizamos los métodos Build() y Run() para construir y ejecutar nuestra aplicación distribuida.

Configuraciones Globales de Servicios

El proyecto ServiceDefaults se encarga de todas nuestras configuraciones globales:

    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;
    }

Todo sucede en el método de extensión AddServiceDefaults() sobre IHostApplicationBuilder en la clase Extensions. Aquí, agregamos y configuramos varios servicios a nuestra instancia del constructor.

Comenzamos con los métodos ConfigureOpenTelemetry() y AddDefaultHealthChecks() que están definidos en la misma clase.

Después de estos pasos iniciales, continuamos mejorando nuestra colección IServiceCollection al incorporar el Descubrimiento de Servicios usando el método AddServiceDiscovery(). Es crucial añadir UseServiceDiscovery() a nuestro cliente HTTP para que el Descubrimiento de Servicios funcione.

De forma adicional: habilitamos por defecto la resiliencia para nuestro cliente HTTP con el método AddStandardResilienceHandler().

Y sí, puedes trabajar de forma muy sencilla con OpenTelemetry. De aquí mi insistencia en artículos pasados. Es una herramienta que debemos tener en nuestra caja de herramientas; tambien puedes usar Aspire con Azure Application Insights

Componentes

Los componentes .NET Aspire comprenden diversos paquetes NuGet que facilitan la integración de aplicaciones distribuidas con servicios y plataformas reconocidas.

Los paquetes NuGet se extienden a servicios bien conocidos como el caché de Redis, múltiples proveedores de bases de datos y varios de los servicios de Azure.

Cada componente está diseñado para proporcionar funcionalidades esenciales para aplicaciones nativas de la nube, lo cual se logra mediante la provisión automatizada o el seguimiento de patrones de configuración estandarizados.

Si por ejemplo quiero añadir funcionalidad con Redis:

var builder = DistributedApplication.CreateBuilder(args);
var apiservice = builder.AddProject<Projects.AspireDistributedApp_ApiService>("apiservice");
var redisCache = builder.AddRedisContainer("cache");
builder.AddProject<Projects.AspireDistributedApp_Web>("webfrontend")
    .WithReference(apiservice)
    .WithReference(redisCache);
builder.Build().Run();

En Program de AppHost, utilizamos el método AddRedisContainer(), pasando el nombre «cache» para registrar un contenedor de Redis.

El siguiente paso es añadirlo como referencia en nuestro proyecto de front-end.

Si quieres probar esto debes ir a contenedores, en este caso se requerirá Docker para que .NET Aspire pueda ejecutar el contenedor de caché.

A continuación, agregamos un paquete NuGet a nuestro proyecto Web:

dotnet add package Aspire.StackExchange.Redis.OutputCaching --prerelease

Y en la aplicación deberías usar:

builder.AddRedisOutputCache("cache");

app.UseOutputCache();

@attribute [OutputCache(Duration = 60)]
Conclusión

Como hemos visto, .NET Aspire facilita significativamente el desarrollo de aplicaciones distribuidas, superando a herramientas como el Proyecto Tye en varios aspectos.

Destaca por su diseño orientado a la nube y su adaptabilidad para el desarrollo de microservicios, ofreciendo una experiencia de integración fluida y sin complicaciones con componentes y servicios fundamentales.

Este marco de trabajo se erige como una solución integral que responde eficazmente a las necesidades de comunicación, escalabilidad, resiliencia y monitoreo, elementos clave en la construcción de aplicaciones robustas y modernas.