Files
ZeroSpace/ZeroSpace/.junie/plans/add-aspire-k8s-postgres.md
T

11 KiB

sessionId
sessionId
session-260925-235814-7y2w

Requirements

Overview & Goals

The goal is to modernize the ZeroSpace application by introducing .NET Aspire orchestration, adding a dedicated backend GraphQL API service powered by Hot Chocolate with Nitro UI (Banana Cake Pop), integrating a PostgreSQL database, containerizing components with Docker, and managing deployments on Kubernetes using industry-standard tooling (Aspirate / K8s manifests).

Scope

  • In Scope:
    • Creation of .NET Aspire AppHost (ZeroSpace.AppHost) and shared Service Defaults (ZeroSpace.ServiceDefaults).
    • Creation of an ASP.NET Core GraphQL API service (ZeroSpace.ApiService) using Hot Chocolate with Entity Framework Core & PostgreSQL (Npgsql) support.
    • Interactive Nitro UI (Banana Cake Pop) GraphQL IDE integration at the /graphql endpoint for rapid schema exploration and testing.
    • GraphQL queries, mutations, filtering, sorting, and projections for UnitData.
    • PostgreSQL container provisioning and database connection orchestration via Aspire.
    • Updating ZeroSpace.Web (Blazor WASM) to consume GraphQL operations from ZeroSpace.ApiService.
    • Docker containerization for both ZeroSpace.ApiService and ZeroSpace.Web.
    • Kubernetes manifest management and Aspirate integration for cluster deployment.
  • Out of Scope:
    • Modifying the unit model generation logic in ZeroSpace.Build.
    • Cloud-specific managed Kubernetes cluster creation scripts (e.g. Terraform/Bicep for AKS/EKS).

User Stories

  • As a Developer, I want to start the web app, GraphQL API, and PostgreSQL database with a single command using .NET Aspire, and use Nitro UI to interactively explore and query schemas so that local development and testing maximize productivity.
  • As an Operator, I want to generate Kubernetes deployment manifests from the Aspire architecture so that all services and databases deploy consistently to production Kubernetes clusters.
  • As a User, I want to browse and manage ZeroSpace unit data through the Blazor Web UI backed by efficient, precision GraphQL data fetching and persistent database storage.

Functional Requirements

  • FR-1: The Aspire AppHost must orchestrate the PostgreSQL container, GraphQL API service, and Web app with configured dependencies and environment variables.
  • FR-2: The ZeroSpace.ApiService must expose a Hot Chocolate GraphQL server with schema filtering, sorting, and projection capabilities over PostgreSQL UnitData.
  • FR-3: The Nitro UI GraphQL IDE must be hosted and accessible at /graphql to enable in-browser schema exploration, query execution, and debugging.
  • FR-4: Database schema migrations and initial seed data from ZeroSpace.Model.Units.All must be executed automatically upon startup.
  • FR-5: ZeroSpace.Web must execute GraphQL queries/mutations against ZeroSpace.ApiService using configured HTTP endpoints.
  • FR-6: Container definitions (Dockerfiles) must exist for all custom applications.
  • FR-7: Kubernetes manifests must specify Deployments, Services, PersistentVolumeClaims, Secrets, and Ingress routing for Web and GraphQL endpoints.

Non-Functional Requirements

  • Observability: OpenTelemetry tracing, metrics, and structured logging enabled across services via ServiceDefaults (including Hot Chocolate diagnostic tracing).
  • Resilience & Health: Health check endpoints (/health, /alive) configured for Kubernetes readiness and liveness probes.
  • Security: Database credentials managed through environment variables, Secrets, and Aspire connection strings without hardcoded passwords.
  • Query Efficiency: GraphQL projections ensure only requested fields are fetched from PostgreSQL, minimizing network transfer and database payload overhead.

Technical Design

Current Implementation

  • ZeroSpace.Web is a standalone Blazor WebAssembly application (Microsoft.NET.Sdk.BlazorWebAssembly) targeting .NET 10, using native Blazor UI components only.
  • ZeroSpace.Model contains records like UnitData.cs, UnitEfficiency.cs, and generated static unit arrays in Units.g.cs.
  • ZeroSpace.Deploy publishes static files to Azure Static Web Apps.
  • ZeroSpace.AppHost, ZeroSpace.ServiceDefaults, and ZeroSpace.ApiService are integrated into the solution.

Key Decisions

  1. Hot Chocolate GraphQL Server with Nitro UI (ZeroSpace.ApiService):
    • Decision: Introduce Hot Chocolate GraphQL server (HotChocolate.AspNetCore, HotChocolate.Data.EntityFramework) and enable the embedded Nitro UI (Banana Cake Pop) GraphQL tool suite.
    • Rationale: GraphQL enables client-driven data fetching, reducing over-fetching and network serialization overhead. Nitro UI provides a rich developer and operator environment for real-time schema introspection, query building, and mutation execution directly in the browser.
  2. .NET Aspire Orchestration & Aspirate for Kubernetes:
    • Decision: Use .NET Aspire AppHost for local orchestration and developer dashboard, and use Aspirate (Aspir8) alongside standard Kubernetes manifests.
    • Rationale: Aspire provides official .NET cloud-native developer tooling, OpenTelemetry defaults, and service discovery, while Aspirate transforms the Aspire manifest into production-ready Kubernetes Helm/Kustomize/YAML artifacts.
  3. Database Architecture & Seeding:
    • Decision: Entity Framework Core with Npgsql.EntityFrameworkCore.PostgreSQL and PostgreSQL 17 container.
    • Rationale: Native EF Core support with Aspire component Aspire.Npgsql.EntityFrameworkCore.PostgreSQL enables automatic connection string binding, resilient retries, and database health checks.

Architecture Diagram

graph TD
  subgraph Local / Dev [Local .NET Aspire AppHost]
    AppHost[ZeroSpace.AppHost]
    Dash[Aspire Dashboard]
    Nitro[Nitro UI / Banana Cake Pop]
    AppHost --> Dash
    AppHost --> Nitro
  end

  subgraph Kubernetes Cluster [Kubernetes Cluster]
    Ingress[Kubernetes Ingress / Gateway]
    
    subgraph Web Pod [Web Deployment]
      Web[ZeroSpace.Web / Nginx]
    end

    subgraph API Pod [API Deployment]
      API[ZeroSpace.ApiService / Hot Chocolate]
    end

    subgraph DB Pod [PostgreSQL StatefulSet / Pod]
      DB[(PostgreSQL 17)]
      PVC[PersistentVolumeClaim]
      DB --- PVC
    end
  end

  Ingress --> Web
  Ingress --> API
  Web -->|GraphQL Queries / Mutations| API
  Nitro -->|Interactive Queries / Schema Docs| API
  API -->|EF Core / Npgsql| DB

Components

  • ZeroSpace.AppHost: Aspire orchestrator project that links PostgreSQL container, ZeroSpace.ApiService, and ZeroSpace.Web.
  • ZeroSpace.ServiceDefaults: Shared project configuring OpenTelemetry, health checks, discovery, and resilience policies.
  • ZeroSpace.ApiService: ASP.NET Core GraphQL API powered by Hot Chocolate, hosting Nitro UI at /graphql, and handling PostgreSQL persistence with EF Core.
  • ZeroSpace.Web: Client-side Blazor WASM UI configured with IUnitsApiClient communicating with ZeroSpace.ApiService via GraphQL.
  • k8s/: Kubernetes manifests (Deployments, Services, PVC, Ingress, Secrets) and Aspirate generation configuration.

File Structure

ZeroSpace/
├── ZeroSpace.AppHost/
│   ├── Program.cs
│   ├── appsettings.json
│   └── ZeroSpace.AppHost.csproj
├── ZeroSpace.ServiceDefaults/
│   ├── Extensions.cs
│   └── ZeroSpace.ServiceDefaults.csproj
├── ZeroSpace.ApiService/
│   ├── Data/
│   │   ├── ZeroSpaceDbContext.cs
│   │   └── DbInitializer.cs
│   ├── GraphQL/
│   │   ├── Query.cs
│   │   ├── Mutation.cs
│   │   └── Types/
│   ├── Dockerfile
│   ├── Program.cs
│   └── ZeroSpace.ApiService.csproj
├── ZeroSpace.Web/
│   ├── Services/
│   │   ├── IUnitsApiClient.cs
│   │   └── UnitsApiClient.cs
│   ├── Dockerfile
│   ├── nginx.conf
│   └── ...
├── k8s/
│   ├── postgres-pvc.yaml
│   ├── postgres-deployment.yaml
│   ├── apiservice-deployment.yaml
│   ├── web-deployment.yaml
│   └── ingress.yaml
├── aspirate.json
└── ZeroSpace.sln

Data Models / Contracts


# GraphQL Schema Contract

type Query {
  units(
    where: UnitDataFilterInput
    order: [UnitDataSortInput!]
  ): [UnitData!]!
  
  unitById(id: String!): UnitData
}

type Mutation {
  createUnit(input: CreateUnitInput!): UnitData!
  updateUnit(id: String!, input: UpdateUnitInput!): UnitData!
  deleteUnit(id: String!): Boolean!
}

Risks & Mitigations

  • Risk: GraphQL N+1 or deep query performance impacts.
    • Mitigation: Leverage Hot Chocolate's [UseProjection], [UseFiltering], and [UseSorting] which translate GraphQL AST directly into optimized PostgreSQL SQL via EF Core IQueryable.
  • Risk: Nitro UI access across different deployment environments.
    • Mitigation: Configure Nitro UI / Banana Cake Pop middleware settings so developer tooling is available locally and behind authorized Ingress routes in staging/production as needed.
  • Risk: Blazor WASM CORS issues when executing GraphQL POST requests.
    • Mitigation: Configure permissive CORS policies in ZeroSpace.ApiService for GraphQL endpoints and route requests through Kubernetes Ingress.

Testing

Validation Approach

  • Verify local orchestration via dotnet run --project ZeroSpace.AppHost.
  • Access Nitro UI at http://localhost:5142/graphql (or through Aspire Dashboard endpoint) to test interactive queries, mutations, and schema documentation.
  • Test GraphQL queries and mutations executed by ZeroSpace.Web against ZeroSpace.ApiService.
  • Build and run Docker containers for ZeroSpace.ApiService and ZeroSpace.Web.
  • Validate Kubernetes manifest syntax and Aspirate manifest compilation (aspirate generate).

Key Scenarios

  1. Aspire Local Launch: Starting ZeroSpace.AppHost boots PostgreSQL container, runs EF Core migrations, starts ZeroSpace.ApiService with Hot Chocolate GraphQL & Nitro UI, and launches ZeroSpace.Web.
  2. Nitro UI Interactive Execution: Open /graphql in browser; Banana Cake Pop / Nitro UI loads; execute { units { id name hexite flux tier faction } } and receive structured JSON response.
  3. Blazor UI Data Flow: Navigating to /units in the Blazor UI sends a GraphQL query payload to ZeroSpace.ApiService, which queries PostgreSQL and returns unit records.
  4. Container Build: docker build succeeds for both ZeroSpace.ApiService and ZeroSpace.Web Dockerfiles.
  5. Kubernetes Deployment: Applying the generated Kubernetes manifests provisions the PVC, deploys PostgreSQL, starts API/Web pods, and exposes them via Service and Ingress.

Edge Cases

  • PostgreSQL Connection Failures: GraphQL queries gracefully return structured GraphQL errors and health checks report unhealthy status.
  • Empty Database: Seeding logic populates initial units from ZeroSpace.Model if the database table is empty before serving GraphQL requests.
  • Complex GraphQL Filtering: Verify filtering by tier and faction compiles to efficient SQL WHERE clauses in PostgreSQL without in-memory buffering.

Execution Plan

✓ Step 1: Configure Hot Chocolate GraphQL and Nitro UI in ZeroSpace.ApiService

✓ Step 2: Update ZeroSpace.Web to consume GraphQL API operations

✓ Step 3: Update AppHost, Ingress, and Kubernetes configuration for GraphQL endpoint

✓ Step 4: Build and Verify Solution