Modeling Your Environment with Aspire

David Fowler
4 min readApr 7, 2025

--

When I talk about modeling in Aspire, I’m talking about describing your application and its environment in a way that a tool can understand — not just a human.

That might sound simple at first. You list your services. You point to a database. Maybe add a frontend. But real-world applications are rarely that clean. And most of what your app really needs to run ends up living in tribal knowledge: in README files, environment variable exports, Slack messages, and someone’s memory.

Where is the contract for what an application needs to execute?

  • What environment variables does it require?
  • What command-line arguments?
  • What protocols does it use to talk to other services?
  • What kind of connection string does Redis require?
  • What format? What authentication mechanism?

Most of these answers are “known” by the team, but not modeled in a way that enables tooling to help.

Aspire changes that. It gives you a structured, programmable way to model your application — its shape, its dependencies, and the assumptions it makes about its environment.

Think of It Like Contract-First Development

If you’ve worked with OpenAPI or Protobuf, you already understand the power of modeling.

When you define an API with OpenAPI or gRPC first, you’re saying: “This is the contract between systems. This is what I expect. This is what I produce.”

Aspire brings that same philosophy to application topology.

You’re not just saying “this app needs Redis” — you’re describing what kind of Redis, what it connects to, what shape the connection should take, and how it’s expected to behave across environments.

That model becomes:

  • A source of truth
  • A contract for platform teams
  • A tool-friendly representation that can be reasoned about, validated, and transformed

Just like contract-first APIs enabled automation (e.g. client codegen, testing tools, mocks), Aspire enables automation around infrastructure, configuration, and deployment.

A Simple Example

Let’s say you have a JavaScript frontend and a C# backend that talks to a PostgreSQL database.

  • In development, the database is just a container you run locally.
  • In production, the database is managed by another team and accessed via an external connection string.

With Aspire, you can model both cases:

  • Use a local container resource for dev.
  • Use an external connection string resource for production.
  • Both modeled the same way in your application graph.

This lets your frontend project reference the backend, and the backend reference the database, with clear, inspectable relationships between them. Aspire handles the wiring — including the connection string format, the credentials, and environment-specific behavior.

A More Complex Scenario

Now imagine this:

  • A React frontend
  • A C# API backend
  • A Redis cache
  • A background worker
  • A shared PostgreSQL database
  • An external payment service (e.g. Stripe)
  • A centralized observability stack

In development:

  • Redis and Postgres are containers.
  • Aspire dashboard for observability locally.
  • Stripe is replaced with a local mock.

In staging or production:

  • Redis is a managed service provided by your infrastructure team.
  • Postgres is provisioned externally.
  • Observability is wired into your real telemetry stack.
  • Stripe is real, with secrets passed in securely.

With Aspire:

  • Each of these is modeled as a resource.
  • They are defined in your environment setup and resolved at publish time.

This means developers can build and test their app as if everything were local, but deploy it into production with real infrastructure — without rewriting config, patching YAML, or introducing runtime surprises.

Aspire Works With What You Already Have

You don’t need to rewrite your app from scratch to benefit from Aspire.

Aspire is designed to model around your existing infrastructure. It can:

  • Wrap existing services and APIs as external resources
  • Integrate with your current configuration and secret management tools
  • Represent hosted resources managed by another team
  • Work with custom deployment processes via publishers

This makes Aspire ideal not just for greenfield projects, but for extending, modernizing, or stabilizing legacy apps.

Whether you’re trying to capture what’s already there or build something new, Aspire lets you do it incrementally. Start by modeling the pieces you understand — then grow from there.

Modeling is Just the Beginning

Making things possible is step one. Aspire gives you the primitives to define contracts and components clearly.

The next step is making it easy — making the pieces snap together intuitively for common application patterns.

We’re building toward an experience where modeling a cache or a database isn’t just accurate — it’s convenient. Where connecting a frontend to a backend, or an app to a message queue, happens with minimal ceremony but full clarity.

Aspire doesn’t just make structured modeling possible. It’s evolving to make modern application assembly feel like snapping together building blocks. And the more structure you model, the more Aspire (and your tooling) can do for you.

Why Modeling Matters

By modeling your app:

  • You create a contract between your app and the infrastructure it needs.
  • You unlock tooling: Aspire knows what to start, what to connect, and how to validate.
  • You reduce onboarding friction: new devs don’t need to guess what your app needs.
  • You make your system inspectable: tools, platforms, and even AIs can understand it.

Aspire lets you move from “tribal knowledge and trial-and-error” to a system that understands the shape of your app — and can do something with it.

In future posts, we’ll dive into how this model powers dev experience, publishing, and platform integration. But it all starts here: with a model.

--

--

David Fowler
David Fowler

Written by David Fowler

Distinguished Engineer at Microsoft

Responses (2)