Getting Started with .NET Aspire for PMCR-O

Orchestrate microservices with one F5. Set up ServiceDefaults. Build resilient agent architectures.

← Back to Articles

.NET Aspire is Microsoft's orchestration framework for cloud-native .NET applications. For PMCR-O projects, it provides service discovery, observability, and container management—allowing you to run your entire agent ecosystem with a single F5.

This guide walks you through setting up Aspire for PMCR-O, including the critical ServiceDefaults project that provides shared infrastructure for all your agent services.

Prerequisites

  • .NET 10 SDK (or later)
  • Docker Desktop (for container orchestration)
  • Visual Studio 2022 or VS Code with C# extension

Step 1: Create Solution Structure

Bash
# Create solution
mkdir PmcroAgents
cd PmcroAgents
dotnet new sln -n PmcroAgents

# Create AppHost (orchestrator)
dotnet new aspire-apphost -n PmcroAgents.AppHost

# Create ServiceDefaults (shared infrastructure)
dotnet new classlib -n PmcroAgents.ServiceDefaults

# Add to solution
dotnet sln add PmcroAgents.AppHost
dotnet sln add PmcroAgents.ServiceDefaults

Step 2: Configure ServiceDefaults

ServiceDefaults is the shared project that provides common infrastructure for all your services: OpenTelemetry, health checks, service discovery, and HTTP resilience.

ServiceDefaults.csproj

XML
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsAspireSharedProject>true</IsAspireSharedProject>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Extensions.Http.Resilience" />
    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http"/>
    <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
  </ItemGroup>
</Project>

Extensions.cs (ServiceDefaults)

This extension method configures OpenTelemetry, health checks, service discovery, and HTTP resilience for all services:

C#
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.ServiceDiscovery;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting
{
    public static class Extensions
    {
        public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) 
            where TBuilder : IHostApplicationBuilder
        {
            builder.ConfigureOpenTelemetry();
            builder.AddDefaultHealthChecks();
            builder.Services.AddServiceDiscovery();
            
            builder.Services.ConfigureHttpClientDefaults(http =>
            {
                http.AddStandardResilienceHandler();
                http.AddServiceDiscovery();
            });

            return builder;
        }

        public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) 
            where TBuilder : IHostApplicationBuilder
        {
            builder.Logging.AddOpenTelemetry(logging =>
            {
                logging.IncludeFormattedMessage = true;
                logging.IncludeScopes = true;
            });

            builder.Services.AddOpenTelemetry()
                .WithMetrics(metrics =>
                {
                    metrics.AddAspNetCoreInstrumentation()
                        .AddHttpClientInstrumentation()
                        .AddRuntimeInstrumentation();
                })
                .WithTracing(tracing =>
                {
                    tracing.AddSource(builder.Environment.ApplicationName)
                        .AddAspNetCoreInstrumentation()
                        .AddHttpClientInstrumentation();
                });

            return builder;
        }

        public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) 
            where TBuilder : IHostApplicationBuilder
        {
            builder.Services.AddHealthChecks()
                .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);

            return builder;
        }

        public static WebApplication MapDefaultEndpoints(this WebApplication app)
        {
            if (app.Environment.IsDevelopment())
            {
                app.MapHealthChecks("/health");
                app.MapHealthChecks("/alive", new HealthCheckOptions
                {
                    Predicate = r => r.Tags.Contains("live")
                });
            }

            return app;
        }
    }
}

Step 3: Create Your First Service

Now create a service that uses ServiceDefaults:

Bash
# Create service project
dotnet new web -n PmcroAgents.PlannerService

# Add ServiceDefaults reference
cd PmcroAgents.PlannerService
dotnet add reference ../PmcroAgents.ServiceDefaults

# Add to solution
cd ..
dotnet sln add PmcroAgents.PlannerService

Program.cs (Service)

C#
var builder = WebApplication.CreateBuilder(args);

// Add ServiceDefaults (OpenTelemetry, health checks, service discovery)
builder.AddServiceDefaults();

var app = builder.Build();

// Map health check endpoints
app.MapDefaultEndpoints();

app.MapGet("/", () => "Planner Service is running");

app.Run();

Step 4: Configure AppHost

The AppHost orchestrates all services. Update PmcroAgents.AppHost/Program.cs:

C#
var builder = DistributedApplication.CreateBuilder(args);

// Add your service
var plannerService = builder.AddProject<Projects.PmcroAgents_PlannerService>("planner");

builder.Build().Run();

Step 5: Run Everything

Set PmcroAgents.AppHost as your startup project and press F5. Aspire will:

  • Start all services
  • Open the Aspire dashboard (observability UI)
  • Enable service discovery between services
  • Configure OpenTelemetry tracing

Why ServiceDefaults Matters: Without it, each service would need to configure OpenTelemetry, health checks, and service discovery individually. ServiceDefaults centralizes this configuration, making your architecture resilient by design—a core PMCR-O principle.

Next Steps

Build Your Own Strange Loop

The PMCR-O framework is open. Star the repository. Fork it. Seed your own intent.

View on GitHub →