Project structure
NetForge is a single solution with two projects: an ASP.NET Core API and a React SPA. The layout is deliberately flat and predictable — once you've seen one feature, you've seen them all.
NetForge.sln
├─ NetForge.Server/ # ASP.NET Core 10 API (minimal APIs)
│ ├─ Features/ # one folder per vertical slice
│ │ ├─ {Domain}/ # Endpoints, Models, Validators, EfConfig, Mappings, Permissions
│ │ └─ _Template/ # copy-me scaffolding (ignored by feature discovery)
│ ├─ Platform/ # cross-cutting: errors, auth, audit, settings, jobs, …
│ ├─ Data/ # AppDbContext + migrations
│ └─ Program.cs # composition root — you rarely touch this
└─ netforge.client/ # React 19 + Vite + TypeScript SPA
├─ src/pages/ # file-system routes (the path tree IS the URL tree)
├─ src/components/ # shared UI (incl. shadcn/ui primitives)
├─ src/lib/api/ # one typed API module per backend slice
├─ src/widgets/ # dashboard widgets
└─ src/locales/ # i18n message catalogsBackend: vertical slices
Each feature is a self-contained folder under Features/{Domain}/, typically six files:
| File | Holds |
|---|---|
Endpoints.cs | the minimal-API routes (these are the handlers) |
Models.cs | request/response DTOs (records) + entities |
Validators.cs | FluentValidation rules |
EfConfig.cs | the EF Core entity configuration |
Mappings.cs | entity → DTO mapping (plain static methods) |
Permissions.cs | the slice's permission constants |
Slices auto-register by reflection — a marker interface is discovered at startup — so you never edit Program.cs to add a feature. A folder prefixed with _ (like _Template) is treated as scaffolding and skipped.
Frontend: file-system routes
Screens live under src/pages/, and the folder tree maps directly to URLs:
index.tsxis the screen (default export); optional same-filePending/Catchexports cover loading and error states._layout.tsxwraps a subtree;meta.tsholds{ title, permissions }.- Route groups like
(app)organize without affecting the URL. - Any
_-prefixed file or directory is ignored by the router (_layout.tsx,_template/).
Routes are generated automatically by the Vite dev server — there's no route config to maintain.
Cross-cutting concerns: the filter pipeline
Validation, audit logging, performance timing, and transactions are applied as an IEndpointFilter pipeline per slice — not re-implemented per handler. This is why a handler can be a few lines: the cross-cutting work happens around it. See Architecture for the full picture.
Where to start
Copy Features/_Template/ (backend) or src/pages/_template/ (frontend) — the scaffolding is the canonical shape. The Add a feature recipe walks through it end to end.