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 restore47.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 |