A production-ready modular .NET 10 monolith + two React 19 apps, built for enterprise SaaS.
This file is the canonical guide for all AI coding tools (Claude Code, Gemini CLI, Cursor, Codex, …).
CLAUDE.md and GEMINI.md are thin bridges that import this file — edit conventions here, not there.
This file is the map. Detailed conventions live in .agents/rules/ and are read on demand — read the
relevant rule file before working in that area (see the index below). Keep this file lean.
A modular monolith (Vertical Slice Architecture) backend that ships with two React + Vite front-ends and a CLI. Multitenancy, auth, auditing, billing, files, chat and more are first-class.
- Backend — .NET 10, EF Core 10, PostgreSQL, Redis, JWT + ASP.NET Identity, Finbuckle multitenancy, Hangfire, OpenAPI/Scalar, Serilog + OpenTelemetry, .NET Aspire.
- Frontends —
clients/admin(operator-facing) andclients/dashboard(tenant-facing): React 19, Vite 7, TypeScript, TanStack Query v5, React Router 7, Radix + Tailwind v4 (shadcn-style), SignalR/SSE.
| Path | What |
|---|---|
src/BuildingBlocks/ |
Shared framework libraries (Core, Persistence, Web, Caching, Eventing, Storage, Quota…). Protected — see below. |
src/Modules/{Name}/ |
Bounded contexts. Each has a runtime project + a .Contracts project (its only public API). |
src/Host/FSH.Starter.Api |
Composition-root Web API host. |
src/Host/FSH.Starter.AppHost |
.NET Aspire orchestrator (Postgres, Redis, MinIO, migrator, API, both React apps). |
src/Host/FSH.Starter.DbMigrator |
One-shot migrate/seed runner. DB is not migrated at API startup. |
src/Host/FSH.Starter.Migrations.PostgreSQL |
All EF migrations, organized per-module by folder. |
src/Tests/ |
Per-module tests, Architecture.Tests (NetArchTest), Integration.Tests (Testcontainers). |
src/Tools/CLI |
The fsh CLI (Spectre.Console). |
clients/admin, clients/dashboard |
The two React apps. |
deploy/ |
Infra (docker, terraform, dokploy). |
| Backend | Frontend | ||
|---|---|---|---|
| Framework | .NET 10 / C# latest | Framework | React 19 + Vite 7 + TS 5.x |
| CQRS | Mediator 3.x (source-gen) | Data | TanStack Query v5 |
| Validation | FluentValidation 12.x | Routing | React Router 7 |
| ORM / DB | EF Core 10 / PostgreSQL (Npgsql) | UI | Radix + Tailwind v4 + CVA (shadcn) |
| Auth | JWT Bearer + ASP.NET Identity | Forms | react-hook-form + zod (admin only) |
| Multitenancy | Finbuckle 10.x | Realtime | @microsoft/signalr, SSE (dashboard) |
| Cache / Jobs | Redis, Hangfire | Tests | Playwright (route-mocked) |
| Docs | OpenAPI + Scalar | API client | hand-written apiFetch (no codegen) |
| Hosting | .NET Aspire | Env | runtime /config.json (not VITE_*) |
| Testing | xUnit, Shouldly, NSubstitute, AutoFixture, NetArchTest, Testcontainers |
# Whole stack (Postgres + pgAdmin + Redis + MinIO + migrator + API + both React apps)
dotnet run --project src/Host/FSH.Starter.AppHost # one-time: npm install in clients/admin & clients/dashboard
dotnet build src/FSH.Starter.slnx # build backend
dotnet run --project src/Host/FSH.Starter.Api # API only → https://localhost:7030 (/scalar)
dotnet test src/FSH.Starter.slnx # tests — integration tests REQUIRE Docker
cd clients/admin && npm install && npm run dev # → http://localhost:5173
cd clients/dashboard && npm install && npm run dev # → http://localhost:5174Migrations / seed (DbMigrator, separate step):
dotnet run --project src/Host/FSH.Starter.DbMigrator -- apply [--seed]
dotnet run --project src/Host/FSH.Starter.DbMigrator -- list-pendingPorts: API 7030 (https)/5030 (http) · admin 5173 · dashboard 5174 · Postgres 5432 · pgAdmin 5050 · Valkey 6379 · MinIO 9000/9001.
- Module boundaries — a module references another module only through its
.Contractsproject, never its runtime project. Enforced byArchitecture.Tests. - Registering a module touches FOUR places —
Program.csMediatoro.Assemblies(two markers each) +moduleAssembliesarray, and the identical pair inDbMigrator/Program.cs. A missing Mediator marker = handlers silently undiscovered. Seearchitecture.md. - Tenant isolation is default-ON via
BaseDbContext. Opt out only viaIGlobalEntity. Subclass DbContexts callbase.OnModelCreatinglast. Seedatabase.md. - Do NOT modify
src/BuildingBlockswithout explicit approval — shared by every module, wide blast radius. - Mediator handlers must be
public sealed, returnValueTask<T>, and.ConfigureAwait(false)every await. - Structured logging only — no string interpolation in log messages; use message templates /
[LoggerMessage]. - Propagate
CancellationTokeninto every EF/IO call; add as= defaulton public service methods. - Every command handler + paginated query handler needs a validator (
{Name}Validator). Enforced byArchitecture.Tests. - Frontend: pass per-call data through
mutate(arg), never via state the mutation callbacks close over (execute-time race). Seefrontend/shared.md. - Docs + changelog travel with the change — a user-facing change (feature, endpoint, config, infra, breaking change) isn't done until the separate docs repo (
github.com/fullstackhero/docs, the Astro site) is updated to match and a changelog entry is added (src/content/docs/changelog/). Don't let the docs drift from the code.
Backend / cross-cutting (.agents/rules/)
| Working on… | Read |
|---|---|
| Module structure, boundaries, registration, DI, middleware order, config | architecture.md |
| Endpoints, CQRS, validation, exceptions, permissions, versioning | api-conventions.md |
| EF Core, entities, migrations, tenant isolation, query filters | database.md |
| Cross-module events, Outbox/Inbox, idempotent handlers | eventing.md |
| Caching (HybridCache/Redis), keys, invalidation | caching.md |
| Background jobs (Hangfire), recurring jobs | jobs.md |
| Outbound HTTP resilience (Polly) | resilience.md |
| Files/blobs, presigned uploads, providers | storage.md |
| CORS, security headers, rate limiting, idempotency, quotas | security.md |
| SignalR / SSE backend | realtime.md |
| Logging, correlation, OpenTelemetry | logging.md |
| Unit test conventions, NetArchTest | testing.md |
| Integration tests (Testcontainers harness + gotchas) | integration-testing.md |
Modifying src/BuildingBlocks (read first — it's protected) |
buildingblocks-protection.md |
| A specific module's quirks | modules/{module}.md (identity, multitenancy, chat, files, webhooks, auditing, billing, catalog, tickets, notifications) |
Frontend (.agents/rules/frontend/)
| Working on… | Read |
|---|---|
| Any React work (shared stack, API client, Query, Tailwind, design language) | frontend/shared.md |
The operator app (clients/admin) |
frontend/admin.md |
The tenant app (clients/dashboard) |
frontend/dashboard.md |
File-scoped namespaces · 4-space indent · explicit types (var only when RHS-obvious) · is null /
is not null · pattern matching + switch expressions · ArgumentNullException.ThrowIfNull guards ·
records for DTOs/events/value objects · default! for required non-nullable strings. Build runs with
TreatWarningsAsErrors — warnings fail the build.
- Feature — Contracts command/query → handler → validator → endpoint → wire in module
MapEndpoints()→ tests. Details:api-conventions.md. - Module — new
Modules.{Name}+.Contracts, implementIModulew/ assembly-level[assembly: FshModule(typeof(XModule), order)], register in all four places, add migration folder + tests. Details:architecture.md. - React page — API module (
src/api/) → page → register lazy route → (admin) mirror permission + RouteGuard → Playwright test. Details:frontend/shared.md.
- Rules —
.agents/rules/*.md(indexed above). Read on demand. - Skills —
.agents/skills/*/SKILL.md: step-by-step task recipes. Scaffolders:add-feature,add-entity,add-module,add-react-page,add-full-slice. Ops:create-migration,add-integration-event,add-permission. Reference:query-patterns,testing-guide,mediator-reference. - Workflows —
.agents/workflows/*.md: task playbooks (code-reviewer,feature-scaffolder,module-creator,architecture-guard,migration-helper).