47  Mono-Repo Architecture: Flutter SPA + .NET WebAPI

This is a clean architecture where the .NET backend serves the Flutter SPA as static files. Let me design the structure for you.

47.1 Repository Structure

my-app/
│
├── Makefile                    # Orchestration commands
├── README.md
├── .gitignore
│
├── frontend/                   # Flutter Web SPA
│   ├── lib/
│   │   ├── main.dart
│   │   ├── app.dart
│   │   ├── features/
│   │   ├── shared/
│   │   └── core/
│   ├── web/
│   │   ├── index.html
│   │   └── manifest.json
│   ├── pubspec.yaml
│   └── analysis_options.yaml
│
├── backend/                    # .NET WebAPI
│   ├── src/
│   │   └── MyApp.Api/
│   │       ├── Controllers/
│   │       ├── Services/
│   │       ├── Models/
│   │       ├── Program.cs
│   │       ├── appsettings.json
│   │       ├── appsettings.Development.json
│   │       ├── MyApp.Api.csproj
│   │       └── wwwroot/        # <- Flutter build output goes here
│   │           └── .gitkeep
│   └── MyApp.Api.sln
│
└── docs/                       # Documentation
    └── architecture.md

47.2 Data Flow Diagram

┌─────────────────────────────────────────────────────────────────┐
│                         MONO-REPO                               │
│                                                                 │
│  ┌──────────────────┐         ┌──────────────────────────────┐  │
│  │     frontend/    │  build  │          backend/            │  │
│  │   (Flutter Web)  │ ──────► │    src/MyApp.Api/wwwroot/    │  │
│  │                  │         │                              │  │
│  │  lib/            │         │  ┌────────────────────────┐  │  │
│  │  web/            │         │  │   .NET WebAPI Server   │  │  │
│  │  pubspec.yaml    │         │  │                        │  │  │
│  └──────────────────┘         │  │  - Serves SPA (/)      │  │  │
│                               │  │  - API endpoints       │  │  │
│                               │  │    (/api/*)            │  │  │
│                               │  └────────────────────────┘  │  │
│                               └──────────────────────────────┘  │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                      Makefile                            │   │
│  │  - make build-frontend   (flutter build web)             │   │
│  │  - make build-backend    (dotnet build)                  │   │
│  │  - make build            (both + copy to wwwroot)        │   │
│  │  - make run              (run .NET server)               │   │
│  │  - make dev-frontend     (flutter run -d chrome)         │   │
│  │  - make clean            (cleanup)                       │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

47.3 Makefile

# === Configuration ===
FRONTEND_DIR := frontend
BACKEND_DIR := backend/src/MyApp.Api
WWWROOT_DIR := $(BACKEND_DIR)/wwwroot

# === Default ===
.PHONY: help
help:
    @echo "Usage: make [target]"
    @echo ""
    @echo "Targets:"
    @echo "  build           Build frontend and backend (production)"
    @echo "  build-frontend  Build Flutter web"
    @echo "  build-backend   Build .NET API"
    @echo "  run             Run .NET server (serves SPA + API)"
    @echo "  dev-frontend    Run Flutter in Chrome (hot reload)"
    @echo "  dev-backend     Run .NET in watch mode"
    @echo "  clean           Clean all build artifacts"
    @echo "  restore         Restore all dependencies"

# === Build ===
.PHONY: build build-frontend build-backend

build: build-frontend build-backend
    @echo "✅ Full build complete"

build-frontend:
    @echo "🔨 Building Flutter web..."
    cd $(FRONTEND_DIR) && flutter build web --release
    @echo "📦 Copying to wwwroot..."
    rm -rf $(WWWROOT_DIR)/*
    cp -r $(FRONTEND_DIR)/build/web/* $(WWWROOT_DIR)/

build-backend:
    @echo "🔨 Building .NET API..."
    cd $(BACKEND_DIR) && dotnet build -c Release

# === Run ===
.PHONY: run dev-frontend dev-backend

run:
    @echo "🚀 Running .NET server..."
    cd $(BACKEND_DIR) && dotnet run -c Release

dev-frontend:
    @echo "🔥 Running Flutter dev server..."
    cd $(FRONTEND_DIR) && flutter run -d chrome

dev-backend:
    @echo "🔥 Running .NET in watch mode..."
    cd $(BACKEND_DIR) && dotnet watch run

# === Utilities ===
.PHONY: clean restore

clean:
    @echo "🧹 Cleaning..."
    rm -rf $(FRONTEND_DIR)/build
    rm -rf $(WWWROOT_DIR)/*
    cd $(BACKEND_DIR) && dotnet clean

restore:
    @echo "📥 Restoring dependencies..."
    cd $(FRONTEND_DIR) && flutter pub get
    cd $(BACKEND_DIR) && dotnet restore

47.4 Key .NET Configuration

In Program.cs, configure to serve SPA fallback:

var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// Serve static files from wwwroot
app.UseDefaultFiles();
app.UseStaticFiles();

app.MapControllers();

// SPA fallback - return index.html for non-API routes
app.MapFallbackToFile("index.html");

app.Run();

47.5 Development Workflow

┌─────────────────────────────────────────────────────────────────┐
│                    DEVELOPMENT MODES                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Option A: Separate Dev Servers (Recommended for dev)           │
│  ─────────────────────────────────────────────────────          │
│                                                                 │
│    Terminal 1:  make dev-frontend   → localhost:3000 (Flutter)  │
│    Terminal 2:  make dev-backend    → localhost:5000 (API)      │
│                                                                 │
│    * Flutter hot-reload works                                   │
│    * Configure Flutter to proxy API calls to :5000              │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Option B: Integrated (Production-like)                         │
│  ─────────────────────────────────────────                      │
│                                                                 │
│    Terminal 1:  make build && make run  → localhost:5000        │
│                                                                 │
│    * Single server serves everything                            │
│    * No hot-reload (rebuild required)                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

47.6 .gitignore (Root)

# Flutter
frontend/build/
frontend/.dart_tool/
frontend/.packages

# .NET
backend/**/bin/
backend/**/obj/
backend/**/*.user

# wwwroot (generated)
backend/src/MyApp.Api/wwwroot/*
!backend/src/MyApp.Api/wwwroot/.gitkeep

# IDE
.idea/
.vscode/
*.swp

# OS
.DS_Store
Thumbs.db

47.7 Quick Start Commands

Command Description
make restore Install all dependencies
make dev-frontend Flutter hot-reload development
make dev-backend .NET watch mode
make build Production build (Flutter → wwwroot)
make run Run production server
make clean Clean all artifacts