Introdução

Aspire no .NET é um conjunto de ferramentas e integrações para descrever, executar, observar e preparar a implantação de aplicações distribuídas. Ele permite declarar a relação entre uma API, uma interface web, um cache e outros recursos em um modelo de aplicação, em vez de depender de uma sequência informal de comandos e variáveis copiadas entre terminais. O benefício mais imediato aparece no desenvolvimento: iniciar o conjunto e investigar uma chamada entre serviços fica mais previsível.

Isso não significa que toda aplicação precise virar um conjunto de microsserviços. Eu posso usar Aspire para uma API acompanhada de Redis e manter o domínio em um monólito bem organizado. Também não significa que um dashboard local resolva a operação em produção: identidade, disponibilidade, persistência, retenção de telemetria e recuperação continuam exigindo decisões próprias. Aspire reduz trabalho repetitivo de composição; não elimina a arquitetura.

O problema que interessa aqui é concreto. Uma pessoa conhece a porta da API, outra sabe qual container precisa subir primeiro, e uma terceira mantém a connection string correta em um arquivo que ninguém documentou. Quando o ambiente finalmente funciona, investigar uma requisição exige combinar logs de processos diferentes. Quanto mais dependências participam desse caminho, maior o custo de reproduzir uma falha e de preparar alguém novo para contribuir.

Neste artigo, eu separo três peças que costumam aparecer juntas: AppHost, ServiceDefaults e dashboard. Depois percorro o starter oficial para mostrar onde cada uma entra e como verificar o resultado. O foco é entender o modelo e fazer uma primeira adoção consciente, não apresentar um catálogo de integrações ou prometer uma plataforma de produção pronta. Vou usar C# e .NET 10 como referência, mas a documentação atual chama o produto de Aspire e também contempla aplicações com outras linguagens.

Pré-requisitos

Para acompanhar o exemplo, considere:

  • .NET SDK 10 instalado no ambiente em que o starter será executado. O runtime sozinho não cria nem compila os projetos.
  • SDK e templates compatíveis. O roteiro fixa os templates em Aspire 13.6.4, sem afirmar que essa será a versão mais recente quando você ler o artigo.
  • Runtime de containers compatível, como Docker, se você habilitar Redis ou outra dependência executada em container. Isso não exige containerizar todos os projetos .NET locais.
  • HTTPS de desenvolvimento configurado. Em Linux, a confiança do certificado depende também da distribuição e do navegador; não desabilite a validação de TLS para contornar o problema.
  • Acesso às fontes de pacotes e imagens usadas pelo starter. Um ambiente corporativo pode exigir proxy, espelho de registry e aprovação de dependências.

Os comandos abaixo são destinados a um ambiente de desenvolvimento isolado. Não apontam para infraestrutura existente nem fazem deploy. Como o starter evolui entre versões, registre o SDK, os templates e as opções escolhidas antes de transformar o exemplo em referência do time.

O que o Aspire resolve, e o que continua sendo arquitetura

Uma aplicação distribuída não é apenas uma lista de executáveis. Ela possui um grafo de dependências: a interface chama uma API, a API acessa um banco, um Worker consome mensagens e alguns desses caminhos precisam produzir telemetria correlacionada. Sem uma descrição compartilhada, parte desse grafo fica espalhada entre documentação, configuração e conhecimento pessoal.

Aspire oferece uma forma de tornar essas relações explícitas. O modelo pode identificar serviços por nomes lógicos, associar dependências e fornecer configurações de conexão. Integrações também podem facilitar registros de clientes, verificações de saúde e instrumentação. O ganho não está em esconder que Redis existe, mas em evitar que cada desenvolvedor reconstrua sua configuração por tentativa e erro.

Eu distinguiria composição técnica de decomposição de negócio. Definir que uma API usa um cache não explica onde termina um contexto de domínio, se duas operações precisam de transação ou como um consumidor evita processar uma mensagem duas vezes. Essas decisões seguem pertencendo à aplicação. O Transactional Outbox Pattern, por exemplo, trata um problema de consistência que não desaparece quando os processos passam a iniciar pelo mesmo AppHost.

Por isso, eu não adotaria Aspire como justificativa para fragmentar um sistema. Uma aplicação modular acompanhada de dependências externas pode aproveitar a ferramenta sem aumentar a quantidade de serviços de negócio. O critério útil é outro: quanto trabalho repetitivo o time tem para compor o ambiente e investigar o comportamento entre recursos? Se a resposta for pequena, talvez a adoção possa esperar. Se for recorrente e cara, existe um problema adequado para resolver.

AppHost descreve recursos e suas relações

O AppHost é o ponto em que o modelo de aplicação é composto. No caminho C#, ele usa as APIs de hosting do Aspire para registrar projetos, containers e recursos suportados pelas integrações. Essa definição permite relacionar dependências por nome, sem espalhar uma porta fixa ou uma connection string local por todos os projetos.

Um AppHost que inclui interface e API pode ter uma configuração como esta. Os tipos em Projects são gerados a partir das referências de projeto do AppHost; não são namespaces que você deve criar manualmente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var builder = DistributedApplication.CreateBuilder(args);

var api = builder.AddProject<Projects.LojaAspire_ApiService>("apiservice")
  .WithHttpHealthCheck("/health");

// Referência de configuração e dependência de inicialização são explícitas.
builder.AddProject<Projects.LojaAspire_Web>("webfrontend")
  .WithExternalHttpEndpoints()
  .WithHttpHealthCheck("/health")
    .WithReference(api)
    .WaitFor(api);

builder.Build().Run();

ℹ️ Informação: O trecho adapta a composição do starter oficial. O código completo é gerado pelo template aspire-starter; exemplos mantidos pela equipe estão no repositório oficial aspire-samples.

Há uma distinção importante: WithReference não significa esperar. A referência disponibiliza a configuração necessária para consumir o recurso, conforme seu tipo. WaitFor expressa uma dependência de inicialização e pode considerar as verificações de saúde configuradas. Se o recurso não tem uma verificação representativa, esperar por ele não comprova que todas as suas operações estão disponíveis.

Também não existe recuperação permanente implícita. Um banco pode ficar indisponível depois que a API iniciou; nesse momento, a aplicação precisa lidar com timeout, cancelamento e falhas transitórias. O modelo ajuda a iniciar o ambiente de forma coerente, mas não transforma o sucesso do startup em garantia de disponibilidade durante toda a execução. Separar essas responsabilidades evita uma falsa sensação de proteção operacional.

AppHost do Aspire referencia web, API e Redis; os serviços enviam telemetria ao dashboard

ServiceDefaults configura os serviços, não o domínio

ServiceDefaults costuma ser um projeto compartilhado criado pelo template. Ele concentra extensões para aplicar configurações comuns aos serviços .NET, como instrumentação OpenTelemetry, health checks, descoberta de serviços e resiliência de clientes HTTP. O nome descreve uma convenção útil, não um servidor central que todas as requisições precisam atravessar.

O efeito depende de cada aplicação adotar essas extensões. No starter, procure chamadas como builder.AddServiceDefaults() durante a configuração e app.MapDefaultEndpoints() no mapeamento de endpoints. Apenas manter o projeto dentro da solução não habilita automaticamente tudo nos demais projetos. Esse detalhe é relevante ao incorporar Aspire a uma aplicação existente: a composição do ambiente e a configuração interna dos serviços são mudanças relacionadas, mas diferentes.

Eu manteria esse projeto pequeno. Configuração de telemetria e políticas técnicas comuns cabem nele; regras de aprovação de pedidos, contratos específicos de um módulo e acesso a dados de negócio, não. Caso contrário, um facilitador de infraestrutura vira uma biblioteca de dependência obrigatória para todo o domínio, aumentando o custo de mudar serviços de forma independente.

Também revisaria os padrões antes de adotá-los. A configuração de resiliência de um cliente HTTP precisa respeitar a semântica da operação. Repetir uma leitura não tem o mesmo risco que repetir uma cobrança ou um comando sem proteção contra duplicidade. Configure a política adequada, use idempotência quando necessária e propague cancelamento.

O mesmo cuidado vale para saúde. No ServiceDefaults padrão, o mapeamento dos endpoints de health check ocorre em desenvolvimento. Se você precisar de probes em produção, configure essa exposição conscientemente, com o escopo e as proteções adequados. Um endpoint técnico não deve vazar detalhes internos só porque já estava disponível no ambiente local.

Dashboard e OpenTelemetry tornam o fluxo investigável

O dashboard do Aspire apresenta recursos e informações de observabilidade, como logs, métricas e traces disponibilizados pela instrumentação. Sua utilidade aparece quando eu consigo sair do sintoma visível na interface e acompanhar o caminho até a API, observando o comportamento de cada etapa em vez de procurar frases soltas em terminais diferentes.

Essa experiência depende de instrumentação e contexto. Um serviço precisa produzir os sinais, e uma chamada entre serviços precisa propagar os identificadores necessários para que os spans apareçam relacionados. Usar uma biblioteca não instrumentada ou interromper a propagação em uma fila pode deixar lacunas. O dashboard ajuda a enxergar o que foi emitido; não inventa o que a aplicação nunca registrou.

Eu também separaria observabilidade técnica de telemetria de negócio. Uma requisição HTTP bem-sucedida e rápida não demonstra, sozinha, que um pedido foi faturado corretamente. Para isso, ainda preciso definir eventos, métricas e atributos que representem o comportamento relevante. Ao escolher esses atributos, evito dados pessoais e dimensões com cardinalidade descontrolada, como usar um identificador diferente como label de cada métrica.

O artigo sobre logging estruturado no .NET aprofunda contexto e propriedades de logs. Aqui, o ponto complementar é a correlação entre componentes e a composição do ambiente. Em produção, retenção, controle de acesso, exportação, amostragem e alertas exigem uma solução operacional definida. OpenTelemetry facilita instrumentar e exportar sinais; não fornece, sozinho, todo o armazenamento e o processo de resposta a incidentes.

⚠️ Atenção: Não exponha o dashboard de desenvolvimento publicamente como atalho de monitoramento. Logs e traces podem revelar URLs, nomes de recursos e informações sensíveis. Revise autenticação, rede e conteúdo da telemetria antes de disponibilizar qualquer interface operacional.

Integrações e descoberta de serviços não eliminam contratos

Aspire diferencia a configuração do recurso no AppHost da configuração do cliente na aplicação. No caso de Redis, uma integração de hosting permite adicionar o recurso ao modelo. Na aplicação consumidora, uma integração de cliente pode registrar a implementação usada pelo código, incluindo sua configuração e recursos técnicos disponíveis.

Essa separação evita uma interpretação frequente: adicionar Redis ao AppHost não faz a API começar a usar cache. O serviço ainda precisa referenciar o recurso e registrar um cliente compatível. O código de negócio também precisa decidir o que armazenar, como compor chaves, qual expiração aplicar e o que fazer quando o cache falhar. Aspire ajuda na conexão, não escolhe a política de cache.

Os nomes precisam corresponder. Se o recurso se chama cache, a configuração do cliente deve procurar essa conexão, em vez de assumir que qualquer Redis disponível será encontrado. Para chamadas HTTP, a descoberta permite trabalhar com nomes lógicos e resolver os endpoints configurados. Isso não equivale a criar DNS público, configurar um API Gateway ou proteger uma API com autorização.

Há ainda uma diferença entre ter uma conexão e usar o recurso com segurança. Um Redis acessível pode continuar sofrendo com valores enormes, chaves sem expiração ou disputa entre aplicações. O artigo Redis: Big Keys Destroem o Desempenho Compartilhado aborda justamente esse risco. Eu manteria essas decisões explícitas mesmo depois de simplificar a criação do ambiente.

Para uma primeira adoção, começaria com um único caminho: interface chamando API. Depois acrescentaria uma dependência externa e verificaria o que mudou na configuração, nos health checks e na telemetria. Esse crescimento incremental deixa visível o que cada integração realmente entrega.

Exemplo Prático

Vou usar o starter oficial, que já fornece uma interface web, uma API, o AppHost e o projeto de padrões de serviço. A ideia é compreender uma composição funcional antes de adaptar o modelo a um repositório existente. Não é necessário copiar classes de negócio nem criar um projeto independente de exemplo para reproduzir estes passos.

Em uma pasta de trabalho, instale a versão de referência dos templates e crie a solução:

1
2
3
4
5
# Fixe a versão para que o roteiro não dependa do template mais recente.
dotnet new install [email protected]

# Crie o starter com projetos voltados ao .NET 10.
dotnet new aspire-starter --name LojaAspire --output LojaAspire --framework net10.0

Leia as opções apresentadas pelo template e registre as que selecionar. O núcleo deste exemplo não depende de habilitar Redis. Se escolher uma opção de cache baseada em container, o runtime de containers precisa estar disponível antes da execução. Também mantenha as versões dos projetos gerados como conjunto, em vez de atualizar somente uma integração sem conferir compatibilidade.

Na estrutura criada, identifique estas responsabilidades:

ProjetoO que conferir
LojaAspire.AppHostRecursos registrados e referências entre interface e API
LojaAspire.ServiceDefaultsExtensões de telemetria, saúde, descoberta e resiliência
LojaAspire.ApiServiceEndpoints de aplicação e adoção dos padrões comuns
LojaAspire.WebCliente HTTP que acessa a API pelo nome lógico

Antes de iniciar, verifique o certificado HTTPS de desenvolvimento. O comando de confiança abaixo se aplica ao ambiente local e pode depender de configuração adicional no Linux; não é uma orientação para modificar certificados de produção:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Configure a confiança do certificado no ambiente de desenvolvimento.
dotnet dev-certs https --trust

# Restaure e compile a solução gerada.
dotnet restore LojaAspire
dotnet build LojaAspire --no-restore

# Revise também as dependências transitivas antes de adotar o exemplo.
dotnet list LojaAspire package --vulnerable --include-transitive

# Inicie a composição pelo AppHost, não por quatro terminais separados.
dotnet run --project LojaAspire/LojaAspire.AppHost

Na validação deste roteiro, o starter 13.6.4 foi gerado com .NET SDK 10.0.402 e compilou com zero erros e zero avisos. A auditoria com dependências transitivas não identificou pacotes vulneráveis conhecidos naquele momento; esse resultado não substitui novas verificações quando as bases de avisos de segurança forem atualizadas.

Use a URL do dashboard informada no console, sem presumir uma porta fixa ou remover o mecanismo de acesso gerado. Abra a interface web a partir da lista de recursos e acione a página que consulta a API. No starter, a demonstração costuma usar dados de previsão do tempo; o ponto importante é observar uma requisição real entre os projetos, não o domínio escolhido para o exemplo.

Minha verificação teria quatro partes: os recursos esperados aparecem em execução; a interface consegue consultar a API; a chamada produz sinais de telemetria; e os logs permitem distinguir a origem de uma falha. Se não houver trace, eu verificaria instrumentação, exportação e propagação, em vez de concluir imediatamente que a chamada não aconteceu.

Depois, em um teste local controlado, interrompa apenas a API do exemplo e repita a consulta. Observe o erro, os limites de espera e a recuperação quando o serviço voltar. Não faça isso em ambiente compartilhado. Esse exercício mostra uma fronteira importante: dependência configurada não significa dependência sempre disponível. Antes de usar o modelo em um produto, o comportamento de falha precisa ser tão compreensível quanto o caminho de sucesso.

Aspire, Docker Compose e Kubernetes têm papéis diferentes

Comparar Aspire com Docker Compose ou Kubernetes como se fossem alternativas perfeitamente equivalentes costuma produzir uma decisão ruim. Eles podem participar do mesmo fluxo, mas atuam em níveis diferentes. Aspire oferece um modelo de aplicação e ferramentas de desenvolvimento, observabilidade e implantação; Compose descreve e executa serviços em containers; Kubernetes opera workloads em um cluster com mecanismos próprios de reconciliação e disponibilidade.

NecessidadeOnde eu começaria a avaliar
Desenvolver serviços e dependências com referências explícitas e telemetria integradaAspire
Executar um conjunto pequeno de serviços já containerizados, com configuração conhecida pelo timeDocker Compose
Operar workloads em cluster com políticas, reconciliação e escalabilidadeKubernetes e sua plataforma operacional
Introduzir um modelo de aplicação e produzir artefatos para um destino suportadoAspire com a integração de implantação apropriada

O AppHost é especialmente relevante para a composição do desenvolvimento, mas a afirmação de que Aspire serve somente para iniciar processos locais também está incompleta. As versões atuais possuem recursos de publicação e implantação, com integrações para destinos como Docker Compose e Kubernetes. A maturidade e os requisitos de cada integração devem ser conferidos na versão adotada; recursos em preview não devem ser tratados como contratos estáveis sem avaliação.

Isso não transforma o AppHost no orquestrador permanente da produção. Mesmo quando um fluxo gera ou aplica artefatos, alguém precisa definir imagens, secrets, storage, rede, probes, políticas, backup e recuperação. O destino continua tendo seu próprio modelo operacional. Eu avaliaria tanto o resultado gerado quanto o processo de atualização e rollback.

Para um time que já opera bem com Compose, a adoção precisa demonstrar ganho concreto, não apenas trocar YAML por C#. Para uma plataforma Kubernetes existente, começaria pela experiência de desenvolvimento e pela integração com a entrega atual, evitando um segundo caminho de publicação sem proprietário claro. Aspire pode reduzir atrito; uma mudança de ferramenta ainda precisa respeitar o que já funciona.

Dicas e Boas Práticas

  • Comece pelo menor grafo útil. Uma interface e uma API já permitem observar descoberta e correlação. Acrescente banco, cache ou mensageria somente quando a aplicação precisar deles, não para preencher o dashboard.
  • Versione a configuração que reproduz o ambiente. Registre o SDK, os templates, os pacotes e as opções usadas. Atualize o conjunto com revisão e teste, sem tratar a palavra “latest” como estratégia de compatibilidade.
  • Diferencie referência, prontidão e resiliência. WithReference, WaitFor e políticas de falha resolvem problemas distintos. Verifique o comportamento depois da inicialização, incluindo indisponibilidade, cancelamento e limites de tempo.
  • Revise o que entra na telemetria. Um ambiente observável não precisa registrar tokens, payloads sensíveis ou dados pessoais. Defina atributos úteis, controle cardinalidade e trate o dashboard como uma interface com requisitos de segurança.
  • Mantenha ServiceDefaults fora do domínio. Compartilhe padrões técnicos pequenos e explícitos. Evite uma biblioteca central que obrigue todos os serviços a importar contratos e regras de negócio sem necessidade.
  • Planeje persistência e segredos separadamente. Um banco criado para desenvolvimento não é uma estratégia de dados de produção. Defina retenção local, restauração e tratamento de credenciais antes de usar informações reais.
  • Teste uma falha real no ambiente isolado. Faça uma chamada, observe o trace e interrompa uma dependência do exemplo com escopo conhecido. O aprendizado está em compreender a reação da aplicação, não apenas em obter indicadores verdes.

Resumo Objetivo

  • Aspire fornece ferramentas e integrações para compor, executar, observar e preparar a implantação de aplicações distribuídas; seu uso não exige transformar um monólito em microsserviços.
  • AppHost descreve recursos e relações. WithReference fornece configuração de referência; WaitFor expressa dependência de inicialização e não garante disponibilidade permanente.
  • ServiceDefaults é um projeto de padrões técnicos adotados pelos serviços, com configurações como OpenTelemetry, descoberta, health checks e resiliência HTTP.
  • Dashboard do Aspire apresenta sinais emitidos pela aplicação. Correlação depende de instrumentação e propagação de contexto; retenção, alertas e segurança exigem decisões operacionais.
  • Integrações de hosting e de cliente têm responsabilidades diferentes: adicionar um recurso ao modelo não faz o código de negócio começar a consumi-lo automaticamente.
  • Implantação com Aspire pode usar integrações para destinos como Docker Compose e Kubernetes, mas não elimina a configuração de secrets, persistência, rede, disponibilidade e recuperação.
  • Starter de referência: os comandos do artigo usam Aspire ProjectTemplates 13.6.4 e .NET 10; versões e opções precisam ser registradas para reproduzir o ambiente.

Leia Também

Referências