Skip to content

Architecture

NetForge is held together by five ideas. Internalize these and the whole codebase becomes predictable — which is exactly what makes it easy for both humans and AI assistants to extend.

The five ideas

  1. Backend = vertical slices. Every feature lives under Features/{Domain}/ and owns its Endpoints.cs, Models.cs, Validators.cs, EfConfig.cs, Mappings.cs, and Permissions.cs. Slices register themselves by reflection at startup — you never edit Program.cs to add a feature.
  2. Frontend = file-system routes. Screens live under src/pages/, and the folder tree is the URL tree. A _-prefixed file or directory is ignored by the router.
  3. Errors = RFC 7807 ProblemDetails, always. Never throw new Exception(...) — throw a DomainException subclass and the global handler maps it to a clean JSON problem with a traceId.
  4. Lists = PagedRequest / PagedResult<T> with an operator-suffix query syntax (?price=gte:10&status=in:a,b&sort=name:asc). On the frontend, useDataGrid() + <DataGrid> consume it. See Data tables.
  5. Cross-cutting concerns = an IEndpointFilter pipeline — validation, audit, performance, and transactions are applied per slice, not re-implemented per handler.

Anatomy of a slice

A minimal-API handler is the handler — there's no MediatR indirection:

csharp
group.MapPost("/", async (CreateProductRequest req, AppDbContext db, CancellationToken ct) =>
{
    var product = Product.Create(req);   // plain mapping, no AutoMapper
    db.Products.Add(product);
    await db.SaveChangesAsync(ct);
    return Results.Created($"/api/products/{product.Id}", product.ToDto());
})
.RequirePermission(ProductPermissions.Create);

The slice exposes its routes through a marker interface that the startup discovers by reflection, so adding Features/Projects/ is enough — no registration step.

The filter pipeline

Each slice's route group opts into the shared filters:

  • Validation — finds the request's FluentValidation validator, runs it, and returns a 400 ProblemDetails on failure (field errors surface inline on forms).
  • Audit — records a trail entry for successful writes (see Audit).
  • Performance — warns on slow handlers and adds an X-Response-Time header.
  • Transaction — wraps write handlers in a database transaction (applied per-handler, so reads don't pay the cost).

What NetForge deliberately avoids

No MediatR/CQRS ceremony, no AutoMapper, no repository layer over EF, no Server Components/SSR, no DDD aggregates. Minimal-API handlers are the handlers, mapping is static methods, and you use DbContext directly. Fewer abstractions, less to learn, faster to extend.

Build your first slice

The Add a feature recipe walks the whole loop — copy _Template, rename, add a migration, and the slice auto-registers.

NetForge Community is MIT-licensed. Pro is a commercial edition.