November 17th, 2025
heart1 reaction

Aspire Multi-Repo Microservices – Windows 365 Integration Journey

Windows 365 uses a microservice architecture based on HTTP services built with ASP.NET Core and Azure Functions. Each service has its own repository and team for clear ownership. All microservices share a common infrastructure, with teams focusing on their designated service.

Aspire streamlines microservice development with an application model for managing services, containers, and dependencies, plus built-in resiliency and observability features. It helps the Windows 365 infrastructure team support local testing and debugging of distributed apps, ensuring consistency across environments.

We began using Aspire earlier this year and encountered challenges with its support for multi-repo microservices, as discussed in Add Multi repo support dotnet/aspire #1137. Fortunately, Aspire offers extension points that enabled us to build multi-repo microservice support. This article shares how we’re enhancing multi-repo microservice productivity with Aspire for Windows 365.

Streamlined Team Workflow with F5 Experience

Below is a diagram illustrating the streamlined workflow for microservice teams using Aspire with a multi-repo setup. Each microservice team has AppHost and ServiceDefaults projects in its repository, enabling management of dependencies and runtime settings independently. One shared infra repo hosts microservice container and emulator integration for all microservices, as well as service discovery and seed data support described in later sections.

Diagram: Aspire AppHost local workflow

For each microservice team, Dev Box is used for local development, with the service repo cloned locally. Docker Desktop, Visual Studio, and WSL are pre-installed, so an engineer just opens the solution and presses F5 to start debugging and testing. This is the F5 experience.

Each repository includes its own AppHost and ServiceDefaults projects so every microservice manages its dependencies and runtime settings independently. The AppHost wires up required containers and resources, ensuring consistent local environments. This enables:

  • Fast onboarding (clone and run)
  • Independent iteration per service
  • Consistent dependency provisioning
  • Predictable local debugging

MiniCloudPC demo for F5 Experience

Below is a short demo video showing the end-to-end F5 workflow with multi-repo microservices.These are the actual samples we use internally, designed to closely replicate real microservice interactions pattern without expose internal production code.

If the embedded video does not play, you can view it directly: MiniCloudPC F5 Experience Demo.

Multi-Repo Microservice Support

Windows 365 enhances multi-repo microservice productivity by building on Aspire’s extension points and native primitives. The shared infra team introduced the MicroserviceResource abstraction and the AddMicroservice extension, which streamline support for project and container resources, enable consistent dependency management, and improve developer workflows. This lets teams independently manage, test, and debug microservices across multiple repositories with improved visibility and automation.

Microservice Container

A resource is the fundamental unit in the Aspire app model. Aspire provides three built-in resource types: Project, Executable, and Container. On top of the container resource, Windows 365 introduced the MicroserviceResource abstraction to simplify the team experience with the AddMicroservice extension for direct dependencies:

var cloudPcDeviceService = builder
    .AddMicroservice(PartnerService.CloudPCDeviceService);

AddMicroservice adds a container resource whose image name is derived from a shared Azure Container Registry naming convention. By default it uses the latest tag; teams can override tags for version pinning. Images are published in release pipelines.

Below is the image publish pipeline which uploads the microservice images to the shared ACR with proper tagging and a consistent naming convention.

Diagram: Aspire Image publish pipeline

At runtime, Aspire pulls the microservice image from ACR based on the naming convention and specified tag. ACR authentication is handled with Aspire interactive services, prompting for login if needed.

Microservice Dependency (Indirect Dependencies)

For transitive dependency graphs (Service A -> Service B -> Service C across separate repos) we terminate the chain using a microservice emulator with the RunAsEmulator extension. This swaps the real service image for a shared emulator image and loads HTTP response templates derived from the real service plus local overrides.

var cloudPcDeviceService = builder
    .AddMicroservice(PartnerService.NetworkService)
    .RunAsEmulator();

Behind the scenes, RunAsEmulator replaces the container image with the shared microservice emulator image and pulls the HTTP template from the real service image plus any local caller overrides.

Microservice Emulator

We leveraged the Visual Studio HTTP file format with a minor adjustment to support HTTP response templates. For distinction, we use the .rest extension for templates. Within a template, response headers and bodies are specified with support to bundle multiple response templates together using ### separators.

Each microservice bundles its own REST templates into the container image for upstream services to use, with an option for the caller service to override locally.

Example response template:

@name=GetOrder1
GET http://localhost/getorder/{name}/am/{age}?number=1&order=2
AddressNumber: {{query.number}}

{
  "Name": "{{route.name}}",
  "Age": "{{route.age}}",
  "Address": "Local",
  "Order": "{{query.order}}"
}

Most HTTP file template features are supported, plus variable binding for route, query, header, and body parameters. Teams like this simple yet powerful approach to emulate indirect dependencies.

Azure Dependency Emulators

Microservices also rely on Azure resources, and Aspire offers built-in support for many (Cosmos DB, Event Hubs, Azure Storage, Azure App Configuration). However, there are resources we use (Azure Key Vault, Microsoft Entra ID, Azure Resource Manager (ARM), and Microsoft Graph) that are not yet natively supported. For these we apply the same REST template approach and package them into a common emulator hub image.

The team created .rest files for common requests to Microsoft Graph, Azure Resource Manager, Microsoft Entra ID, Microsoft Intune, etc., covering typical responses. For instance, a request to Graph’s /users endpoint might return a pre-captured JSON list of users. These templates can be generic or Windows 365 specific.

When a microservice calls one of these external endpoints, service discovery reroutes the call to the local emulator hub, which serves the response from the template, similar to microservice emulation. This provides consistent local behavior without live cloud dependencies.

Service Discovery

For microservice resources we provide a client library integrated with service discovery to locate and call their dependencies. This is implicit for all service-to-service calls. Calling WithReference is optional, providing an optimal experience and reduced boilerplate for microservice owners.

Internally we use a service map to maintain metadata and resolve endpoints from Aspire. As services and emulators start, their endpoints are populated into the service map, accessible by the infra library.

This enables seamless service-to-service communication without manual configuration. The integration ensures that microservices can discover and interact locally as they would in production, reducing setup errors.

Seed Data Support

Manually preparing test data was repetitive and time-consuming. We introduced seed data support using Aspire lifecycle hooks and application model features to automatically import and export Cosmos DB seed data maintained in the repository. This ensures developers start with a consistent database state and quickly access needed test scenarios.

Diagram: Aspire Seed Data Support

Cloud Test with Azure DevOps Agent Pool

Aspire end-to-end tests run on CloudTest as part of CI/CD to ensure only stable builds are promoted. We use an Azure DevOps Agent Pool with pre-configured images containing Docker to execute distributed Aspire E2E tests before pull request merge, enforcing quality standards.

Diagram: Aspire Cloud Test

Wrapping Up

By extending Aspire with a microservice resource, emulators, seed data automation, and cloud E2E gating, Windows 365 accelerates multi-repo microservice development while keeping teams autonomous and environments consistent.

Adopting Aspire shortened local setup, test, and debug cycles from hours (or days) to minutes. In about six months we completed multi-repo support, and more than 15 microservices have onboarded while adoption continues to grow. Emulator + seed data + cloud E2E integration improved reliability while preserving independent repository ownership.

As Aspire evolves, we will adopt new capabilities. We are exploring deployment support, runner support, UX testing with Playwright, and deeper observability dashboards leveraging Aspire diagnostics. More updates to come.

Author

0 comments